guppy/
errors.rs

1// Copyright (c) The cargo-guppy Contributors
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! Contains types that describe errors and warnings that `guppy` methods can return.
5
6use crate::{PackageId, graph::feature::FeatureId};
7use Error::*;
8use camino::Utf8PathBuf;
9use std::{error, fmt};
10pub use target_spec::Error as TargetSpecError;
11
12/// Error type describing the sorts of errors `guppy` can return.
13#[derive(Debug)]
14#[non_exhaustive]
15pub enum Error {
16    /// An error occurred while executing `cargo metadata`.
17    CommandError(Box<dyn error::Error + Send + Sync>),
18    /// An error occurred while parsing `cargo metadata` JSON.
19    MetadataParseError(serde_json::Error),
20    /// An error occurred while serializing `cargo metadata` JSON.
21    MetadataSerializeError(serde_json::Error),
22    /// An error occurred while constructing a `PackageGraph` from parsed metadata.
23    PackageGraphConstructError(String),
24    /// A package ID was unknown to this `PackageGraph`.
25    UnknownPackageId(PackageId),
26    /// A feature ID was unknown to this `FeatureGraph`.
27    UnknownFeatureId(PackageId, String),
28    /// A package specified by path was unknown to this workspace.
29    UnknownWorkspacePath(Utf8PathBuf),
30    /// A package specified by name was unknown to this workspace.
31    UnknownWorkspaceName(String),
32    /// An error was returned by `target-spec`.
33    TargetSpecError(String, TargetSpecError),
34    /// An internal error occurred within this `PackageGraph`.
35    PackageGraphInternalError(String),
36    /// An internal error occurred within this `FeatureGraph`.
37    FeatureGraphInternalError(String),
38    /// A summary ID was unknown to this `PackageGraph`.
39    ///
40    /// This is present if the `summaries` feature is enabled.
41    #[cfg(feature = "summaries")]
42    UnknownSummaryId(guppy_summaries::SummaryId),
43    /// While resolving a [`PackageSetSummary`](crate::graph::summaries::PackageSetSummary),
44    /// some elements were unknown to the `PackageGraph`.
45    ///
46    /// This is present if the `summaries` feature is enabled.
47    #[cfg(feature = "summaries")]
48    UnknownPackageSetSummary {
49        /// A description attached to the error.
50        message: String,
51        /// Summary IDs that weren't known to the `PackageGraph`.
52        unknown_summary_ids: Vec<crate::graph::summaries::SummaryId>,
53        /// Workspace packages that weren't known to the `PackageGraph`.
54        unknown_workspace_members: Vec<String>,
55        /// Third-party packages that weren't known to the `PackageGraph`.
56        unknown_third_party: Vec<crate::graph::summaries::ThirdPartySummary>,
57    },
58    /// While resolving a [`PackageSetSummary`](crate::graph::summaries::PackageSetSummary),
59    /// an unknown external registry was encountered.
60    #[cfg(feature = "summaries")]
61    UnknownRegistryName {
62        /// A description attached to the error.
63        message: String,
64
65        /// The summary for which the name wasn't recognized.
66        summary: Box<crate::graph::summaries::ThirdPartySummary>,
67
68        /// The registry name that wasn't recognized.
69        registry_name: String,
70    },
71    /// An error occurred while serializing to TOML.
72    #[cfg(feature = "summaries")]
73    TomlSerializeError(toml::ser::Error),
74}
75
76impl Error {
77    pub(crate) fn command_error(err: cargo_metadata::Error) -> Self {
78        Error::CommandError(Box::new(err))
79    }
80
81    pub(crate) fn unknown_feature_id(feature_id: FeatureId<'_>) -> Self {
82        Error::UnknownFeatureId(
83            feature_id.package_id().clone(),
84            feature_id.label().to_string(),
85        )
86    }
87}
88
89impl fmt::Display for Error {
90    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91        match self {
92            CommandError(_) => write!(f, "`cargo metadata` execution failed"),
93            MetadataParseError(_) => write!(f, "`cargo metadata` returned invalid JSON output"),
94            MetadataSerializeError(_) => write!(f, "failed to serialize `cargo metadata` to JSON"),
95            PackageGraphConstructError(s) => write!(f, "failed to construct package graph: {s}"),
96            UnknownPackageId(id) => write!(f, "unknown package ID: {id}"),
97            UnknownFeatureId(package_id, feature) => {
98                write!(f, "unknown feature ID: '{package_id}/{feature}'")
99            }
100            UnknownWorkspacePath(path) => write!(f, "unknown workspace path: {path}"),
101            UnknownWorkspaceName(name) => write!(f, "unknown workspace package name: {name}"),
102            TargetSpecError(msg, _) => write!(f, "target spec error while {msg}"),
103            PackageGraphInternalError(msg) => write!(f, "internal error in package graph: {msg}"),
104            FeatureGraphInternalError(msg) => write!(f, "internal error in feature graph: {msg}"),
105            #[cfg(feature = "summaries")]
106            UnknownSummaryId(summary_id) => write!(f, "unknown summary ID: {summary_id}"),
107            #[cfg(feature = "summaries")]
108            UnknownPackageSetSummary {
109                message,
110                unknown_summary_ids,
111                unknown_workspace_members,
112                unknown_third_party,
113            } => {
114                writeln!(f, "unknown elements: {message}")?;
115                if !unknown_summary_ids.is_empty() {
116                    writeln!(f, "* unknown summary IDs:")?;
117                    for summary_id in unknown_summary_ids {
118                        writeln!(f, "  - {summary_id}")?;
119                    }
120                }
121                if !unknown_workspace_members.is_empty() {
122                    writeln!(f, "* unknown workspace names:")?;
123                    for workspace_member in unknown_workspace_members {
124                        writeln!(f, "  - {workspace_member}")?;
125                    }
126                }
127                if !unknown_third_party.is_empty() {
128                    writeln!(f, "* unknown third-party:")?;
129                    for third_party in unknown_third_party {
130                        writeln!(f, "  - {third_party}")?;
131                    }
132                }
133                Ok(())
134            }
135            #[cfg(feature = "summaries")]
136            UnknownRegistryName {
137                message,
138                summary,
139                registry_name,
140            } => {
141                writeln!(
142                    f,
143                    "unknown registry name: {message}\n* for third-party: {summary}\n* name: {registry_name}\n"
144                )
145            }
146            #[cfg(feature = "summaries")]
147            TomlSerializeError(_) => write!(f, "failed to serialize to TOML"),
148        }
149    }
150}
151
152impl error::Error for Error {
153    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
154        match self {
155            MetadataParseError(err) => Some(err),
156            MetadataSerializeError(err) => Some(err),
157            CommandError(err) => Some(err.as_ref()),
158            PackageGraphConstructError(_) => None,
159            UnknownPackageId(_) => None,
160            UnknownFeatureId(_, _) => None,
161            UnknownWorkspacePath(_) => None,
162            UnknownWorkspaceName(_) => None,
163            TargetSpecError(_, err) => Some(err),
164            PackageGraphInternalError(_) => None,
165            FeatureGraphInternalError(_) => None,
166            #[cfg(feature = "summaries")]
167            UnknownSummaryId(_) => None,
168            #[cfg(feature = "summaries")]
169            UnknownPackageSetSummary { .. } => None,
170            #[cfg(feature = "summaries")]
171            UnknownRegistryName { .. } => None,
172            #[cfg(feature = "summaries")]
173            TomlSerializeError(err) => Some(err),
174        }
175    }
176}
177
178/// Describes warnings emitted during feature graph construction.
179#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
180#[non_exhaustive]
181pub enum FeatureGraphWarning {
182    /// A feature that was requested is missing from a package.
183    MissingFeature {
184        /// The stage of building the feature graph where the warning occurred.
185        stage: FeatureBuildStage,
186        /// The package ID for which the feature was requested.
187        package_id: PackageId,
188        /// The name of the feature.
189        feature_name: String,
190    },
191
192    /// A self-loop was discovered.
193    SelfLoop {
194        /// The package ID for which the self-loop was discovered.
195        package_id: PackageId,
196        /// The name of the feature for which the self-loop was discovered.
197        feature_name: String,
198    },
199}
200
201impl fmt::Display for FeatureGraphWarning {
202    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203        use FeatureGraphWarning::*;
204        match self {
205            MissingFeature {
206                stage,
207                package_id,
208                feature_name,
209            } => write!(
210                f,
211                "{stage}: for package '{package_id}', missing feature '{feature_name}'"
212            ),
213            SelfLoop {
214                package_id,
215                feature_name,
216            } => write!(
217                f,
218                "for package '{package_id}', self-loop detected for named feature '{feature_name}'"
219            ),
220        }
221    }
222}
223
224/// Describes the stage of construction at which a feature graph warning occurred.
225#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
226#[non_exhaustive]
227pub enum FeatureBuildStage {
228    /// The warning occurred while adding edges for the `[features]` section of `Cargo.toml`.
229    AddNamedFeatureEdges {
230        /// The package ID for which edges were being added.
231        package_id: PackageId,
232        /// The feature name from which edges were being added.
233        from_feature: String,
234    },
235    /// The warning occurred while adding dependency edges.
236    AddDependencyEdges {
237        /// The package ID for which edges were being added.
238        package_id: PackageId,
239        /// The name of the dependency.
240        dep_name: String,
241    },
242}
243
244impl fmt::Display for FeatureBuildStage {
245    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
246        use FeatureBuildStage::*;
247        match self {
248            AddNamedFeatureEdges {
249                package_id,
250                from_feature,
251            } => write!(
252                f,
253                "for package '{package_id}', while adding named feature edges from '{from_feature}'"
254            ),
255            AddDependencyEdges {
256                package_id,
257                dep_name,
258            } => write!(
259                f,
260                "for package '{package_id}', while adding edges for dependency '{dep_name}'",
261            ),
262        }
263    }
264}