hakari/
lib.rs

1// Copyright (c) The cargo-guppy Contributors
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4#![warn(missing_docs)]
5
6//! `hakari` is the library underlying [`cargo hakari`](https://docs.rs/cargo-hakari/*), a tool to
7//! manage `workspace-hack` packages.
8//!
9//! # Examples
10//!
11//! ```rust
12//! use guppy::MetadataCommand;
13//! use hakari::{HakariBuilder, HakariOutputOptions};
14//!
15//! // Use this workspace's PackageGraph for these tests.
16//! let package_graph = MetadataCommand::new()
17//!     .build_graph()
18//!     .expect("obtained cargo-guppy's PackageGraph");
19//!
20//! // The second argument to HakariBuilder::new specifies a Hakari (workspace-hack) package.
21//! // In this repository, the package is called "guppy-workspace-hack".
22//! let hakari_package = package_graph.workspace().member_by_name("guppy-workspace-hack").unwrap().id();
23//! let hakari_builder = HakariBuilder::new(&package_graph, Some(hakari_package))
24//!     .expect("HakariBuilder was constructed");
25//!
26//! // HakariBuilder has a number of config options. For this example, use the defaults.
27//! let hakari = hakari_builder.compute();
28//!
29//! // hakari can be used to build a TOML representation that forms part of a Cargo.toml file.
30//! // Existing Cargo.toml files can be managed using Hakari::read_toml.
31//! let toml = hakari.to_toml_string(&HakariOutputOptions::default()).expect("TOML output was constructed");
32//!
33//! // toml contains the Cargo.toml [dependencies] that would go in the Hakari package. It can be
34//! // written out through `HakariCargoToml` (returned by Hakari::read_toml) or manually.
35//! println!("Cargo.toml contents:\n{}", toml);
36//! ```
37//!
38//! The `cargo-guppy` repository uses a workspace-hack crate managed by `cargo hakari`. [See the
39//! generated `Cargo.toml`.](https://github.com/guppy-rs/guppy/blob/main/workspace-hack/Cargo.toml)
40//!
41//! The `cargo-guppy` repository also has a number of fixtures that demonstrate Hakari's output.
42//! [Here is an example.](https://github.com/guppy-rs/guppy/blob/main/fixtures/guppy/hakari/metadata_guppy_869476c-1.toml)
43//!
44//! # How `hakari` works
45//!
46//! Hakari follows a three-step process.
47//!
48//! ## 1. Configuration
49//!
50//! A [`HakariBuilder`] provides options to configure how a Hakari computation is done. Options supported
51//! include:
52//! * [the location of the `workspace-hack` package](HakariBuilder::new)
53//! * [platforms to simulate Cargo builds on](HakariBuilder::set_platforms)
54//! * [the version of the Cargo resolver to use](HakariBuilder::set_resolver)
55//! * [packages to be excluded during computation](HakariBuilder::add_traversal_excludes)
56//! * [packages to be excluded from the final output](HakariBuilder::add_final_excludes)
57//!
58//! With the optional `cli-support` feature, `HakariBuilder` options can be
59//! [read from](HakariBuilder::from_summary) or [written to](HakariBuilder::to_summary)
60//! a file as TOML or some other format.
61//!
62//! ## 2. Computation
63//!
64//! Once a `HakariBuilder` is configured, its [`compute`](HakariBuilder::compute) method can be
65//! called to create a `Hakari` instance. The algorithm runs in three steps:
66//!
67//! 1. Use guppy to [simulate a Cargo build](guppy::graph::cargo) for every workspace package and
68//!    every given platform, with no features, default features and all features. Collect the
69//!    results into
70//!    [a map](internals::ComputedMap) indexed by every dependency and the different sets of
71//!    features it was built with.
72//! 2. Scan through the map to figure out which dependencies are built with two or more
73//!    different feature sets, collecting them into an [output map](internals::OutputMap).
74//! 3. If one assumes that the output map will be written out to the `workspace-hack` package
75//!    through step 3 below, it is possible that it causes some extra packages to be built with a
76//!    second feature set. Look for such packages, add them to the output map, and iterate until a
77//!    fixpoint is reached and no new packages are built more than one way.
78//!
79//! This computation is done in a parallel fashion, using the [Rayon](rayon) library.
80//!
81//! The result of this computation is a [`Hakari`] instance.
82//!
83//! ## 3. Serialization
84//!
85//! The last step is to serialize the contents of the output map into the `workspace-hack` package's
86//! `Cargo.toml` file.
87//!
88//! 1. [`Hakari::read_toml`] reads an existing `Cargo.toml` file on disk. This file is
89//!    *partially generated*:
90//!
91//!    ```toml
92//!    [package]
93//!    name = "workspace-hack"
94//!    version = "0.1.0"
95//!    # more options...
96//!
97//!    #### BEGIN HAKARI SECTION
98//!    ...
99//!    #### END HAKARI SECTION
100//!    ```
101//!
102//!    The contents outside the `BEGIN HAKARI SECTION` and `END HAKARI SECTION` lines may be
103//!    edited by hand. The contents within this section are automatically generated.
104//!
105//!    On success, a [`HakariCargoToml`] is returned.
106//!
107//! 2. [`Hakari::to_toml_string`](Hakari::to_toml_string) returns the new contents of the
108//!    automatically generated section.
109//! 3. [`HakariCargoToml::write_to_file`](HakariCargoToml::write_to_file) writes out the contents
110//!    to disk.
111//!
112//! `HakariCargoToml` also supports serializing contents to memory and producing diffs.
113//!
114//! # Future work
115//!
116//! `hakari` is still missing a few features:
117//!
118//! * Simulating cross-compilations
119//! * Platform-specific excludes
120//! * Only including a subset of packages in the final result (e.g. unifying core packages like
121//!   `syn` but not any others)
122//!
123//! These features will be added as time permits.
124
125mod cargo_toml;
126#[cfg(feature = "cli-support")]
127pub mod cli_ops;
128pub mod explain;
129mod hakari;
130mod helpers;
131#[cfg(feature = "proptest1")]
132mod proptest_helpers;
133#[cfg(feature = "cli-support")]
134pub mod summaries;
135mod toml_out;
136pub mod verify;
137
138pub use crate::{
139    cargo_toml::*,
140    hakari::{DepFormatVersion, Hakari, HakariBuilder, UnifyTargetHost, WorkspaceHackLineStyle},
141    toml_out::*,
142};
143
144pub mod internals {
145    //! Access to internal Hakari data structures.
146    //!
147    //! These are provided in case some post-processing needs to be done.
148
149    pub use crate::hakari::{
150        ComputedInnerMap, ComputedInnerValue, ComputedMap, ComputedValue, OutputKey, OutputMap,
151    };
152}
153
154/// Re-export diffy.
155pub use diffy;