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::{graph::feature::FeatureId, PackageId};
7use camino::Utf8PathBuf;
8use std::{error, fmt};
9pub use target_spec::Error as TargetSpecError;
10use Error::*;
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: {}\n* for third-party: {}\n* name: {}\n",
144                    message, summary, registry_name
145                )
146            }
147            #[cfg(feature = "summaries")]
148            TomlSerializeError(_) => write!(f, "failed to serialize to TOML"),
149        }
150    }
151}
152
153impl error::Error for Error {
154    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
155        match self {
156            MetadataParseError(err) => Some(err),
157            MetadataSerializeError(err) => Some(err),
158            CommandError(err) => Some(err.as_ref()),
159            PackageGraphConstructError(_) => None,
160            UnknownPackageId(_) => None,
161            UnknownFeatureId(_, _) => None,
162            UnknownWorkspacePath(_) => None,
163            UnknownWorkspaceName(_) => None,
164            TargetSpecError(_, err) => Some(err),
165            PackageGraphInternalError(_) => None,
166            FeatureGraphInternalError(_) => None,
167            #[cfg(feature = "summaries")]
168            UnknownSummaryId(_) => None,
169            #[cfg(feature = "summaries")]
170            UnknownPackageSetSummary { .. } => None,
171            #[cfg(feature = "summaries")]
172            UnknownRegistryName { .. } => None,
173            #[cfg(feature = "summaries")]
174            TomlSerializeError(err) => Some(err),
175        }
176    }
177}
178
179/// Describes warnings emitted during feature graph construction.
180#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
181#[non_exhaustive]
182pub enum FeatureGraphWarning {
183    /// A feature that was requested is missing from a package.
184    MissingFeature {
185        /// The stage of building the feature graph where the warning occurred.
186        stage: FeatureBuildStage,
187        /// The package ID for which the feature was requested.
188        package_id: PackageId,
189        /// The name of the feature.
190        feature_name: String,
191    },
192
193    /// A self-loop was discovered.
194    SelfLoop {
195        /// The package ID for which the self-loop was discovered.
196        package_id: PackageId,
197        /// The name of the feature for which the self-loop was discovered.
198        feature_name: String,
199    },
200}
201
202impl fmt::Display for FeatureGraphWarning {
203    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
204        use FeatureGraphWarning::*;
205        match self {
206            MissingFeature {
207                stage,
208                package_id,
209                feature_name,
210            } => write!(
211                f,
212                "{}: for package '{}', missing feature '{}'",
213                stage, package_id, feature_name
214            ),
215            SelfLoop {
216                package_id,
217                feature_name,
218            } => write!(
219                f,
220                "for package '{}', self-loop detected for named feature '{}'",
221                package_id, feature_name
222            ),
223        }
224    }
225}
226
227/// Describes the stage of construction at which a feature graph warning occurred.
228#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
229#[non_exhaustive]
230pub enum FeatureBuildStage {
231    /// The warning occurred while adding edges for the `[features]` section of `Cargo.toml`.
232    AddNamedFeatureEdges {
233        /// The package ID for which edges were being added.
234        package_id: PackageId,
235        /// The feature name from which edges were being added.
236        from_feature: String,
237    },
238    /// The warning occurred while adding dependency edges.
239    AddDependencyEdges {
240        /// The package ID for which edges were being added.
241        package_id: PackageId,
242        /// The name of the dependency.
243        dep_name: String,
244    },
245}
246
247impl fmt::Display for FeatureBuildStage {
248    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
249        use FeatureBuildStage::*;
250        match self {
251            AddNamedFeatureEdges {
252                package_id,
253                from_feature,
254            } => write!(
255                f,
256                "for package '{}', while adding named feature edges from '{}'",
257                package_id, from_feature
258            ),
259            AddDependencyEdges {
260                package_id,
261                dep_name,
262            } => write!(
263                f,
264                "for package '{}', while adding edges for dependency '{}'",
265                package_id, dep_name,
266            ),
267        }
268    }
269}