vanguards_rs/
error.rs

1//! Error types for vanguards-rs.
2//!
3//! This module defines the [`enum@Error`] enum representing all possible error conditions
4//! in the vanguards-rs library. Each variant provides specific information about
5//! the failure and guidance on recovery.
6//!
7//! # Overview
8//!
9//! The error system is designed to provide:
10//!
11//! - **Specific error types** for different failure categories
12//! - **Recovery guidance** for each error type
13//! - **Seamless integration** with stem-rs errors
14//! - **Informative messages** without leaking sensitive data
15//!
16//! # Error Categories
17//!
18//! ```text
19//!   Error
20//!   ├── Io                     ◄── File/network I/O failures
21//!   ├── Config                 ◄── Invalid configuration
22//!   ├── Control                ◄── Tor control protocol errors (from stem-rs)
23//!   ├── State                  ◄── State file corruption/format issues
24//!   ├── Consensus              ◄── Consensus parsing failures
25//!   ├── NoNodesRemain          ◄── All relays filtered out
26//!   ├── Validation             ◄── Invalid input data
27//!   └── DescriptorUnavailable  ◄── Missing descriptors
28//! ```
29//!
30//! # Recovery Guide
31//!
32//! | Error | Recoverable | Retry | Recommended Action |
33//! |-------|-------------|-------|-------------------|
34//! | [`Io`](Error::Io) | Sometimes | Yes (backoff) | Check permissions, disk space |
35//! | [`Config`](Error::Config) | No | No | Fix configuration file |
36//! | [`Control`](Error::Control) | Sometimes | Yes | Reconnect to Tor |
37//! | [`State`](Error::State) | Sometimes | No | Delete state file, restart |
38//! | [`Consensus`](Error::Consensus) | Sometimes | Yes | Wait for new consensus |
39//! | [`NoNodesRemain`](Error::NoNodesRemain) | No | No | Adjust ExcludeNodes |
40//! | [`Validation`](Error::Validation) | No | No | Fix input data |
41//! | [`DescriptorUnavailable`](Error::DescriptorUnavailable) | Yes | Yes | Wait for bootstrap |
42//!
43//! # Example
44//!
45//! ## Basic Error Handling
46//!
47//! ```rust
48//! use vanguards_rs::{Config, Error, Result};
49//!
50//! fn load_config() -> Result<Config> {
51//!     let config = Config::from_file(std::path::Path::new("vanguards.conf"))?;
52//!     config.validate()?;
53//!     Ok(config)
54//! }
55//!
56//! fn main() {
57//!     match load_config() {
58//!         Ok(config) => println!("Config loaded successfully"),
59//!         Err(Error::Io(e)) => eprintln!("File error: {}", e),
60//!         Err(Error::Config(msg)) => eprintln!("Config error: {}", msg),
61//!         Err(e) => eprintln!("Other error: {}", e),
62//!     }
63//! }
64//! ```
65//!
66//! ## Retry Logic
67//!
68//! ```rust,no_run
69//! use vanguards_rs::{Error, Result};
70//! use std::time::Duration;
71//!
72//! async fn with_retry<F, T>(mut f: F, max_retries: u32) -> Result<T>
73//! where
74//!     F: FnMut() -> Result<T>,
75//! {
76//!     let mut attempts = 0;
77//!     loop {
78//!         match f() {
79//!             Ok(result) => return Ok(result),
80//!             Err(Error::Io(_)) | Err(Error::Control(_)) if attempts < max_retries => {
81//!                 attempts += 1;
82//!                 tokio::time::sleep(Duration::from_secs(1 << attempts)).await;
83//!             }
84//!             Err(e) => return Err(e),
85//!         }
86//!     }
87//! }
88//! ```
89//!
90//! # See Also
91//!
92//! - [`Result`] - Type alias for `std::result::Result<T, Error>`
93//! - [`stem_rs::Error`] - Underlying Tor control errors
94//! - [`Config::validate`](crate::Config::validate) - Configuration validation
95
96use thiserror::Error;
97
98/// Errors that can occur during vanguards-rs operations.
99///
100/// This enum represents all possible error conditions in the library.
101/// Each variant provides specific information about the failure and
102/// guidance on recovery.
103///
104/// # Error Handling Patterns
105///
106/// ## Match on Specific Errors
107///
108/// ```rust
109/// use vanguards_rs::Error;
110///
111/// fn handle_error(err: Error) {
112///     match err {
113///         Error::Io(io_err) => {
114///             eprintln!("I/O error: {}", io_err);
115///             // Check file permissions, disk space, network
116///         }
117///         Error::Config(msg) => {
118///             eprintln!("Configuration error: {}", msg);
119///             // Fix configuration and restart
120///         }
121///         Error::Control(ctrl_err) => {
122///             eprintln!("Tor control error: {}", ctrl_err);
123///             // Reconnect to Tor
124///         }
125///         Error::State(msg) => {
126///             eprintln!("State file error: {}", msg);
127///             // Delete state file and restart
128///         }
129///         Error::Consensus(msg) => {
130///             eprintln!("Consensus error: {}", msg);
131///             // Wait for Tor to get new consensus
132///         }
133///         Error::NoNodesRemain => {
134///             eprintln!("No nodes remain after filtering");
135///             // Adjust ExcludeNodes configuration
136///         }
137///         Error::Validation(msg) => {
138///             eprintln!("Validation error: {}", msg);
139///             // Fix invalid input
140///         }
141///         Error::DescriptorUnavailable(msg) => {
142///             eprintln!("Descriptor unavailable: {}", msg);
143///             // Wait for Tor to finish bootstrapping
144///         }
145///     }
146/// }
147/// ```
148///
149/// ## Check if Retryable
150///
151/// ```rust
152/// use vanguards_rs::Error;
153///
154/// fn is_retryable(err: &Error) -> bool {
155///     matches!(err,
156///         Error::Io(_) |
157///         Error::Control(_) |
158///         Error::Consensus(_) |
159///         Error::DescriptorUnavailable(_)
160///     )
161/// }
162/// ```
163///
164/// # See Also
165///
166/// - [`Result`] - Type alias using this error type
167/// - [`stem_rs::Error`] - Underlying control protocol errors
168#[derive(Debug, Error)]
169pub enum Error {
170    /// I/O error during file or network operations.
171    ///
172    /// This error wraps standard I/O errors that occur during file operations
173    /// (reading/writing state files, config files) or network operations.
174    ///
175    /// # Recovery
176    ///
177    /// - Check file permissions and paths
178    /// - Retry with exponential backoff for transient issues
179    /// - Verify disk space for write operations
180    #[error("I/O error: {0}")]
181    Io(#[from] std::io::Error),
182
183    /// Configuration error.
184    ///
185    /// This error indicates invalid configuration values or parsing failures.
186    /// The message describes what was wrong with the configuration.
187    ///
188    /// # Recovery
189    ///
190    /// Fix the configuration file or command-line arguments. This error
191    /// is not recoverable without user intervention.
192    #[error("configuration error: {0}")]
193    Config(String),
194
195    /// Tor control protocol error.
196    ///
197    /// This error wraps errors from stem-rs when communicating with Tor's
198    /// control port.
199    ///
200    /// # Recovery
201    ///
202    /// - Check if Tor is running
203    /// - Verify control port configuration
204    /// - Retry connection with backoff
205    #[error("Tor control error: {0}")]
206    Control(#[from] stem_rs::Error),
207
208    /// State file error.
209    ///
210    /// This error indicates problems with the vanguard state file, such as
211    /// corruption, invalid format, or incompatible version.
212    ///
213    /// # Recovery
214    ///
215    /// - Delete the corrupted state file and let vanguards create a fresh one
216    /// - Check file permissions
217    /// - Verify the file wasn't modified externally
218    #[error("state file error: {0}")]
219    State(String),
220
221    /// Consensus parsing error.
222    ///
223    /// This error occurs when parsing the network consensus fails.
224    ///
225    /// # Recovery
226    ///
227    /// - Wait for a new consensus
228    /// - Verify Tor has finished bootstrapping
229    /// - Check DataDirectory configuration
230    #[error("consensus parse error: {0}")]
231    Consensus(String),
232
233    /// No nodes remain after applying restrictions.
234    ///
235    /// This error occurs when all relays are filtered out by the configured
236    /// restrictions (ExcludeNodes, flag requirements, etc.).
237    ///
238    /// # Recovery
239    ///
240    /// - Review ExcludeNodes configuration
241    /// - Reduce restrictions
242    /// - Wait for more relays to appear in consensus
243    #[error("no nodes remain after restrictions")]
244    NoNodesRemain,
245
246    /// Input validation error.
247    ///
248    /// This error indicates that input data failed validation checks.
249    ///
250    /// # Recovery
251    ///
252    /// Fix the invalid input. This error is not recoverable without
253    /// correcting the input data.
254    #[error("validation error: {0}")]
255    Validation(String),
256
257    /// Descriptor unavailable.
258    ///
259    /// This error occurs when Tor doesn't have the required descriptors
260    /// cached yet, typically during bootstrap.
261    ///
262    /// # Recovery
263    ///
264    /// - Wait for Tor to finish bootstrapping
265    /// - Retry after a short delay
266    #[error("descriptor unavailable: {0}")]
267    DescriptorUnavailable(String),
268}
269
270/// Result type alias for vanguards-rs operations.
271///
272/// This is a convenience alias for `std::result::Result<T, Error>` used
273/// throughout the vanguards-rs library.
274///
275/// # Example
276///
277/// ```rust
278/// use vanguards_rs::{Config, Result};
279///
280/// fn load_and_validate_config() -> Result<Config> {
281///     let config = Config::from_file(std::path::Path::new("vanguards.conf"))?;
282///     config.validate()?;
283///     Ok(config)
284/// }
285/// ```
286///
287/// # See Also
288///
289/// - [`enum@Error`] - The error type used in this result
290pub type Result<T> = std::result::Result<T, Error>;