Skip to main content

dnf_repofile/
error.rs

1//! Error types for DNF `.repo` file parsing, validation, and variable expansion.
2//!
3//! This module defines the top-level [`enum@Error`] enum (via [`thiserror`]) that
4//! aggregates all domain-specific error types in the library:
5//!
6//! - [`ParseError`] — INI parsing failures (malformed sections, missing `=`, etc.)
7//! - [`ParseBoolError`] — invalid boolean values like `"maybe"`
8//! - [`ExpandError`] — variable substitution failures (unknown name, depth, syntax)
9//!
10//! The [`Result`] type alias is a convenience for `Result<T, Error>`.
11
12use thiserror::Error;
13
14/// Top-level error type for the library.
15///
16/// Aggregates all domain-specific errors into a single enum so callers can
17/// use [`Result<T>`](Result) without naming every variant.
18///
19/// Supports automatic conversion from [`ParseError`], [`ParseBoolError`],
20/// and [`std::io::Error`] via [`From`].
21///
22/// # Examples
23///
24/// ```
25/// use dnf_repofile::Error;
26///
27/// let err = Error::Other("custom error".into());
28/// assert_eq!(err.to_string(), "custom error");
29/// ```
30#[non_exhaustive]
31#[derive(Error, Debug)]
32pub enum Error {
33    /// Failed to parse a `.repo` file string.
34    #[error("failed to parse .repo file: {0}")]
35    Parse(#[from] ParseError),
36
37    /// Failed to parse a boolean value from a `.repo` file option.
38    #[error("failed to parse boolean value '{0}'")]
39    ParseBool(#[from] ParseBoolError),
40
41    /// An option value is invalid for its expected type.
42    #[error("invalid option value for '{key}': {message}")]
43    InvalidValue {
44        /// The option key (e.g., `"enabled"`, `"priority"`).
45        key: String,
46        /// A human-readable description of why the value is invalid.
47        message: String,
48    },
49
50    /// A repository with this ID already exists in the file.
51    #[error("repo '{0}' already exists in file")]
52    DuplicateRepo(String),
53
54    /// A repository with this ID was not found.
55    #[error("repo '{0}' not found")]
56    RepoNotFound(String),
57
58    /// An I/O error occurred (file read/write).
59    #[error("IO error: {0}")]
60    Io(#[from] std::io::Error),
61
62    /// A generic error message.
63    #[error("{0}")]
64    Other(String),
65}
66
67/// Result type alias for convenience.
68///
69/// Equivalent to `std::result::Result<T, [`Error`]>`.
70pub type Result<T> = std::result::Result<T, Error>;
71
72impl From<String> for Error {
73    fn from(s: String) -> Self {
74        Error::Other(s)
75    }
76}
77
78/// Error when parsing a boolean value from a `.repo` file option fails.
79///
80/// This occurs when a value expected to be a DNF boolean (`1`/`0`/`yes`/`no`/`true`/`false`/`on`/`off`)
81/// contains something else entirely (e.g., `"maybe"`).
82#[derive(Error, Debug)]
83#[error("invalid boolean value: '{input}'")]
84pub struct ParseBoolError {
85    /// The raw input string that could not be parsed as a boolean.
86    pub input: String,
87}
88
89/// Error when parsing a `.repo` file fails.
90///
91/// Covers all INI-level syntax issues: invalid section headers, missing
92/// `=` separators, empty section names, and invalid repository IDs.
93#[non_exhaustive]
94#[derive(Error, Debug)]
95pub enum ParseError {
96    /// A section header (e.g., `[repo-name]`) has invalid syntax.
97    #[error("invalid section header at line {line}: '{header}'")]
98    InvalidSection {
99        /// The 1-based line number where the error occurred.
100        line: usize,
101        /// The raw header text.
102        header: String,
103    },
104
105    /// A key-value pair is missing the `=` separator.
106    #[error("missing '=' in key-value pair at line {line}: '{line_text}'")]
107    MissingEquals {
108        /// The 1-based line number where the error occurred.
109        line: usize,
110        /// The raw line text.
111        line_text: String,
112    },
113
114    /// A section header with an empty name was encountered (`[]`).
115    #[error("empty section name")]
116    EmptySectionName,
117
118    /// A repository ID contains invalid characters or is otherwise malformed.
119    #[error("invalid repo ID '{id}': {reason}")]
120    InvalidRepoId {
121        /// The problematic repo ID string.
122        id: String,
123        /// Why the ID is invalid.
124        reason: String,
125    },
126
127    /// An I/O error occurred while reading the file.
128    #[error("I/O error reading file: {0}")]
129    Io(#[from] std::io::Error),
130}
131
132/// Error when expanding DNF variables fails.
133///
134/// Covers three failure modes: a variable name not found in the substitution
135/// map, exceeding the maximum recursion depth (default 32), and malformed
136/// variable expression syntax.
137#[non_exhaustive]
138#[derive(Error, Debug)]
139pub enum ExpandError {
140    /// A variable referenced in the input string was not found in the
141    /// substitution map.
142    #[error("variable '{name}' not found in substitution map")]
143    VariableNotFound {
144        /// The name of the missing variable.
145        name: String,
146    },
147
148    /// The maximum recursion depth was exceeded while expanding a variable
149    /// expression (likely a circular reference).
150    #[error("maximum recursion depth ({depth}) exceeded while expanding '{expr}'")]
151    MaxDepthExceeded {
152        /// The depth at which expansion stopped.
153        depth: u32,
154        /// The expression being expanded when the limit was hit.
155        expr: String,
156    },
157
158    /// A variable expression has invalid syntax (e.g., an empty `$`
159    /// without a following name).
160    #[error("malformed variable expression: '{expr}'")]
161    MalformedExpression {
162        /// The invalid expression text.
163        expr: String,
164    },
165}