1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
//! Convenience macros for `libvctrl_handler`.
//!
//! # Purpose
//!
//! This module provides macros that simplify common error-handling patterns
//! used throughout the crate. They are exported with `#[macro_export]` and
//! can therefore be used by downstream code as well as by internal modules.
//!
//! # Design Rationale
//!
//! - **`vctrl_error_other!`** reduces boilerplate when constructing the
//! catch-all [`VctrlError::Other`](crate::VctrlError::Other) variant. It
//! mirrors the standard [`format!`] syntax, making call sites familiar.
//! - **`string_payload_variants!`** centralizes a repetitive `match` pattern
//! used by the [`PartialEq`] implementation of [`VctrlError`]. Instead of
//! manually extracting string payloads from many variants, the macro
//! generates a private helper function.
//!
//! # Macro Hygiene
//!
//! Both macros use `$crate`-qualified paths where appropriate. This ensures
//! that the generated code refers to the correct crate even when the macros
//! are re-exported or used from downstream crates with different names.
//!
//! # Internal Mechanism
//!
//! [`vctrl_error_other!`] performs a standard token expansion: it wraps the
//! result of `format!` directly in [`VctrlError::Other`].
//!
//! [`string_payload_variants!`] accepts a comma-separated list of variant
//! identifiers. It expands into a `const fn string_payload` that matches each
//! listed variant and returns `Some(s.as_str())`. The generated function is
//! scoped to the location where the macro is invoked, typically inside the
//! `eq` method of `impl PartialEq for VctrlError`.
//!
//! # Examples
//!
//! Constructing a formatted error:
//!
//! ```
//! use libvctrl_handler::vctrl_error_other;
//!
//! let err = vctrl_error_other!("failed to open '{}': {}", "config.toml", "permission denied");
//! assert_eq!(
//! err.to_string(),
//! "failed to open 'config.toml': permission denied"
//! );
//! ```
/// Creates a [`VctrlError::Other`] variant with a formatted message.
///
/// This macro is a shorthand for building miscellaneous errors without
/// manually calling `format!`. It accepts the same arguments as `format!`
/// and wraps the result in [`VctrlError::Other`].
///
/// # Design Rationale
///
/// Error construction is frequent in fallible code. By providing a macro,
/// callers can avoid the visual noise of `VctrlError::Other(format!(...))`
/// and instead write a single concise invocation.
///
/// # How It Works
///
/// The macro expands to:
///
/// ```text
/// $crate::VctrlError::Other(format!($($arg)*))
/// ```
///
/// The use of `$crate` guarantees that the macro resolves the correct
/// `VctrlError` type even if the macro is called from a downstream crate
/// that imports the macro under a different name.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// # use libvctrl_handler::vctrl_error_other;
/// let err = vctrl_error_other!("failed to open '{}': {}", "config.toml", "permission denied");
/// assert_eq!(
/// err.to_string(),
/// "failed to open 'config.toml': permission denied"
/// );
/// ```
///
/// Using with format specifiers:
///
/// ```
/// # use libvctrl_handler::vctrl_error_other;
/// let code = 42;
/// let err = vctrl_error_other!("unexpected exit code {code}");
/// assert_eq!(err.to_string(), "unexpected exit code 42");
/// ```
///
/// The returned value is a [`VctrlError`](crate::VctrlError), so it can be
/// propagated with the `?` operator in functions returning
/// [`Result<T, VctrlError>`](crate::VctrlError):
///
/// ```
/// # use libvctrl_handler::{vctrl_error_other, VctrlError};
/// fn fallible(code: u32) -> Result<(), VctrlError> {
/// if code != 0 {
/// return Err(vctrl_error_other!("non-zero exit code: {code}"));
/// }
/// Ok(())
/// }
///
/// assert!(fallible(1).is_err());
/// assert!(fallible(0).is_ok());
/// ```
/// Helper macro to generate the `string_payload` function for [`VctrlError`].
///
/// This macro is used inside the [`PartialEq`] implementation of
/// [`VctrlError`](crate::VctrlError) to extract the string payload from all
/// variants that carry a [`String`]. It must be exported because it is
/// invoked from the `errors` module.
///
/// # Design Rationale
///
/// [`VctrlError`](crate::VctrlError) has several string-bearing variants:
/// [`InvalidName`](crate::VctrlError::InvalidName),
/// [`InvalidEmail`](crate::VctrlError::InvalidEmail),
/// [`RefNotFound`](crate::VctrlError::RefNotFound),
/// [`CorruptedData`](crate::VctrlError::CorruptedData),
/// [`SerializationError`](crate::VctrlError::SerializationError), and
/// [`Other`](crate::VctrlError::Other). In the `eq` method, these variants
/// must be compared by their string content. The macro avoids repeating the
/// same `match` arm for every variant.
///
/// # How It Works
///
/// The macro accepts a list of variant identifiers and expands to a local
/// `const fn string_payload(v: &VctrlError) -> Option<&str>` that returns
/// `Some(s.as_str())` for the listed variants and `None` otherwise. The
/// function is generated at the invocation site, so the surrounding scope
/// must already have `VctrlError` in scope.
///
/// # Examples
///
/// The macro can be used with a locally defined error enum to generate a
/// payload extractor:
///
/// ```
/// use libvctrl_handler::string_payload_variants;
///
/// enum VctrlError {
/// InvalidName(String),
/// RefNotFound(String),
/// }
///
/// string_payload_variants!(InvalidName, RefNotFound);
///
/// let err = VctrlError::InvalidName("bad".to_string());
/// assert_eq!(string_payload(&err), Some("bad"));
///
/// let other = VctrlError::RefNotFound("main".to_string());
/// assert_eq!(string_payload(&other), Some("main"));
/// ```