Skip to main content

git_policy_cli/
config_error.rs

1//! What:
2//!  The one failure type for rejected `cli-git.config.jsonc` content or files.
3//! Why:
4//!  Callers render a single configuration diagnostic without parsing message text.
5//!
6//! In TS you'd write (pseudocode):
7//! ```ts
8//! class ConfigError extends Error {}
9//! ```
10
11/// What:
12///  `ConfigError` owns the explanation of one rejected configuration.
13///       `String` is a heap-allocated,
14///  growable UTF-8 buffer this struct owns.
15///       Sibling the reader might expect:
16///  `&str`,
17///  a borrowed view that owns nothing.
18/// Why:
19///   The error outlives the parsed document and the temporary formatted text,
20///       so a borrowed `&str` would dangle;
21///  `String` keeps the bytes alive.
22///
23/// In TS you'd write (pseudocode):
24/// ```ts
25/// class ConfigError extends Error { readonly message: string }
26/// ```
27#[derive(Clone, Debug, Eq, PartialEq)]
28pub struct ConfigError {
29    /// Complete diagnostic naming the affected key or file and the accepted values.
30    pub message: String,
31}
32
33/// What:
34///  `impl ConfigError { ... }` attaches functions to the struct,
35///  like class statics.
36/// Why:
37///   Validation sites keep their condition next to its user-facing explanation.
38///
39/// In TS you'd write (pseudocode):
40/// ```ts
41/// new ConfigError(message);
42/// ```
43impl ConfigError {
44    /// What:
45    ///  Build an error from borrowed text.
46    ///  `&str` lends the caller's bytes;
47    ///       `String::from` copies them into storage the error owns.
48    /// Why:
49    ///   Messages are usually `format!` temporaries that end with the caller's statement.
50    ///
51    /// In TS you'd write (pseudocode):
52    /// ```ts
53    /// constructor(message: string) { super(message); }
54    /// ```
55    pub fn new(message: &str) -> ConfigError {
56        // What: `return ConfigError { message: ... };` builds the struct and hands it back.
57        // Why:  Rust never copies a borrowed string into an owning field implicitly.
58        //
59        // In TS you'd write (pseudocode):
60        // ```ts
61        // return { message };
62        // ```
63        return ConfigError {
64            message: String::from(message),
65        };
66    }
67}
68
69/// What:
70///  `impl std::fmt::Display for ConfigError` supplies Rust's "print me" interface.
71///       `::` walks module paths,
72///  like `std.fmt.Display` would in TS.
73/// Why:
74///   The executable writes the message with ordinary formatting,
75///  not field access.
76///
77/// In TS you'd write (pseudocode):
78/// ```ts
79/// toString(): string { return this.message; }
80/// ```
81impl std::fmt::Display for ConfigError {
82    /// What:
83    ///  `&self` borrows this error read-only;
84    ///  `&mut` lends the formatter for writing;
85    ///       `'_` is an unnamed lifetime the compiler fills in.
86    /// Why:
87    ///   Formatting writes into the caller's buffer without allocating another string.
88    ///
89    /// In TS you'd write (pseudocode):
90    /// ```ts
91    /// format(out: Writer): void { out.write(this.message); }
92    /// ```
93    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94        // `.as_str()` lends the owned message as a borrowed view for the writer.
95        return formatter.write_str(self.message.as_str());
96    }
97}
98
99/// What:
100///  An empty `impl` marks the type as a standard error value.
101/// Why:
102///   Generic error handling can carry it without converting it to a bare string.
103///
104/// In TS you'd write (pseudocode):
105/// ```ts
106/// // `extends Error` already provides this in TypeScript.
107/// ```
108impl std::error::Error for ConfigError {}
109
110/// Message controls stay out of the release executable.
111#[cfg(test)]
112#[path = "config_error_tests.rs"]
113mod tests;