node_maintainer/
error.rs

1use futures::channel::mpsc;
2use kdl::{KdlDocument, KdlNode};
3use miette::Diagnostic;
4use thiserror::Error;
5
6use crate::{NpmPackageLock, NpmPackageLockEntry};
7
8#[allow(clippy::large_enum_variant)]
9#[derive(Debug, Error, Diagnostic)]
10pub enum NodeMaintainerError {
11    /// Unsupported resolved URL scheme
12    #[error("Unsupported resolved URL scheme")]
13    #[diagnostic(code(node_maintainer::kdl::unsupported_url_scheme), url(docsrs))]
14    UnsupportedScheme(String),
15
16    /// Failed to parse a resolved URL while parsing lockfile
17    #[error("Failed to parse a resolved URL while parsing lockfile: {0}")]
18    #[diagnostic(code(node_maintainer::kdl::url_parse_error), url(docsrs))]
19    UrlParseError(String, #[source] url::ParseError),
20
21    /// Failed to parse a Semver string.
22    #[error("Failed to parse a Semver string.")]
23    #[diagnostic(code(node_maintainer::kdl::semver_parse_error), url(docsrs))]
24    SemverParseError(#[from] node_semver::SemverError),
25
26    /// Missing version for NPM package entry in lockfile.
27    #[error("Missing version for NPM package entry in lockfile.")]
28    #[diagnostic(code(node_maintainer::kdl::missing_version), url(docsrs))]
29    MissingVersion,
30
31    /// Missing resolution for package entry in lockfile.
32    #[error("Missing version for NPM package entry in lockfile.")]
33    #[diagnostic(code(node_maintainer::kdl::missing_version), url(docsrs))]
34    MissingResolution,
35
36    /// Failed to parse an integrity value.
37    #[error(transparent)]
38    #[diagnostic(code(node_maintainer::kdl::integrity_parse_error), url(docsrs))]
39    IntegrityParseError(#[from] ssri::Error),
40
41    /// Failed to parse an integrity value while loading lockfile.
42    #[error("Failed to parse an integrity value while loading lockfile node:\n{0}")]
43    #[diagnostic(code(node_maintainer::kdl::integrity_parse_error), url(docsrs))]
44    KdlLockfileIntegrityParseError(KdlNode, #[source] ssri::Error),
45
46    /// Missing package node name.
47    #[error("Missing package node name:\n{0}")]
48    #[diagnostic(code(node_maintainer::kdl::missing_node_name), url(docsrs))]
49    KdlLockMissingName(KdlNode),
50
51    /// Missing package node name.
52    #[error("Missing package name:\n{0:#?}")]
53    #[diagnostic(code(node_maintainer::npm::missing_name), url(docsrs))]
54    NpmLockMissingName(Box<NpmPackageLockEntry>),
55
56    /// Failed to parse an integrity value while loading NPM lockfile.
57    #[error("Failed to parse an integrity value while loading lockfile node:\n{0:#?}")]
58    #[diagnostic(code(node_maintainer::npm::integrity_parse_error), url(docsrs))]
59    NpmLockfileIntegrityParseError(Box<NpmPackageLockEntry>, #[source] ssri::Error),
60
61    /// Unsupported NPM Package Lock version.
62    #[error("Unsupported NPM Package Lock version: {0}")]
63    #[diagnostic(
64        code(node_maintainer::npm::unsupported_package_lock_Version),
65        url(docsrs)
66    )]
67    NpmUnsupportedPackageLockVersion(u64),
68
69    /// No root node in KDL lockfile.
70    #[error("No root node in KDL lockfile.")]
71    #[diagnostic(code(node_maintainer::kdl::missing_root), url(docsrs))]
72    KdlLockMissingRoot(KdlDocument),
73
74    /// No root node in NPM lockfile.
75    #[error("No root package in NPM lockfile.")]
76    #[diagnostic(code(node_maintainer::npm::missing_root), url(docsrs))]
77    NpmLockMissingRoot(NpmPackageLock),
78
79    /// Error parsing lockfile.
80    #[error(transparent)]
81    #[diagnostic(code(node_maintainer::kdl::parse_error), url(docsrs))]
82    KdlParseError(#[from] kdl::KdlError),
83
84    #[error("Invalid lockfile version format.")]
85    #[diagnostic(code(node_maintainer::kdl::invalid_lockfile_version), url(docsrs))]
86    InvalidLockfileVersion,
87
88    /// Error from serde_wasm_bindgen
89    #[cfg(target_arch = "wasm32")]
90    #[error(transparent)]
91    #[diagnostic(code(node_maintainer::serde_wasm_bindgen::error), url(docsrs))]
92    SerdeWasmBindgenError(#[from] serde_wasm_bindgen::Error),
93
94    /// Generic package spec error.
95    #[error(transparent)]
96    #[diagnostic(transparent)]
97    PackageSpecError(#[from] oro_package_spec::PackageSpecError),
98
99    /// Generic IO Error.
100    #[error("{0}")]
101    #[diagnostic(code(node_maintainer::io_error), url(docsrs))]
102    IoError(String, #[source] std::io::Error),
103
104    #[cfg(not(target_arch = "wasm32"))]
105    /// Generic error returned from Nassun.
106    #[error(transparent)]
107    #[diagnostic(transparent)]
108    NassunError(#[from] nassun::NassunError),
109
110    /// Failed to create either a symlink or a junction while linking
111    /// dependencies using the isolated linker.
112    ///
113    /// This can happen if the user does not have permission to create
114    /// symlinks on a Windows system, and the fallback junction operation
115    /// fails for some reason--likely because junctions are also unsupported.
116    ///
117    /// Since only administrator-elevated users can create symlinks on
118    /// Windows, the best workaround for this issue is to switch to the
119    /// hoisted linker.
120    ///
121    /// In Orogene, this is the `--hoisted` flag, or `options { hoisted true;
122    /// }` in oro.kdl.
123    #[cfg(windows)]
124    #[error("Failed to create either a symlink or junction from {} to {}.", .0.display(), .1.display())]
125    #[diagnostic(
126        code(node_maintainer::junctions_not_supported),
127        url(docsrs),
128        help("The isolated linker requires your Windows user to either be able to create symlinks, which requires Administrator privileges, or create junctions, which require an NTFS filesystem, among other things. If you see this message, you might consider switching to the 'hoisted' linker instead.")
129    )]
130    JunctionsNotSupported(
131        std::path::PathBuf,
132        std::path::PathBuf,
133        #[source] std::io::Error,
134    ),
135
136    #[cfg(target_arch = "wasm32")]
137    /// Generic error returned from Nassun.
138    #[error(transparent)]
139    #[diagnostic(transparent)]
140    NassunError(#[from] nassun::error::NassunError),
141
142    /// Generic serde_json error.
143    #[error(transparent)]
144    #[diagnostic(code(node_maintainer::serde_json_error), url(docsrs))]
145    SerdeJsonError(#[from] serde_json::Error),
146
147    /// Generic error. Refer to the error message for more details.
148    #[error("{0}")]
149    #[diagnostic(code(node_maintainer::miscellaneous_error), url(docsrs))]
150    MiscError(String),
151
152    /// Failed to send data through mpsc channel. This is likely an internal
153    /// error of some sort.
154    #[error("Failed to send data through mpsc channel.")]
155    #[diagnostic(code(node_maintainer::mpsc_error), url(docsrs))]
156    TrySendError,
157
158    /// Failed to validate a graph. Refer to the error message for more details.
159    #[error("{0}")]
160    #[diagnostic(code(node_maintainer::graph_error), url(docsrs))]
161    GraphValidationError(String),
162
163    /// Got an error while walking `node_modules`. Refer to the error message
164    /// for specific details.
165    #[cfg(not(target_arch = "wasm32"))]
166    #[error(transparent)]
167    #[diagnostic(code(node_maintainer::walkdir_error), url(docsrs))]
168    WalkDirError(#[from] walkdir::Error),
169
170    /// Failed to read `package.json` during the build step. Refer to the
171    /// error message for more details.
172    #[cfg(not(target_arch = "wasm32"))]
173    #[error("Failed to read manifest during build step, at {}", .0.display())]
174    #[diagnostic(code(node_maintainer::build_manifest_read_error), url(docsrs))]
175    BuildManifestReadError(std::path::PathBuf, #[source] std::io::Error),
176
177    /// Some error occurred while running a script. Refer to the error message
178    /// for more details.
179    #[cfg(not(target_arch = "wasm32"))]
180    #[error(transparent)]
181    #[diagnostic(transparent)]
182    OroScriptError(#[from] oro_script::OroScriptError),
183
184    /// Locked file was requested, but a new dependency tree was resolved that
185    /// would cause changes to the lockfile. The contents of `package.json`
186    /// may have changed since the last time the lockfile was updated.
187    ///
188    /// This typically happens when a dependency is added or removed from
189    /// package.json while locked mode is enabled. If you have an existing
190    /// lockfile and get this error without any modifications to package.json,
191    /// please [report this as a
192    /// bug](https://github.com/orogene/orogene/issues/new).
193    #[error("Locked file was requested, but a new dependency tree was resolved that would cause changes to the lockfile. The contents of `package.json` may have changed since the last time the lockfile was updated.")]
194    #[diagnostic(
195        code(node_maintainer::lockfile_mismatch),
196        url(docsrs),
197        help("Did you modify package.json by hand?")
198    )]
199    LockfileMismatch,
200}
201
202impl<T> From<mpsc::TrySendError<T>> for NodeMaintainerError {
203    fn from(_: mpsc::TrySendError<T>) -> Self {
204        NodeMaintainerError::TrySendError
205    }
206}
207
208pub trait IoContext {
209    type T;
210
211    fn io_context(self, context: impl FnOnce() -> String) -> Result<Self::T, NodeMaintainerError>;
212}
213
214impl<T> IoContext for Result<T, std::io::Error> {
215    type T = T;
216
217    fn io_context(self, context: impl FnOnce() -> String) -> Result<Self::T, NodeMaintainerError> {
218        self.map_err(|e| NodeMaintainerError::IoError(context(), e))
219    }
220}