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}