Skip to main content

mlua_pkg/
error.rs

1//! Crate-wide error type for the PkgMgr subsystem.
2//!
3//! [`PkgError`] is the single error type for all PkgMgr operations
4//! (manifest parsing, lockfile I/O, git fetch, CLI).  It is distinct from
5//! the existing [`crate::ResolveError`], which covers runtime `resolve()` failures.
6//! Additional variants are added by subsequent subtasks.
7
8use std::path::PathBuf;
9
10/// Crate-wide error for PkgMgr operations.
11///
12/// # Error taxonomy
13///
14/// | Variant | Phase | Source |
15/// |---------|-------|--------|
16/// | [`ManifestParse`](Self::ManifestParse) | Parse | [`toml::de::Error`] via `#[from]` |
17/// | [`LockfileParse`](Self::LockfileParse) | Parse | [`toml::de::Error`] via `map_err` |
18/// | [`LockfileWrite`](Self::LockfileWrite) | Serialize | [`toml::ser::Error`] via `#[from]` |
19/// | [`MissingLockfile`](Self::MissingLockfile) | I/O | path not found |
20/// | [`SameNameConflict`](Self::SameNameConflict) | Validate | duplicate name in lockfile |
21/// | [`Io`](Self::Io) | I/O | [`std::io::Error`] via `#[from]` |
22/// | [`Validation`](Self::Validation) | Post-parse | custom message |
23/// | [`GitFetch`](Self::GitFetch) | Fetch | [`git2::Error`] via `#[from]` |
24/// | [`TagShaMismatch`](Self::TagShaMismatch) | Fetch | tag ↔ SHA conflict |
25///
26/// Variants are `#[non_exhaustive]` so that future subtasks can add new
27/// variants without breaking downstream `match` arms that include a wildcard.
28///
29/// The existing [`crate::ResolveError`] is intentionally not modified.
30#[non_exhaustive]
31#[derive(Debug, thiserror::Error)]
32pub enum PkgError {
33    /// TOML parse failure when reading `mlua-pkg.toml`.
34    ///
35    /// Automatically constructed from [`toml::de::Error`] via `?`.
36    #[error("manifest parse error: {source}")]
37    ManifestParse {
38        #[from]
39        source: toml::de::Error,
40    },
41
42    /// TOML parse failure when reading `mlua-pkg.lock`.
43    ///
44    /// Does **not** carry `#[from]` — [`ManifestParse`](Self::ManifestParse)
45    /// already claims `From<toml::de::Error>`.  Lockfile read paths must use
46    /// `.map_err(|e| PkgError::LockfileParse { source: e })` explicitly.
47    #[error("lockfile parse error: {source}")]
48    LockfileParse { source: toml::de::Error },
49
50    /// TOML serialization failure when writing `mlua-pkg.lock`.
51    ///
52    /// Automatically constructed from [`toml::ser::Error`] via `?`.
53    #[error("lockfile write error: {source}")]
54    LockfileWrite {
55        #[from]
56        source: toml::ser::Error,
57    },
58
59    /// `mlua-pkg.lock` not found at the expected path.
60    ///
61    /// Raised by [`Lockfile::read`](crate::lockfile::Lockfile::read) when the
62    /// file is absent, to distinguish the "not yet installed" case from other
63    /// I/O failures.
64    #[error("lockfile not found: {}", path.display())]
65    MissingLockfile { path: PathBuf },
66
67    /// Two packages share the same `name` field within a lockfile.
68    ///
69    /// Package names must be unique within a lockfile.  Raised during
70    /// [`Lockfile::read`](crate::lockfile::Lockfile::read) and (in ST5) at
71    /// install time.
72    #[error("duplicate package name in lockfile: {name:?}")]
73    SameNameConflict { name: String },
74
75    /// I/O error while reading or writing files (manifest, lockfile, cache).
76    ///
77    /// Automatically constructed from [`std::io::Error`] via `?`.
78    #[error("I/O error: {source}")]
79    Io {
80        #[from]
81        source: std::io::Error,
82    },
83
84    /// Post-parse validation failure.
85    ///
86    /// Raised when the parsed manifest satisfies TOML grammar but violates
87    /// semantic constraints, e.g. specifying both `tag` and `rev` in a
88    /// single dependency entry.
89    #[error("manifest validation error: {message}")]
90    Validation { message: String },
91
92    /// git2 operation failure during fetch or clone.
93    ///
94    /// Automatically constructed from [`git2::Error`] via `?`.
95    #[error("git fetch error: {source}")]
96    GitFetch {
97        #[from]
98        source: git2::Error,
99    },
100
101    /// The resolved SHA for a tag did not match the expected SHA.
102    ///
103    /// Raised when the caller pins a specific SHA together with a tag and
104    /// the tag resolves to a different commit.
105    #[error("tag SHA mismatch: expected {expected}, got {actual}")]
106    TagShaMismatch { expected: String, actual: String },
107
108    /// No suitable entry point directory found within a cached package.
109    ///
110    /// Raised by [`mlua_pkg::resolve_entry`](crate::resolve_entry) when neither
111    /// the override path nor any fallback candidate (`src/`, `lua/`, `.`) exists
112    /// as a directory under `cache_path`.
113    ///
114    /// Also raised during [`VendoredResolver::from_lockfile`](crate::resolvers::VendoredResolver::from_lockfile)
115    /// construction when a package in the lockfile has no resolvable entry.
116    #[error("entry not found for {name:?}: tried {}", format_paths(attempted))]
117    EntryNotFound {
118        name: String,
119        attempted: Vec<PathBuf>,
120    },
121}
122
123fn format_paths(paths: &[PathBuf]) -> String {
124    paths
125        .iter()
126        .map(|p| p.display().to_string())
127        .collect::<Vec<_>>()
128        .join(", ")
129}