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}