1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
// Copyright (c) The cargo-guppy Contributors // SPDX-License-Identifier: MIT OR Apache-2.0 use crate::graph::PackageGraph; use crate::Error; use cargo_metadata::CargoOpt; use serde::{Deserialize, Serialize}; use std::io; use std::path::PathBuf; /// A builder for configuring `cargo metadata` invocations. /// /// This is the most common entry point for constructing a `PackageGraph`. /// /// ## Examples /// /// Build a `PackageGraph` for the Cargo workspace in the current directory: /// /// ```rust /// use guppy::MetadataCommand; /// use guppy::graph::PackageGraph; /// /// let mut cmd = MetadataCommand::new(); /// let package_graph = PackageGraph::from_command(&mut cmd); /// ``` #[derive(Clone, Debug, Default)] pub struct MetadataCommand { inner: cargo_metadata::MetadataCommand, } impl MetadataCommand { /// Creates a default `cargo metadata` command builder. /// /// By default, this will look for `Cargo.toml` in the ancestors of this process's current /// directory. pub fn new() -> Self { let mut inner = cargo_metadata::MetadataCommand::new(); // Always use --all-features so that we get a full view of the graph. inner.features(CargoOpt::AllFeatures); Self { inner } } /// Sets the path to the `cargo` executable. /// /// If unset, this will use the `$CARGO` environment variable, or else `cargo` from `$PATH`. pub fn cargo_path(&mut self, path: impl Into<PathBuf>) -> &mut Self { self.inner.cargo_path(path); self } /// Sets the path to `Cargo.toml`. /// /// By default, this will look for `Cargo.toml` in the ancestors of the current directory. Note /// that this doesn't need to be the root `Cargo.toml` in a workspace -- any member of the /// workspace is fine. pub fn manifest_path(&mut self, path: impl Into<PathBuf>) -> &mut Self { self.inner.manifest_path(path); self } /// Sets the current directory of the `cargo metadata` process. /// /// By default, the current directory will be inherited from this process. pub fn current_dir(&mut self, path: impl Into<PathBuf>) -> &mut Self { self.inner.current_dir(path); self } // *Do not* implement no_deps or features. /// Arbitrary flags to pass to `cargo metadata`. These will be added to the end of the /// command invocation. /// /// Note that `guppy` internally: /// * uses `--format-version 1` as its metadata format. /// * passes in `--all-features`, so that `guppy` has a full view of the dependency graph. /// * does not pass in `--no-deps`, so that `guppy` knows about non-workspace dependencies. /// /// Attempting to override either of those options may lead to unexpected results. pub fn other_options(&mut self, options: impl Into<Vec<String>>) -> &mut Self { self.inner.other_options(options); self } /// Runs the configured `cargo metadata` and returns a deserialized `CargoMetadata`. pub fn exec(&mut self) -> Result<CargoMetadata, Error> { let inner = self.inner.exec().map_err(Error::command_error)?; Ok(CargoMetadata(inner)) } /// Runs the configured `cargo metadata` and returns a parsed `PackageGraph`. pub fn build_graph(&mut self) -> Result<PackageGraph, Error> { let metadata = self.exec()?; metadata.build_graph() } } /// A deserialized Cargo metadata returned by a `MetadataCommand`. /// /// This is an alternative entry point for constructing a `PackageGraph`, to be used if the JSON /// output of `cargo metadata` is already available. /// /// This struct implements `serde::Serialize` and `Deserialize`. #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(transparent)] pub struct CargoMetadata(pub(crate) cargo_metadata::Metadata); impl CargoMetadata { /// Deserializes this JSON blob into a `CargoMetadata`. pub fn parse_json(json: impl AsRef<str>) -> Result<Self, Error> { let inner = serde_json::from_str(json.as_ref()).map_err(Error::MetadataParseError)?; Ok(Self(inner)) } /// Serializes this metadata into the given writer. pub fn serialize(&self, writer: &mut impl io::Write) -> Result<(), Error> { serde_json::to_writer(writer, &self.0).map_err(Error::MetadataSerializeError) } /// Parses this metadata and builds a `PackageGraph` from it. pub fn build_graph(self) -> Result<PackageGraph, Error> { PackageGraph::from_metadata(self) } }