stem_rs/
lib.rs

1//! # stem-rs
2//!
3//! A Rust implementation of the Stem library for Tor control protocol interaction.
4//!
5//! # Overview
6//!
7//! stem-rs provides idiomatic Rust APIs for interacting with Tor's control protocol,
8//! maintaining functional parity with Python Stem. The library enables:
9//!
10//! - Control socket communication (TCP and Unix domain sockets)
11//! - All authentication methods (NONE, PASSWORD, COOKIE, SAFECOOKIE)
12//! - High-level Controller API for Tor interaction
13//! - Complete descriptor parsing (server, micro, consensus, extra-info, hidden service)
14//! - Event subscription and handling
15//! - Exit policy parsing and evaluation
16//! - ORPort relay communication
17//! - Version parsing and comparison
18//!
19//! # Feature Flags
20//!
21//! stem-rs uses feature flags to allow you to compile only what you need, reducing
22//! compile time and binary size.
23//!
24//! ## Default Features
25//!
26//! By default, all features are enabled:
27//!
28//! ```toml
29//! [dependencies]
30//! stem-rs = "1.2"  # Includes all features
31//! ```
32//!
33//! ## Minimal Build
34//!
35//! For a minimal build with just the core functionality:
36//!
37//! ```toml
38//! [dependencies]
39//! stem-rs = { version = "1.2", default-features = false }
40//! ```
41//!
42//! This includes: socket communication, authentication, protocol parsing, utilities,
43//! and version handling.
44//!
45//! ## Available Features
46//!
47//! | Feature | Description | Dependencies |
48//! |---------|-------------|--------------|
49//! | `full` | All features (default) | All features below |
50//! | `controller` | High-level Controller API | `events` |
51//! | `descriptors` | Tor descriptor parsing | `client`, `exit-policy` |
52//! | `events` | Event subscription and handling | None |
53//! | `exit-policy` | Exit policy parsing and evaluation | None |
54//! | `client` | ORPort relay communication | None |
55//! | `interpreter` | Interactive Tor control interpreter | `controller`, `events` |
56//! | `compression` | Gzip decompression for descriptors | None |
57//!
58//! ## Custom Feature Combinations
59//!
60//! **Controller only** (no descriptor parsing):
61//! ```toml
62//! [dependencies]
63//! stem-rs = { version = "1.2", default-features = false, features = ["controller"] }
64//! ```
65//!
66//! **Descriptors only** (offline analysis):
67//! ```toml
68//! [dependencies]
69//! stem-rs = { version = "1.2", default-features = false, features = ["descriptors"] }
70//! ```
71//!
72//! **Controller + Descriptors** (most common):
73//! ```toml
74//! [dependencies]
75//! stem-rs = { version = "1.2", default-features = false, features = ["controller", "descriptors"] }
76//! ```
77//!
78//! ## Compile Time Improvements
79//!
80//! Approximate compile time reductions with feature flags:
81//!
82//! - **Minimal build**: ~40% faster (excludes descriptors, controller, events)
83//! - **Controller-only**: ~30% faster (excludes descriptor parsing)
84//! - **Descriptors-only**: ~20% faster (excludes controller, events)
85//!
86//! Binary size reductions follow similar patterns.
87//!
88//! # Choosing the Right Library: stem-rs vs tor-metrics-library
89//!
90//! ## Use stem-rs for:
91//! Real-time Tor control, live network interaction (circuits, streams, hidden services),
92//! event monitoring, configuration management, and interactive applications.
93//!
94//! ## Use tor-metrics-library for:
95//! Historical analysis, batch processing of archived descriptors, metrics collection,
96//! database export, network research, and async streaming of large archives.
97//!
98//! # Architecture
99//!
100//! The library is organized into these primary modules:
101//!
102//! - [`socket`]: Low-level control socket communication
103//! - [`auth`]: Authentication methods and protocol info
104//! - [`controller`]: High-level Controller API
105//! - [`descriptor`]: Tor descriptor parsing
106//! - [`events`]: Event types and handling
107//! - [`exit_policy`]: Exit policy evaluation
108//! - [`client`]: Direct ORPort relay communication
109//! - [`response`]: Control protocol response parsing
110//! - [`interpreter`]: Interactive Tor control interpreter
111//! - [`version`]: Tor version parsing and comparison
112//! - [`util`]: Validation utilities for fingerprints, nicknames, etc.
113//!
114//! # Quick Start
115//!
116//! ```rust,no_run
117//! use stem_rs::{controller::Controller, Error};
118//!
119//! #[tokio::main]
120//! async fn main() -> Result<(), Error> {
121//!     // Connect to Tor's control port
122//!     let mut controller = Controller::from_port("127.0.0.1:9051".parse().unwrap()).await?;
123//!     
124//!     // Authenticate (auto-detects method)
125//!     controller.authenticate(None).await?;
126//!     
127//!     // Query Tor version
128//!     let version = controller.get_version().await?;
129//!     println!("Connected to Tor {}", version);
130//!     
131//!     Ok(())
132//! }
133//! ```
134//!
135//! # Using Descriptors with Controller
136//!
137//! The [`controller::Controller`] provides methods to retrieve and work with
138//! Tor network descriptors. This enables intelligent circuit building, relay
139//! selection, and network analysis.
140//!
141//! ## Retrieving Network Consensus
142//!
143//! The consensus document contains the current state of the Tor network:
144//!
145//! ```rust,no_run
146//! use stem_rs::controller::Controller;
147//!
148//! # async fn example() -> Result<(), stem_rs::Error> {
149//! let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
150//! controller.authenticate(None).await?;
151//!
152//! let consensus = controller.get_consensus().await?;
153//! println!("Network has {} authorities", consensus.authorities.len());
154//! println!("Consensus valid from {} to {}",
155//!          consensus.valid_after, consensus.valid_until);
156//! # Ok(())
157//! # }
158//! ```
159//!
160//! ## Finding Relays by Flags
161//!
162//! Filter relays based on directory authority flags:
163//!
164//! ```rust,no_run
165//! use stem_rs::{controller::Controller, Flag};
166//!
167//! # async fn example() -> Result<(), stem_rs::Error> {
168//! let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
169//! controller.authenticate(None).await?;
170//!
171//! let guard_relays = controller.find_relays_by_flag(Flag::Guard).await?;
172//! let exit_relays = controller.find_relays_by_flag(Flag::Exit).await?;
173//! let fast_stable = controller.find_relays_by_flag(Flag::Fast).await?
174//!     .into_iter()
175//!     .filter(|r| r.flags.contains(&"Stable".to_string()))
176//!     .collect::<Vec<_>>();
177//!
178//! println!("Found {} guards, {} exits, {} fast+stable relays",
179//!          guard_relays.len(), exit_relays.len(), fast_stable.len());
180//! # Ok(())
181//! # }
182//! ```
183//!
184//! ## Selecting High-Performance Relays
185//!
186//! Find the fastest relays for high-bandwidth circuits:
187//!
188//! ```rust,no_run
189//! use stem_rs::controller::Controller;
190//!
191//! # async fn example() -> Result<(), stem_rs::Error> {
192//! let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
193//! controller.authenticate(None).await?;
194//!
195//! let top_10 = controller.find_fastest_relays(10).await?;
196//! for (i, relay) in top_10.iter().enumerate() {
197//!     println!("#{}: {} - {} KB/s",
198//!              i + 1, relay.nickname, relay.bandwidth.unwrap_or(0));
199//! }
200//! # Ok(())
201//! # }
202//! ```
203//!
204//! ## Bandwidth-Weighted Guard Selection
205//!
206//! Select guard relays using Tor's bandwidth-weighted algorithm:
207//!
208//! ```rust,no_run
209//! use stem_rs::controller::Controller;
210//!
211//! # async fn example() -> Result<(), stem_rs::Error> {
212//! let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
213//! controller.authenticate(None).await?;
214//!
215//! if let Some(guard) = controller.select_guard_relay().await? {
216//!     println!("Selected guard: {} ({})", guard.nickname, guard.fingerprint);
217//!     println!("Bandwidth: {} KB/s", guard.bandwidth.unwrap_or(0));
218//! }
219//! # Ok(())
220//! # }
221//! ```
222//!
223//! ## Building Circuits with Descriptor Data
224//!
225//! Use descriptor information to build circuits through specific relays:
226//!
227//! ```rust,no_run
228//! use stem_rs::{controller::{Controller, CircuitId}, Flag};
229//!
230//! # async fn example() -> Result<(), stem_rs::Error> {
231//! let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
232//! controller.authenticate(None).await?;
233//!
234//! let guard = controller.select_guard_relay().await?
235//!     .ok_or_else(|| stem_rs::Error::Protocol("No guards available".into()))?;
236//!
237//! let middle_relays = controller.find_fastest_relays(100).await?;
238//! let middle = middle_relays.get(0)
239//!     .ok_or_else(|| stem_rs::Error::Protocol("No middle relays".into()))?;
240//!
241//! let exit_relays = controller.find_relays_by_flag(Flag::Exit).await?;
242//! let exit = exit_relays.get(0)
243//!     .ok_or_else(|| stem_rs::Error::Protocol("No exit relays".into()))?;
244//!
245//! let path = vec![
246//!     guard.fingerprint.as_str(),
247//!     middle.fingerprint.as_str(),
248//!     exit.fingerprint.as_str(),
249//! ];
250//!
251//! let circuit_id = CircuitId("0".to_string());
252//! controller.extend_circuit(&circuit_id, &path).await?;
253//! println!("Built circuit through {} -> {} -> {}",
254//!          guard.nickname, middle.nickname, exit.nickname);
255//! # Ok(())
256//! # }
257//! ```
258//!
259//! ## Filtering by Exit Policy
260//!
261//! Find exit relays that allow specific destinations:
262//!
263//! ```rust,no_run
264//! use stem_rs::{controller::Controller, Flag};
265//! use std::net::IpAddr;
266//!
267//! # async fn example() -> Result<(), stem_rs::Error> {
268//! let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
269//! controller.authenticate(None).await?;
270//!
271//! let exit_relays = controller.find_relays_by_flag(Flag::Exit).await?;
272//!
273//! let https_exits: Vec<_> = exit_relays.into_iter()
274//!     .filter(|relay| {
275//!         relay.exit_policy.as_ref()
276//!             .map(|policy| policy.can_exit_to(443))
277//!             .unwrap_or(false)
278//!     })
279//!     .collect();
280//!
281//! println!("Found {} exits allowing HTTPS", https_exits.len());
282//! # Ok(())
283//! # }
284//! ```
285//!
286//! ## Retrieving Full Relay Descriptors
287//!
288//! Get detailed information about specific relays:
289//!
290//! ```rust,no_run
291//! use stem_rs::controller::Controller;
292//!
293//! # async fn example() -> Result<(), stem_rs::Error> {
294//! let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
295//! controller.authenticate(None).await?;
296//!
297//! let entries = controller.get_router_status_entries().await?;
298//! if let Some(relay) = entries.first() {
299//!     let descriptor = controller
300//!         .get_server_descriptor(&relay.fingerprint)
301//!         .await?;
302//!     
303//!     println!("Relay: {} at {}", descriptor.nickname, descriptor.address);
304//!     let platform_str = descriptor.platform.as_ref()
305//!         .and_then(|p| std::str::from_utf8(p).ok())
306//!         .unwrap_or("unknown");
307//!     println!("Platform: {}", platform_str);
308//!     println!("Bandwidth: avg={}, burst={}, observed={}",
309//!              descriptor.bandwidth_avg,
310//!              descriptor.bandwidth_burst,
311//!              descriptor.bandwidth_observed);
312//!     println!("Exit policy: {}", descriptor.exit_policy);
313//! }
314//! # Ok(())
315//! # }
316//! ```
317//!
318//! ## Monitoring Network Changes
319//!
320//! Subscribe to descriptor events to track network changes:
321//!
322//! ```rust,no_run
323//! use stem_rs::{controller::Controller, EventType};
324//!
325//! # async fn example() -> Result<(), stem_rs::Error> {
326//! let mut controller = Controller::from_port("127.0.0.1:9051".parse()?).await?;
327//! controller.authenticate(None).await?;
328//!
329//! controller.set_events(&[EventType::NewDesc]).await?;
330//!
331//! loop {
332//!     let event = controller.recv_event().await?;
333//!     match event {
334//!         stem_rs::events::ParsedEvent::NewDesc(desc_event) => {
335//!             println!("New descriptors: {} relays", desc_event.relays.len());
336//!             
337//!             for (fingerprint, _nickname) in &desc_event.relays {
338//!                 if let Ok(desc) = controller.get_server_descriptor(fingerprint).await {
339//!                     println!("Updated relay: {} at {}", desc.nickname, desc.address);
340//!                 }
341//!             }
342//!         }
343//!         _ => {}
344//!     }
345//! }
346//! # Ok(())
347//! # }
348//! ```
349//!
350//! ## Best Practices
351//!
352//! - **Cache descriptors**: Consensus documents are valid for 3 hours, cache them
353//! - **Validate descriptors**: Use `descriptor.validate()` to check for malformed data
354//! - **Handle unavailable descriptors**: Not all relays have cached descriptors
355//! - **Respect bandwidth weights**: Use bandwidth-weighted selection for fairness
356//! - **Filter by flags**: Always check Guard/Exit/Fast/Stable flags for circuit building
357//! - **Monitor events**: Subscribe to NEWDESC/NEWCONSENSUS to stay current
358//!
359//! # Thread Safety
360//!
361//! The [`controller::Controller`] type is `Send` but not `Sync`. For concurrent access,
362//! wrap it in `Arc<Mutex<Controller>>` or use separate connections.
363//!
364//! # Security Considerations
365//!
366//! - Authentication tokens are cleared from memory after use
367//! - Constant-time comparison is used for sensitive data (see [`util::secure_compare`])
368//! - Input validation prevents protocol injection attacks
369//!
370//! # Error Handling
371//!
372//! All fallible operations return [`Result<T, Error>`]. The [`enum@Error`] enum provides
373//! specific error variants for different failure modes:
374//!
375//! - [`Error::Socket`] - I/O and connection failures
376//! - [`Error::Authentication`] - Authentication failures (see [`AuthError`])
377//! - [`Error::OperationFailed`] - Tor rejected the operation
378//! - [`Error::Descriptor`] - Descriptor parsing failures (see [`descriptor::DescriptorError`])
379//! - [`Error::Parse`] - Legacy parse errors (deprecated, use [`Error::Descriptor`])
380//!
381//! See the [`enum@Error`] documentation for recovery guidance.
382
383#![warn(missing_docs)]
384#![warn(rustdoc::broken_intra_doc_links)]
385
386pub mod auth;
387#[cfg(feature = "client")]
388pub mod client;
389#[cfg(feature = "controller")]
390pub mod controller;
391#[cfg(feature = "descriptors")]
392pub mod descriptor;
393#[cfg(feature = "events")]
394pub mod events;
395#[cfg(feature = "exit-policy")]
396pub mod exit_policy;
397#[cfg(feature = "interpreter")]
398pub mod interpreter;
399pub mod protocol;
400pub mod response;
401pub mod socket;
402pub mod types;
403pub mod util;
404pub mod version;
405
406// Re-export commonly used types at crate root
407#[cfg(feature = "controller")]
408pub use controller::Controller;
409pub use socket::ControlSocket;
410pub use version::Version;
411
412use std::fmt;
413use thiserror::Error;
414
415/// Errors that can occur during stem-rs operations.
416///
417/// This enum represents all possible error conditions in the library.
418/// Each variant provides specific information about the failure.
419///
420/// # Error Categories
421///
422/// - **I/O Errors**: [`Socket`](Error::Socket) - Connection and communication failures
423/// - **Protocol Errors**: [`Protocol`](Error::Protocol) - Malformed control protocol data
424/// - **Auth Errors**: [`Authentication`](Error::Authentication) - Authentication failures
425/// - **Operation Errors**: [`OperationFailed`](Error::OperationFailed) - Tor rejected the request
426/// - **Descriptor Errors**: [`Descriptor`](Error::Descriptor) - Descriptor parsing failures
427/// - **Parse Errors**: [`Parse`](Error::Parse) - Legacy parse errors (deprecated)
428///
429/// # Recovery Guide
430///
431/// | Error | Recoverable | Retry Meaningful |
432/// |-------|-------------|------------------|
433/// | [`Socket`](Error::Socket) | Sometimes | Yes, with backoff |
434/// | [`Protocol`](Error::Protocol) | No | No |
435/// | [`Authentication`](Error::Authentication) | Sometimes | Yes, with different credentials |
436/// | [`OperationFailed`](Error::OperationFailed) | Depends on code | Check error code |
437/// | [`Descriptor`](Error::Descriptor) | No | No |
438/// | [`Parse`](Error::Parse) | No | No |
439/// | [`Timeout`](Error::Timeout) | Yes | Yes, with longer timeout |
440/// | [`SocketClosed`](Error::SocketClosed) | Yes | Yes, reconnect first |
441/// | [`Download`](Error::Download) | Sometimes | Yes, with backoff |
442/// | [`DownloadTimeout`](Error::DownloadTimeout) | Yes | Yes, with longer timeout |
443///
444/// # Example
445///
446/// ```rust
447/// use stem_rs::Error;
448///
449/// fn handle_error(err: Error) {
450///     match err {
451///         Error::Socket(io_err) => {
452///             eprintln!("Connection failed: {}", io_err);
453///             // Retry with exponential backoff
454///         }
455///         Error::Authentication(auth_err) => {
456///             eprintln!("Auth failed: {}", auth_err);
457///             // Check credentials or try different auth method
458///         }
459///         Error::Descriptor(desc_err) => {
460///             eprintln!("Descriptor parse error: {}", desc_err);
461///             // Log and skip this descriptor
462///         }
463///         Error::Parse { location, reason } => {
464///             eprintln!("Parse error at {}: {}", location, reason);
465///             // Log and skip this descriptor (legacy error)
466///         }
467///         Error::OperationFailed { code, message } => {
468///             eprintln!("Tor rejected request: {} - {}", code, message);
469///             // Check if operation can be retried
470///         }
471///         _ => eprintln!("Error: {}", err),
472///     }
473/// }
474/// ```
475#[derive(Debug, Error)]
476pub enum Error {
477    /// I/O error during socket communication.
478    ///
479    /// This error wraps standard I/O errors that occur during socket operations.
480    /// Common causes include connection refused, connection reset, and network
481    /// unreachable errors.
482    ///
483    /// # Recovery
484    ///
485    /// - Check if Tor is running and the control port is accessible
486    /// - Retry with exponential backoff for transient network issues
487    /// - Verify firewall rules allow the connection
488    #[error("socket error: {0}")]
489    Socket(#[from] std::io::Error),
490
491    /// Malformed data received from the control protocol.
492    ///
493    /// This indicates the data received from Tor doesn't conform to the
494    /// expected control protocol format. This typically indicates a bug
495    /// in either Tor or this library.
496    ///
497    /// # Recovery
498    ///
499    /// This error is not recoverable. Report the issue with the malformed data.
500    #[error("protocol error: {0}")]
501    Protocol(String),
502
503    /// Authentication with Tor failed.
504    ///
505    /// See [`AuthError`] for specific authentication failure reasons.
506    ///
507    /// # Recovery
508    ///
509    /// - Check credentials (password, cookie file path)
510    /// - Verify Tor's authentication configuration
511    /// - Try a different authentication method
512    #[error("authentication failed: {0}")]
513    Authentication(#[from] AuthError),
514
515    /// Tor was unable to complete the requested operation.
516    ///
517    /// This error is returned when Tor understands the request but cannot
518    /// fulfill it. The error code and message provide details about why.
519    ///
520    /// # Fields
521    ///
522    /// - `code`: The numeric error code from Tor (e.g., "552")
523    /// - `message`: Human-readable error description from Tor
524    ///
525    /// # Recovery
526    ///
527    /// Check the error code to determine if retry is meaningful:
528    /// - 4xx codes: Client error, fix the request
529    /// - 5xx codes: Server error, may be transient
530    #[error("operation failed: {code} {message}")]
531    OperationFailed {
532        /// The error code returned by Tor.
533        code: String,
534        /// The error message returned by Tor.
535        message: String,
536    },
537
538    /// The request cannot be satisfied with current Tor state.
539    ///
540    /// This error indicates a valid request that Tor cannot fulfill due to
541    /// its current state (e.g., requesting a circuit when Tor is not connected).
542    ///
543    /// # Recovery
544    ///
545    /// Wait for Tor to reach the required state, then retry.
546    #[error("unsatisfiable request: {0}")]
547    UnsatisfiableRequest(String),
548
549    /// The request was malformed or invalid.
550    ///
551    /// This indicates a programming error - the request doesn't conform
552    /// to the control protocol specification.
553    ///
554    /// # Recovery
555    ///
556    /// Fix the request format. This is not a transient error.
557    #[error("invalid request: {0}")]
558    InvalidRequest(String),
559
560    /// The request contained invalid arguments.
561    ///
562    /// Similar to [`InvalidRequest`](Error::InvalidRequest), but specifically
563    /// for argument validation failures.
564    ///
565    /// # Recovery
566    ///
567    /// Fix the arguments. This is not a transient error.
568    #[error("invalid arguments: {0}")]
569    InvalidArguments(String),
570
571    /// Failed to parse a descriptor or other structured data.
572    ///
573    /// This error occurs when parsing Tor descriptors (server descriptors,
574    /// consensus documents, etc.) and the data doesn't match the expected format.
575    ///
576    /// See [`descriptor::DescriptorError`] for specific descriptor error types.
577    ///
578    /// # Recovery
579    ///
580    /// This error is not recoverable for the specific descriptor. Log the
581    /// error and skip to the next descriptor if processing multiple.
582    #[cfg(feature = "descriptors")]
583    #[error("descriptor parse error: {0}")]
584    Descriptor(#[from] crate::descriptor::DescriptorError),
585
586    /// Failed to parse a descriptor or other structured data (legacy).
587    ///
588    /// This error occurs when parsing Tor descriptors (server descriptors,
589    /// consensus documents, etc.) and the data doesn't match the expected format.
590    ///
591    /// # Fields
592    ///
593    /// - `location`: Where in the data the parse error occurred
594    /// - `reason`: Description of what was expected vs. found
595    ///
596    /// # Recovery
597    ///
598    /// This error is not recoverable for the specific descriptor. Log the
599    /// error and skip to the next descriptor if processing multiple.
600    ///
601    /// # Note
602    ///
603    /// This variant is deprecated in favor of [`Error::Descriptor`] which provides
604    /// more specific error information. It is kept for backward compatibility.
605    #[error("parse error at {location}: {reason}")]
606    Parse {
607        /// Location in the data where parsing failed.
608        location: String,
609        /// Description of the parse failure.
610        reason: String,
611    },
612
613    /// Failed to download a resource from the network.
614    ///
615    /// This error occurs when downloading descriptors or other data from
616    /// directory authorities or mirrors.
617    ///
618    /// # Fields
619    ///
620    /// - `url`: The URL that failed to download
621    /// - `reason`: Description of the failure
622    ///
623    /// # Recovery
624    ///
625    /// - Retry with exponential backoff
626    /// - Try a different directory authority or mirror
627    #[error("download failed: {url} - {reason}")]
628    Download {
629        /// The URL that failed to download.
630        url: String,
631        /// The reason for the download failure.
632        reason: String,
633    },
634
635    /// Download timed out before completing.
636    ///
637    /// The configured timeout was reached before the download completed.
638    ///
639    /// # Recovery
640    ///
641    /// - Increase the timeout value
642    /// - Try a different server
643    /// - Check network connectivity
644    #[error("download timeout: {url}")]
645    DownloadTimeout {
646        /// The URL that timed out.
647        url: String,
648    },
649
650    /// A general operation timeout occurred.
651    ///
652    /// The operation did not complete within the expected time.
653    ///
654    /// # Recovery
655    ///
656    /// - Increase timeout if configurable
657    /// - Check if Tor is responsive
658    /// - Retry the operation
659    #[error("timeout")]
660    Timeout,
661
662    /// The control socket was closed unexpectedly.
663    ///
664    /// This indicates the connection to Tor was lost. This can happen if
665    /// Tor exits, the network connection is interrupted, or the socket
666    /// is closed from the other end.
667    ///
668    /// # Recovery
669    ///
670    /// Reconnect to Tor and re-authenticate.
671    #[error("socket closed")]
672    SocketClosed,
673
674    /// The requested descriptor is not available.
675    ///
676    /// Tor doesn't have the requested descriptor cached and cannot
677    /// retrieve it.
678    ///
679    /// # Recovery
680    ///
681    /// - Wait and retry (descriptor may become available)
682    /// - Try downloading from a different source
683    #[error("descriptor unavailable: {0}")]
684    DescriptorUnavailable(String),
685
686    /// Failed to extend or create a circuit.
687    ///
688    /// The circuit could not be built through the requested relays.
689    ///
690    /// # Recovery
691    ///
692    /// - Try different relays
693    /// - Wait for network conditions to improve
694    /// - Check if the target relay is online
695    #[error("circuit extension failed: {0}")]
696    CircuitExtensionFailed(String),
697
698    /// Failed to parse a socket address.
699    ///
700    /// This error occurs when parsing a string into a socket address fails.
701    ///
702    /// # Recovery
703    ///
704    /// Verify the address format is correct (e.g., "127.0.0.1:9051").
705    #[error("address parse error: {0}")]
706    AddrParse(#[from] std::net::AddrParseError),
707}
708
709/// Authentication-specific errors.
710///
711/// These errors provide detailed information about why authentication
712/// with Tor's control port failed.
713///
714/// # Authentication Methods
715///
716/// Tor supports several authentication methods:
717///
718/// - **NONE**: No authentication required (open control port)
719/// - **PASSWORD**: Password-based authentication (HashedControlPassword)
720/// - **COOKIE**: Cookie file authentication (CookieAuthentication)
721/// - **SAFECOOKIE**: Challenge-response cookie authentication (recommended)
722///
723/// # Recovery Guide
724///
725/// | Error | Recovery Action |
726/// |-------|-----------------|
727/// | [`NoMethods`](AuthError::NoMethods) | Configure authentication in torrc |
728/// | [`IncorrectPassword`](AuthError::IncorrectPassword) | Verify password matches HashedControlPassword |
729/// | [`CookieUnreadable`](AuthError::CookieUnreadable) | Check file permissions and path |
730/// | [`IncorrectCookie`](AuthError::IncorrectCookie) | Cookie file may be stale; restart Tor |
731/// | [`ChallengeFailed`](AuthError::ChallengeFailed) | SAFECOOKIE protocol error; try COOKIE |
732/// | [`MissingPassword`](AuthError::MissingPassword) | Provide password for PASSWORD auth |
733///
734/// # Example
735///
736/// ```rust
737/// use stem_rs::AuthError;
738///
739/// fn handle_auth_error(err: AuthError) {
740///     match err {
741///         AuthError::IncorrectPassword => {
742///             eprintln!("Wrong password - check your torrc HashedControlPassword");
743///         }
744///         AuthError::CookieUnreadable(path) => {
745///             eprintln!("Cannot read cookie file: {}", path);
746///             eprintln!("Check file permissions and that Tor is running");
747///         }
748///         AuthError::NoMethods => {
749///             eprintln!("No compatible auth methods - configure torrc");
750///         }
751///         _ => eprintln!("Authentication error: {}", err),
752///     }
753/// }
754/// ```
755#[derive(Debug, Error)]
756pub enum AuthError {
757    /// No compatible authentication methods are available.
758    ///
759    /// Tor's PROTOCOLINFO response didn't include any authentication
760    /// methods that this library supports.
761    ///
762    /// # Recovery
763    ///
764    /// Configure at least one of: CookieAuthentication, HashedControlPassword,
765    /// or disable authentication entirely in torrc.
766    #[error("no authentication methods available")]
767    NoMethods,
768
769    /// The provided password was incorrect.
770    ///
771    /// PASSWORD authentication failed because the password doesn't match
772    /// the HashedControlPassword in torrc.
773    ///
774    /// # Recovery
775    ///
776    /// Verify the password matches what was used to generate HashedControlPassword.
777    /// Use `tor --hash-password` to generate a new hash if needed.
778    #[error("incorrect password")]
779    IncorrectPassword,
780
781    /// The cookie file could not be read.
782    ///
783    /// COOKIE or SAFECOOKIE authentication requires reading a cookie file,
784    /// but the file couldn't be accessed.
785    ///
786    /// # Recovery
787    ///
788    /// - Verify the cookie file path is correct
789    /// - Check file permissions (must be readable by your process)
790    /// - Ensure Tor is running (cookie file is created on startup)
791    #[error("cookie file unreadable: {0}")]
792    CookieUnreadable(String),
793
794    /// The cookie value was incorrect.
795    ///
796    /// The cookie file was read successfully, but Tor rejected the value.
797    /// This can happen if the cookie file is stale (from a previous Tor run).
798    ///
799    /// # Recovery
800    ///
801    /// Restart Tor to generate a fresh cookie file, then retry authentication.
802    #[error("incorrect cookie value")]
803    IncorrectCookie,
804
805    /// The cookie file has an incorrect size.
806    ///
807    /// Tor's cookie file should be exactly 32 bytes. A different size
808    /// indicates file corruption or an incorrect file.
809    ///
810    /// # Recovery
811    ///
812    /// Verify you're reading the correct cookie file. Restart Tor if needed.
813    #[error("incorrect cookie size")]
814    IncorrectCookieSize,
815
816    /// SAFECOOKIE challenge-response failed.
817    ///
818    /// The SAFECOOKIE authentication protocol failed during the
819    /// challenge-response exchange.
820    ///
821    /// # Recovery
822    ///
823    /// - Fall back to COOKIE authentication if available
824    /// - Verify the cookie file is current
825    /// - Check for network issues between client and Tor
826    #[error("safecookie challenge failed")]
827    ChallengeFailed,
828
829    /// SAFECOOKIE authentication is not supported.
830    ///
831    /// The Tor version doesn't support SAFECOOKIE, or it's disabled.
832    ///
833    /// # Recovery
834    ///
835    /// Use COOKIE or PASSWORD authentication instead.
836    #[error("safecookie challenge unsupported")]
837    ChallengeUnsupported,
838
839    /// A security check failed during authentication.
840    ///
841    /// This indicates a potential security issue, such as a mismatch
842    /// in expected vs. received authentication data.
843    ///
844    /// # Recovery
845    ///
846    /// This may indicate a man-in-the-middle attack. Verify your
847    /// connection to Tor is secure.
848    #[error("auth security failure")]
849    SecurityFailure,
850
851    /// PASSWORD authentication was requested but no password provided.
852    ///
853    /// The authenticate method was called without a password, but
854    /// PASSWORD is the only available authentication method.
855    ///
856    /// # Recovery
857    ///
858    /// Provide a password to the authenticate method.
859    #[error("missing password")]
860    MissingPassword,
861
862    /// Tor advertised unrecognized authentication methods.
863    ///
864    /// PROTOCOLINFO returned authentication methods this library
865    /// doesn't recognize. This may indicate a newer Tor version.
866    ///
867    /// # Recovery
868    ///
869    /// Update stem-rs to a newer version that supports these methods.
870    #[error("unrecognized auth methods: {0:?}")]
871    UnrecognizedMethods(Vec<String>),
872
873    /// Wrong socket type for the requested authentication.
874    ///
875    /// Some authentication methods are only valid for certain socket types
876    /// (e.g., Unix domain sockets vs. TCP sockets).
877    ///
878    /// # Recovery
879    ///
880    /// Use a different authentication method appropriate for your socket type.
881    #[error("incorrect socket type")]
882    IncorrectSocketType,
883}
884
885/// Logging severity levels for Tor events.
886///
887/// These levels correspond to Tor's internal logging runlevels and are used
888/// in log events received via the control protocol.
889///
890/// # Severity Order
891///
892/// From most to least severe: [`Err`](Runlevel::Err) > [`Warn`](Runlevel::Warn) >
893/// [`Notice`](Runlevel::Notice) > [`Info`](Runlevel::Info) > [`Debug`](Runlevel::Debug)
894///
895/// # Example
896///
897/// ```rust
898/// use stem_rs::Runlevel;
899///
900/// let level = Runlevel::Notice;
901/// println!("Log level: {}", level); // Prints "NOTICE"
902/// ```
903#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
904pub enum Runlevel {
905    /// Low-level runtime information for debugging.
906    ///
907    /// Very verbose output useful for development and troubleshooting.
908    Debug,
909    /// High-level runtime information.
910    ///
911    /// General operational information about Tor's activities.
912    Info,
913    /// Information that may be helpful to the user.
914    ///
915    /// Normal operational messages that users might want to see.
916    Notice,
917    /// Non-critical issues the user should be aware of.
918    ///
919    /// Problems that don't prevent operation but may need attention.
920    Warn,
921    /// Critical issues that impair Tor's ability to function.
922    ///
923    /// Serious errors that may prevent normal operation.
924    Err,
925}
926
927impl fmt::Display for Runlevel {
928    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
929        match self {
930            Runlevel::Debug => write!(f, "DEBUG"),
931            Runlevel::Info => write!(f, "INFO"),
932            Runlevel::Notice => write!(f, "NOTICE"),
933            Runlevel::Warn => write!(f, "WARN"),
934            Runlevel::Err => write!(f, "ERR"),
935        }
936    }
937}
938
939/// Signals that can be sent to the Tor process.
940///
941/// These signals control Tor's behavior and can be sent via
942/// [`controller::Controller::signal`].
943///
944/// # Signal Pairs
945///
946/// Some signals have Unix signal equivalents:
947/// - [`Reload`](Signal::Reload) / [`Hup`](Signal::Hup) - Reload configuration (SIGHUP)
948/// - [`Shutdown`](Signal::Shutdown) / [`Int`](Signal::Int) - Graceful shutdown (SIGINT)
949/// - [`Dump`](Signal::Dump) / [`Usr1`](Signal::Usr1) - Dump stats (SIGUSR1)
950/// - [`Debug`](Signal::Debug) / [`Usr2`](Signal::Usr2) - Debug logging (SIGUSR2)
951/// - [`Halt`](Signal::Halt) / [`Term`](Signal::Term) - Immediate exit (SIGTERM)
952///
953/// # Example
954///
955/// ```rust,no_run
956/// use stem_rs::{controller::Controller, Signal};
957///
958/// # async fn example() -> Result<(), stem_rs::Error> {
959/// # let mut controller = Controller::from_port("127.0.0.1:9051".parse().unwrap()).await?;
960/// // Request new circuits for privacy
961/// controller.signal(Signal::Newnym).await?;
962///
963/// // Clear DNS cache
964/// controller.signal(Signal::ClearDnsCache).await?;
965/// # Ok(())
966/// # }
967/// ```
968#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
969pub enum Signal {
970    /// Reload configuration files.
971    ///
972    /// Tor will reload torrc and apply changes that can be changed at runtime.
973    /// Equivalent to sending SIGHUP.
974    Reload,
975    /// Alias for [`Reload`](Signal::Reload).
976    ///
977    /// Unix SIGHUP signal equivalent.
978    Hup,
979    /// Controlled shutdown.
980    ///
981    /// Tor will close listeners and exit cleanly after current connections
982    /// complete, waiting ShutdownWaitLength if configured as a relay.
983    Shutdown,
984    /// Alias for [`Shutdown`](Signal::Shutdown).
985    ///
986    /// Unix SIGINT signal equivalent.
987    Int,
988    /// Dump information about open connections and circuits to the log.
989    ///
990    /// Useful for debugging connection issues.
991    Dump,
992    /// Alias for [`Dump`](Signal::Dump).
993    ///
994    /// Unix SIGUSR1 signal equivalent.
995    Usr1,
996    /// Switch logging to DEBUG level.
997    ///
998    /// Temporarily enables debug-level logging until the next RELOAD.
999    Debug,
1000    /// Alias for [`Debug`](Signal::Debug).
1001    ///
1002    /// Unix SIGUSR2 signal equivalent.
1003    Usr2,
1004    /// Immediate shutdown.
1005    ///
1006    /// Tor exits immediately without waiting for connections to close.
1007    Halt,
1008    /// Alias for [`Halt`](Signal::Halt).
1009    ///
1010    /// Unix SIGTERM signal equivalent.
1011    Term,
1012    /// Request new circuits for future connections.
1013    ///
1014    /// Clears the current circuit cache and builds new circuits.
1015    /// Also clears the DNS cache. Rate-limited to prevent abuse.
1016    /// Use this for privacy when you want to appear as a "new" user.
1017    Newnym,
1018    /// Clear cached DNS results.
1019    ///
1020    /// Forces Tor to re-resolve all hostnames on subsequent requests.
1021    ClearDnsCache,
1022    /// Trigger a heartbeat log message.
1023    ///
1024    /// Useful for monitoring that Tor is responsive.
1025    Heartbeat,
1026    /// Wake from dormant mode.
1027    ///
1028    /// Resumes normal operation if Tor was in dormant mode.
1029    /// Disables dormant mode.
1030    Active,
1031    /// Enter dormant mode.
1032    ///
1033    /// Reduces resource usage (CPU and network) when Tor is not actively needed.
1034    /// Tor will avoid building circuits and making network connections.
1035    Dormant,
1036}
1037
1038impl fmt::Display for Signal {
1039    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1040        match self {
1041            Signal::Reload => write!(f, "RELOAD"),
1042            Signal::Hup => write!(f, "HUP"),
1043            Signal::Shutdown => write!(f, "SHUTDOWN"),
1044            Signal::Int => write!(f, "INT"),
1045            Signal::Dump => write!(f, "DUMP"),
1046            Signal::Usr1 => write!(f, "USR1"),
1047            Signal::Debug => write!(f, "DEBUG"),
1048            Signal::Usr2 => write!(f, "USR2"),
1049            Signal::Halt => write!(f, "HALT"),
1050            Signal::Term => write!(f, "TERM"),
1051            Signal::Newnym => write!(f, "NEWNYM"),
1052            Signal::ClearDnsCache => write!(f, "CLEARDNSCACHE"),
1053            Signal::Heartbeat => write!(f, "HEARTBEAT"),
1054            Signal::Active => write!(f, "ACTIVE"),
1055            Signal::Dormant => write!(f, "DORMANT"),
1056        }
1057    }
1058}
1059
1060/// Flags assigned to Tor relays by directory authorities.
1061///
1062/// These flags indicate various characteristics of relays and are used
1063/// for path selection and relay classification.
1064///
1065/// # Flag Meanings
1066///
1067/// Flags are assigned based on relay behavior and capabilities:
1068/// - Performance flags: [`Fast`](Flag::Fast), [`Stable`](Flag::Stable)
1069/// - Role flags: [`Guard`](Flag::Guard), [`Exit`](Flag::Exit), [`Authority`](Flag::Authority)
1070/// - Status flags: [`Running`](Flag::Running), [`Valid`](Flag::Valid)
1071/// - Warning flags: [`BadExit`](Flag::BadExit), [`BadDirectory`](Flag::BadDirectory)
1072///
1073/// # Example
1074///
1075/// ```rust
1076/// use stem_rs::Flag;
1077///
1078/// let flag = Flag::Guard;
1079/// println!("Relay flag: {}", flag); // Prints "Guard"
1080/// ```
1081#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1082pub enum Flag {
1083    /// Relay is a directory authority.
1084    ///
1085    /// This relay is one of the trusted directory authorities that
1086    /// vote on the network consensus.
1087    Authority,
1088    /// Relay shouldn't be used as an exit due to being problematic or malicious.
1089    ///
1090    /// The relay has been flagged for bad behavior when used as an exit node.
1091    BadExit,
1092    /// Relay shouldn't be used for directory information.
1093    ///
1094    /// Note: This flag was removed from Tor but may appear in older descriptors.
1095    BadDirectory,
1096    /// Relay's exit policy makes it useful as an exit node.
1097    ///
1098    /// The relay allows exiting to a reasonable number of ports.
1099    Exit,
1100    /// Relay is suitable for high-bandwidth circuits.
1101    ///
1102    /// The relay has sufficient bandwidth for performance-sensitive traffic.
1103    Fast,
1104    /// Relay is suitable for being an entry guard (first hop).
1105    ///
1106    /// The relay is stable and fast enough to be used as a guard node.
1107    Guard,
1108    /// Relay is being used as a hidden service directory.
1109    ///
1110    /// The relay stores and serves hidden service descriptors.
1111    HsDir,
1112    /// Relay can be referred to by its nickname.
1113    ///
1114    /// The nickname is unique and verified.
1115    Named,
1116    /// Relay's Ed25519 key doesn't match the consensus.
1117    ///
1118    /// There's a mismatch in the relay's Ed25519 identity.
1119    NoEdConsensus,
1120    /// Relay is currently usable.
1121    ///
1122    /// The relay is online and responding to connections.
1123    Running,
1124    /// Relay is suitable for long-lived circuits.
1125    ///
1126    /// The relay has good uptime and is reliable for persistent connections.
1127    Stable,
1128    /// Relay descriptor is outdated and should be re-uploaded.
1129    ///
1130    /// The relay's descriptor is stale and needs to be refreshed.
1131    StaleDesc,
1132    /// Relay isn't currently bound to a nickname.
1133    ///
1134    /// The nickname is not verified or is shared with other relays.
1135    Unnamed,
1136    /// Relay supports the v2 directory protocol.
1137    ///
1138    /// The relay can serve directory information via the v2 protocol.
1139    V2Dir,
1140    /// Relay supports the v3 directory protocol.
1141    ///
1142    /// The relay can serve directory information via the v3 protocol.
1143    V3Dir,
1144    /// Relay has been validated.
1145    ///
1146    /// The relay's identity has been verified by the directory authorities.
1147    Valid,
1148}
1149
1150impl fmt::Display for Flag {
1151    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1152        match self {
1153            Flag::Authority => write!(f, "Authority"),
1154            Flag::BadExit => write!(f, "BadExit"),
1155            Flag::BadDirectory => write!(f, "BadDirectory"),
1156            Flag::Exit => write!(f, "Exit"),
1157            Flag::Fast => write!(f, "Fast"),
1158            Flag::Guard => write!(f, "Guard"),
1159            Flag::HsDir => write!(f, "HSDir"),
1160            Flag::Named => write!(f, "Named"),
1161            Flag::NoEdConsensus => write!(f, "NoEdConsensus"),
1162            Flag::Running => write!(f, "Running"),
1163            Flag::Stable => write!(f, "Stable"),
1164            Flag::StaleDesc => write!(f, "StaleDesc"),
1165            Flag::Unnamed => write!(f, "Unnamed"),
1166            Flag::V2Dir => write!(f, "V2Dir"),
1167            Flag::V3Dir => write!(f, "V3Dir"),
1168            Flag::Valid => write!(f, "Valid"),
1169        }
1170    }
1171}
1172
1173/// Status of a circuit in the Tor network.
1174///
1175/// Circuits progress through these states during their lifecycle.
1176/// Tor may provide statuses not in this enum.
1177///
1178/// # Circuit Lifecycle
1179///
1180/// ```text
1181/// LAUNCHED -> EXTENDED -> BUILT -> CLOSED
1182///     |          |
1183///     v          v
1184///   FAILED    FAILED
1185/// ```
1186///
1187/// # Example
1188///
1189/// ```rust
1190/// use stem_rs::CircStatus;
1191///
1192/// let status = CircStatus::Built;
1193/// println!("Circuit status: {}", status); // Prints "BUILT"
1194/// ```
1195#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1196pub enum CircStatus {
1197    /// New circuit was created.
1198    ///
1199    /// The circuit has been initiated but not yet extended to any relays.
1200    Launched,
1201    /// Circuit finished being created and can accept traffic.
1202    ///
1203    /// The circuit is fully built and ready for use.
1204    Built,
1205    /// Waiting to see if there's a circuit with a better guard.
1206    ///
1207    /// Tor is evaluating whether to use this circuit or wait for a better one.
1208    GuardWait,
1209    /// Circuit has been extended by a hop.
1210    ///
1211    /// The circuit is being built and has added another relay.
1212    Extended,
1213    /// Circuit construction failed.
1214    ///
1215    /// The circuit could not be completed. See [`CircClosureReason`] for details.
1216    Failed,
1217    /// Circuit has been closed.
1218    ///
1219    /// The circuit is no longer usable. See [`CircClosureReason`] for details.
1220    Closed,
1221}
1222
1223impl fmt::Display for CircStatus {
1224    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1225        match self {
1226            CircStatus::Launched => write!(f, "LAUNCHED"),
1227            CircStatus::Built => write!(f, "BUILT"),
1228            CircStatus::GuardWait => write!(f, "GUARD_WAIT"),
1229            CircStatus::Extended => write!(f, "EXTENDED"),
1230            CircStatus::Failed => write!(f, "FAILED"),
1231            CircStatus::Closed => write!(f, "CLOSED"),
1232        }
1233    }
1234}
1235
1236/// Attributes about how a circuit is built.
1237///
1238/// These flags describe special properties of circuit construction.
1239/// Introduced in Tor version 0.2.3.11.
1240///
1241/// # Example
1242///
1243/// ```rust
1244/// use stem_rs::CircBuildFlag;
1245///
1246/// let flag = CircBuildFlag::IsInternal;
1247/// println!("Build flag: {}", flag); // Prints "IS_INTERNAL"
1248/// ```
1249#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1250pub enum CircBuildFlag {
1251    /// Single hop circuit to fetch directory information.
1252    ///
1253    /// A one-hop tunnel used for directory fetches, not for user traffic.
1254    OneHopTunnel,
1255    /// Circuit that won't be used for client traffic.
1256    ///
1257    /// Internal circuits are used for Tor's own operations.
1258    IsInternal,
1259    /// Circuit only includes high capacity relays.
1260    ///
1261    /// Built for bandwidth-intensive operations.
1262    NeedCapacity,
1263    /// Circuit only includes relays with high uptime.
1264    ///
1265    /// Built for long-lived connections that need stability.
1266    NeedUptime,
1267}
1268
1269impl fmt::Display for CircBuildFlag {
1270    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1271        match self {
1272            CircBuildFlag::OneHopTunnel => write!(f, "ONEHOP_TUNNEL"),
1273            CircBuildFlag::IsInternal => write!(f, "IS_INTERNAL"),
1274            CircBuildFlag::NeedCapacity => write!(f, "NEED_CAPACITY"),
1275            CircBuildFlag::NeedUptime => write!(f, "NEED_UPTIME"),
1276        }
1277    }
1278}
1279
1280/// Purpose of a circuit.
1281///
1282/// Describes what a circuit is intended for. Introduced in Tor version 0.2.1.6.
1283///
1284/// # Example
1285///
1286/// ```rust
1287/// use stem_rs::CircPurpose;
1288///
1289/// let purpose = CircPurpose::General;
1290/// println!("Circuit purpose: {}", purpose); // Prints "GENERAL"
1291/// ```
1292#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1293pub enum CircPurpose {
1294    /// General client traffic or fetching directory information.
1295    ///
1296    /// Standard circuits used for normal Tor operations.
1297    General,
1298    /// Client-side introduction point for a hidden service circuit.
1299    ///
1300    /// Used when connecting to a hidden service's introduction point.
1301    HsClientIntro,
1302    /// Client-side hidden service rendezvous circuit.
1303    ///
1304    /// Used for the rendezvous connection when accessing a hidden service.
1305    HsClientRend,
1306    /// Server-side introduction point for a hidden service circuit.
1307    ///
1308    /// Used by hidden services to establish introduction points.
1309    HsServiceIntro,
1310    /// Server-side hidden service rendezvous circuit.
1311    ///
1312    /// Used by hidden services for rendezvous connections.
1313    HsServiceRend,
1314    /// Testing to see if we're reachable as a relay.
1315    ///
1316    /// Self-test circuits to verify relay reachability.
1317    Testing,
1318    /// Circuit that was built by a controller.
1319    ///
1320    /// Explicitly created via the control protocol.
1321    Controller,
1322    /// Circuit being kept around to measure timeout.
1323    ///
1324    /// Used for circuit build time measurement.
1325    MeasureTimeout,
1326    /// Constructed in advance for hidden service vanguards.
1327    ///
1328    /// Pre-built circuits for vanguard protection.
1329    HsVanguards,
1330    /// Probing if circuits are being maliciously closed.
1331    ///
1332    /// Used to detect path bias attacks.
1333    PathBiasTesting,
1334    /// Circuit is unused but remains open to disguise closure time.
1335    ///
1336    /// Padding circuits to prevent traffic analysis.
1337    CircuitPadding,
1338}
1339
1340impl fmt::Display for CircPurpose {
1341    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1342        match self {
1343            CircPurpose::General => write!(f, "GENERAL"),
1344            CircPurpose::HsClientIntro => write!(f, "HS_CLIENT_INTRO"),
1345            CircPurpose::HsClientRend => write!(f, "HS_CLIENT_REND"),
1346            CircPurpose::HsServiceIntro => write!(f, "HS_SERVICE_INTRO"),
1347            CircPurpose::HsServiceRend => write!(f, "HS_SERVICE_REND"),
1348            CircPurpose::Testing => write!(f, "TESTING"),
1349            CircPurpose::Controller => write!(f, "CONTROLLER"),
1350            CircPurpose::MeasureTimeout => write!(f, "MEASURE_TIMEOUT"),
1351            CircPurpose::HsVanguards => write!(f, "HS_VANGUARDS"),
1352            CircPurpose::PathBiasTesting => write!(f, "PATH_BIAS_TESTING"),
1353            CircPurpose::CircuitPadding => write!(f, "CIRCUIT_PADDING"),
1354        }
1355    }
1356}
1357
1358/// Reason that a circuit is being closed or failed to be established.
1359///
1360/// Provides detailed information about why a circuit ended.
1361///
1362/// # Example
1363///
1364/// ```rust
1365/// use stem_rs::CircClosureReason;
1366///
1367/// let reason = CircClosureReason::Timeout;
1368/// println!("Closure reason: {}", reason); // Prints "TIMEOUT"
1369/// ```
1370#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1371pub enum CircClosureReason {
1372    /// No reason given.
1373    None,
1374    /// Violation in the Tor protocol.
1375    ///
1376    /// A relay sent malformed or unexpected data.
1377    TorProtocol,
1378    /// Internal error.
1379    ///
1380    /// An internal error occurred in Tor.
1381    Internal,
1382    /// Requested by the client via a TRUNCATE command.
1383    ///
1384    /// The circuit was explicitly closed by the client.
1385    Requested,
1386    /// Relay is currently hibernating.
1387    ///
1388    /// The relay is in low-power mode and not accepting circuits.
1389    Hibernating,
1390    /// Relay is out of memory, sockets, or circuit IDs.
1391    ///
1392    /// The relay has exhausted resources.
1393    ResourceLimit,
1394    /// Unable to contact the relay.
1395    ///
1396    /// Network connectivity issue to the next hop.
1397    ConnectFailed,
1398    /// Relay had the wrong OR identification.
1399    ///
1400    /// The relay's identity key didn't match what was expected.
1401    OrIdentity,
1402    /// Connection failed after being established.
1403    ///
1404    /// The OR connection was closed unexpectedly.
1405    OrConnClosed,
1406    /// Circuit has expired.
1407    ///
1408    /// The circuit exceeded MaxCircuitDirtiness lifetime.
1409    Finished,
1410    /// Circuit construction timed out.
1411    ///
1412    /// The circuit took too long to build.
1413    Timeout,
1414    /// Circuit unexpectedly closed.
1415    ///
1416    /// The circuit was destroyed by a relay.
1417    Destroyed,
1418    /// Not enough relays to make a circuit.
1419    ///
1420    /// Insufficient relays available for path selection.
1421    NoPath,
1422    /// Requested hidden service does not exist.
1423    ///
1424    /// The onion address is invalid or the service is offline.
1425    NoSuchService,
1426    /// Same as Timeout but left open for measurement.
1427    ///
1428    /// Circuit timed out but was kept for build time measurement.
1429    MeasurementExpired,
1430    /// Introduction point is redundant with another circuit.
1431    ///
1432    /// Another circuit already serves this introduction point.
1433    IpNowRedundant,
1434}
1435
1436impl fmt::Display for CircClosureReason {
1437    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1438        match self {
1439            CircClosureReason::None => write!(f, "NONE"),
1440            CircClosureReason::TorProtocol => write!(f, "TORPROTOCOL"),
1441            CircClosureReason::Internal => write!(f, "INTERNAL"),
1442            CircClosureReason::Requested => write!(f, "REQUESTED"),
1443            CircClosureReason::Hibernating => write!(f, "HIBERNATING"),
1444            CircClosureReason::ResourceLimit => write!(f, "RESOURCELIMIT"),
1445            CircClosureReason::ConnectFailed => write!(f, "CONNECTFAILED"),
1446            CircClosureReason::OrIdentity => write!(f, "OR_IDENTITY"),
1447            CircClosureReason::OrConnClosed => write!(f, "OR_CONN_CLOSED"),
1448            CircClosureReason::Finished => write!(f, "FINISHED"),
1449            CircClosureReason::Timeout => write!(f, "TIMEOUT"),
1450            CircClosureReason::Destroyed => write!(f, "DESTROYED"),
1451            CircClosureReason::NoPath => write!(f, "NOPATH"),
1452            CircClosureReason::NoSuchService => write!(f, "NOSUCHSERVICE"),
1453            CircClosureReason::MeasurementExpired => write!(f, "MEASUREMENT_EXPIRED"),
1454            CircClosureReason::IpNowRedundant => write!(f, "IP_NOW_REDUNDANT"),
1455        }
1456    }
1457}
1458
1459/// Type of change reflected in a circuit by a CIRC_MINOR event.
1460///
1461/// These events indicate minor changes to circuits that don't affect
1462/// their overall status.
1463///
1464/// # Example
1465///
1466/// ```rust
1467/// use stem_rs::CircEvent;
1468///
1469/// let event = CircEvent::PurposeChanged;
1470/// println!("Circuit event: {}", event); // Prints "PURPOSE_CHANGED"
1471/// ```
1472#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1473pub enum CircEvent {
1474    /// Circuit purpose or hidden service state has changed.
1475    ///
1476    /// The circuit's intended use has been modified.
1477    PurposeChanged,
1478    /// Circuit connections are being reused for a different circuit.
1479    ///
1480    /// An existing circuit is being repurposed.
1481    Cannibalized,
1482}
1483
1484impl fmt::Display for CircEvent {
1485    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1486        match self {
1487            CircEvent::PurposeChanged => write!(f, "PURPOSE_CHANGED"),
1488            CircEvent::Cannibalized => write!(f, "CANNIBALIZED"),
1489        }
1490    }
1491}
1492
1493/// State of a hidden service circuit.
1494///
1495/// These states track the progress of hidden service connections.
1496/// Introduced in Tor version 0.2.3.11.
1497///
1498/// # State Prefixes
1499///
1500/// - `HSCI_*` - Client-side introduction point
1501/// - `HSCR_*` - Client-side rendezvous point
1502/// - `HSSI_*` - Service-side introduction point
1503/// - `HSSR_*` - Service-side rendezvous point
1504///
1505/// # Example
1506///
1507/// ```rust
1508/// use stem_rs::HiddenServiceState;
1509///
1510/// let state = HiddenServiceState::HscrJoined;
1511/// println!("HS state: {}", state); // Prints "HSCR_JOINED"
1512/// ```
1513#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1514pub enum HiddenServiceState {
1515    /// Client connecting to the introduction point.
1516    HsciConnecting,
1517    /// Client sent INTRODUCE1 and awaiting reply.
1518    HsciIntroSent,
1519    /// Client received reply, circuit is closing.
1520    HsciDone,
1521    /// Client connecting to rendezvous point.
1522    HscrConnecting,
1523    /// Rendezvous point established, awaiting introduction.
1524    HscrEstablishedIdle,
1525    /// Introduction received, awaiting rendezvous.
1526    HscrEstablishedWaiting,
1527    /// Client connected to the hidden service.
1528    HscrJoined,
1529    /// Service connecting to introduction point.
1530    HssiConnecting,
1531    /// Service established introduction point.
1532    HssiEstablished,
1533    /// Service connecting to rendezvous point.
1534    HssrConnecting,
1535    /// Service connected to rendezvous point.
1536    HssrJoined,
1537}
1538
1539impl fmt::Display for HiddenServiceState {
1540    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1541        match self {
1542            HiddenServiceState::HsciConnecting => write!(f, "HSCI_CONNECTING"),
1543            HiddenServiceState::HsciIntroSent => write!(f, "HSCI_INTRO_SENT"),
1544            HiddenServiceState::HsciDone => write!(f, "HSCI_DONE"),
1545            HiddenServiceState::HscrConnecting => write!(f, "HSCR_CONNECTING"),
1546            HiddenServiceState::HscrEstablishedIdle => write!(f, "HSCR_ESTABLISHED_IDLE"),
1547            HiddenServiceState::HscrEstablishedWaiting => write!(f, "HSCR_ESTABLISHED_WAITING"),
1548            HiddenServiceState::HscrJoined => write!(f, "HSCR_JOINED"),
1549            HiddenServiceState::HssiConnecting => write!(f, "HSSI_CONNECTING"),
1550            HiddenServiceState::HssiEstablished => write!(f, "HSSI_ESTABLISHED"),
1551            HiddenServiceState::HssrConnecting => write!(f, "HSSR_CONNECTING"),
1552            HiddenServiceState::HssrJoined => write!(f, "HSSR_JOINED"),
1553        }
1554    }
1555}
1556
1557/// Status of a stream going through Tor.
1558///
1559/// Streams represent individual TCP connections tunneled through circuits.
1560///
1561/// # Example
1562///
1563/// ```rust
1564/// use stem_rs::StreamStatus;
1565///
1566/// let status = StreamStatus::Succeeded;
1567/// println!("Stream status: {}", status); // Prints "SUCCEEDED"
1568/// ```
1569#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1570pub enum StreamStatus {
1571    /// Request for a new connection.
1572    New,
1573    /// Request to resolve an address.
1574    NewResolve,
1575    /// Address is being re-mapped to another.
1576    Remap,
1577    /// Sent a connect cell along a circuit.
1578    SentConnect,
1579    /// Sent a resolve cell along a circuit.
1580    SentResolve,
1581    /// Stream has been established.
1582    Succeeded,
1583    /// Stream is detached and won't be re-established.
1584    Failed,
1585    /// Stream is detached but might be re-established.
1586    Detached,
1587    /// Awaiting a controller's ATTACHSTREAM request.
1588    ControllerWait,
1589    /// Stream has closed.
1590    Closed,
1591}
1592
1593impl fmt::Display for StreamStatus {
1594    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1595        match self {
1596            StreamStatus::New => write!(f, "NEW"),
1597            StreamStatus::NewResolve => write!(f, "NEWRESOLVE"),
1598            StreamStatus::Remap => write!(f, "REMAP"),
1599            StreamStatus::SentConnect => write!(f, "SENTCONNECT"),
1600            StreamStatus::SentResolve => write!(f, "SENTRESOLVE"),
1601            StreamStatus::Succeeded => write!(f, "SUCCEEDED"),
1602            StreamStatus::Failed => write!(f, "FAILED"),
1603            StreamStatus::Detached => write!(f, "DETACHED"),
1604            StreamStatus::ControllerWait => write!(f, "CONTROLLER_WAIT"),
1605            StreamStatus::Closed => write!(f, "CLOSED"),
1606        }
1607    }
1608}
1609
1610/// Reason that a stream is being closed or failed to be established.
1611///
1612/// Provides detailed information about why a stream ended.
1613///
1614/// # Example
1615///
1616/// ```rust
1617/// use stem_rs::StreamClosureReason;
1618///
1619/// let reason = StreamClosureReason::Done;
1620/// println!("Closure reason: {}", reason); // Prints "DONE"
1621/// ```
1622#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1623pub enum StreamClosureReason {
1624    /// None of the other reasons apply.
1625    Misc,
1626    /// Unable to resolve the hostname.
1627    ResolveFailed,
1628    /// Remote host refused the connection.
1629    ConnectRefused,
1630    /// OR refuses to connect due to exit policy.
1631    ExitPolicy,
1632    /// Circuit is being shut down.
1633    Destroy,
1634    /// Connection has been closed normally.
1635    Done,
1636    /// Connection timed out.
1637    Timeout,
1638    /// Routing error while contacting the destination.
1639    NoRoute,
1640    /// Relay is temporarily hibernating.
1641    Hibernating,
1642    /// Internal error at the relay.
1643    Internal,
1644    /// Relay has insufficient resources.
1645    ResourceLimit,
1646    /// Connection was unexpectedly reset.
1647    ConnReset,
1648    /// Violation in the Tor protocol.
1649    TorProtocol,
1650    /// Directory info requested from non-directory relay.
1651    NotDirectory,
1652    /// Endpoint has sent a RELAY_END cell.
1653    End,
1654    /// Endpoint was a private address (127.0.0.1, 10.0.0.1, etc).
1655    PrivateAddr,
1656}
1657
1658impl fmt::Display for StreamClosureReason {
1659    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1660        match self {
1661            StreamClosureReason::Misc => write!(f, "MISC"),
1662            StreamClosureReason::ResolveFailed => write!(f, "RESOLVEFAILED"),
1663            StreamClosureReason::ConnectRefused => write!(f, "CONNECTREFUSED"),
1664            StreamClosureReason::ExitPolicy => write!(f, "EXITPOLICY"),
1665            StreamClosureReason::Destroy => write!(f, "DESTROY"),
1666            StreamClosureReason::Done => write!(f, "DONE"),
1667            StreamClosureReason::Timeout => write!(f, "TIMEOUT"),
1668            StreamClosureReason::NoRoute => write!(f, "NOROUTE"),
1669            StreamClosureReason::Hibernating => write!(f, "HIBERNATING"),
1670            StreamClosureReason::Internal => write!(f, "INTERNAL"),
1671            StreamClosureReason::ResourceLimit => write!(f, "RESOURCELIMIT"),
1672            StreamClosureReason::ConnReset => write!(f, "CONNRESET"),
1673            StreamClosureReason::TorProtocol => write!(f, "TORPROTOCOL"),
1674            StreamClosureReason::NotDirectory => write!(f, "NOTDIRECTORY"),
1675            StreamClosureReason::End => write!(f, "END"),
1676            StreamClosureReason::PrivateAddr => write!(f, "PRIVATE_ADDR"),
1677        }
1678    }
1679}
1680
1681/// Cause of a stream being remapped to another address.
1682///
1683/// # Example
1684///
1685/// ```rust
1686/// use stem_rs::StreamSource;
1687///
1688/// let source = StreamSource::Cache;
1689/// println!("Stream source: {}", source); // Prints "CACHE"
1690/// ```
1691#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1692pub enum StreamSource {
1693    /// Tor is remapping because of a cached answer.
1694    Cache,
1695    /// Exit relay requested the remap.
1696    Exit,
1697}
1698
1699impl fmt::Display for StreamSource {
1700    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1701        match self {
1702            StreamSource::Cache => write!(f, "CACHE"),
1703            StreamSource::Exit => write!(f, "EXIT"),
1704        }
1705    }
1706}
1707
1708/// Purpose of a stream.
1709///
1710/// Describes what the stream is being used for. Only provided with new streams.
1711///
1712/// # Example
1713///
1714/// ```rust
1715/// use stem_rs::StreamPurpose;
1716///
1717/// let purpose = StreamPurpose::User;
1718/// println!("Stream purpose: {}", purpose); // Prints "USER"
1719/// ```
1720#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1721pub enum StreamPurpose {
1722    /// Fetching directory information (descriptors, consensus, etc).
1723    DirFetch,
1724    /// Uploading our descriptor to an authority.
1725    DirUpload,
1726    /// User initiated DNS request.
1727    DnsRequest,
1728    /// Checking that our directory port is reachable externally.
1729    DirportTest,
1730    /// Either relaying user traffic or not one of the above categories.
1731    User,
1732}
1733
1734impl fmt::Display for StreamPurpose {
1735    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1736        match self {
1737            StreamPurpose::DirFetch => write!(f, "DIR_FETCH"),
1738            StreamPurpose::DirUpload => write!(f, "DIR_UPLOAD"),
1739            StreamPurpose::DnsRequest => write!(f, "DNS_REQUEST"),
1740            StreamPurpose::DirportTest => write!(f, "DIRPORT_TEST"),
1741            StreamPurpose::User => write!(f, "USER"),
1742        }
1743    }
1744}
1745
1746/// Status of an OR (Onion Router) connection.
1747///
1748/// OR connections are the TLS connections between Tor relays.
1749///
1750/// # Example
1751///
1752/// ```rust
1753/// use stem_rs::OrStatus;
1754///
1755/// let status = OrStatus::Connected;
1756/// println!("OR status: {}", status); // Prints "CONNECTED"
1757/// ```
1758#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1759pub enum OrStatus {
1760    /// Received OR connection, starting server-side handshake.
1761    New,
1762    /// Launched outbound OR connection, starting client-side handshake.
1763    Launched,
1764    /// OR connection has been established.
1765    Connected,
1766    /// Attempt to establish OR connection failed.
1767    Failed,
1768    /// OR connection has been closed.
1769    Closed,
1770}
1771
1772impl fmt::Display for OrStatus {
1773    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1774        match self {
1775            OrStatus::New => write!(f, "NEW"),
1776            OrStatus::Launched => write!(f, "LAUNCHED"),
1777            OrStatus::Connected => write!(f, "CONNECTED"),
1778            OrStatus::Failed => write!(f, "FAILED"),
1779            OrStatus::Closed => write!(f, "CLOSED"),
1780        }
1781    }
1782}
1783
1784/// Reason that an OR connection is being closed or failed.
1785///
1786/// # Example
1787///
1788/// ```rust
1789/// use stem_rs::OrClosureReason;
1790///
1791/// let reason = OrClosureReason::Done;
1792/// println!("OR closure reason: {}", reason); // Prints "DONE"
1793/// ```
1794#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1795pub enum OrClosureReason {
1796    /// OR connection shut down cleanly.
1797    Done,
1798    /// Got ECONNREFUSED when connecting to the relay.
1799    ConnectRefused,
1800    /// Identity of the relay wasn't what we expected.
1801    Identity,
1802    /// Got ECONNRESET or similar error from relay.
1803    ConnectReset,
1804    /// Got ETIMEOUT or similar error from relay.
1805    Timeout,
1806    /// Got ENOTCONN, ENETUNREACH, ENETDOWN, EHOSTUNREACH, or similar.
1807    NoRoute,
1808    /// Got a different kind of I/O error from relay.
1809    IoError,
1810    /// Relay has insufficient resources.
1811    ResourceLimit,
1812    /// Connection refused for another reason.
1813    Misc,
1814    /// No pluggable transport was available.
1815    PtMissing,
1816}
1817
1818impl fmt::Display for OrClosureReason {
1819    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1820        match self {
1821            OrClosureReason::Done => write!(f, "DONE"),
1822            OrClosureReason::ConnectRefused => write!(f, "CONNECTREFUSED"),
1823            OrClosureReason::Identity => write!(f, "IDENTITY"),
1824            OrClosureReason::ConnectReset => write!(f, "CONNECTRESET"),
1825            OrClosureReason::Timeout => write!(f, "TIMEOUT"),
1826            OrClosureReason::NoRoute => write!(f, "NOROUTE"),
1827            OrClosureReason::IoError => write!(f, "IOERROR"),
1828            OrClosureReason::ResourceLimit => write!(f, "RESOURCELIMIT"),
1829            OrClosureReason::Misc => write!(f, "MISC"),
1830            OrClosureReason::PtMissing => write!(f, "PT_MISSING"),
1831        }
1832    }
1833}
1834
1835/// Type of guard relay usage.
1836///
1837/// # Example
1838///
1839/// ```rust
1840/// use stem_rs::GuardType;
1841///
1842/// let guard_type = GuardType::Entry;
1843/// println!("Guard type: {}", guard_type); // Prints "ENTRY"
1844/// ```
1845#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1846pub enum GuardType {
1847    /// Used to connect to the Tor network (entry guard).
1848    Entry,
1849}
1850
1851impl fmt::Display for GuardType {
1852    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1853        match self {
1854            GuardType::Entry => write!(f, "ENTRY"),
1855        }
1856    }
1857}
1858
1859/// Status of a guard relay.
1860///
1861/// # Example
1862///
1863/// ```rust
1864/// use stem_rs::GuardStatus;
1865///
1866/// let status = GuardStatus::Up;
1867/// println!("Guard status: {}", status); // Prints "UP"
1868/// ```
1869#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1870pub enum GuardStatus {
1871    /// New guard that we weren't previously using.
1872    New,
1873    /// Removed from use as one of our guards.
1874    Dropped,
1875    /// Guard is now reachable.
1876    Up,
1877    /// Guard is now unreachable.
1878    Down,
1879    /// Consensus or relay considers this relay unusable as a guard.
1880    Bad,
1881    /// Consensus or relay considers this relay usable as a guard.
1882    Good,
1883}
1884
1885impl fmt::Display for GuardStatus {
1886    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1887        match self {
1888            GuardStatus::New => write!(f, "NEW"),
1889            GuardStatus::Dropped => write!(f, "DROPPED"),
1890            GuardStatus::Up => write!(f, "UP"),
1891            GuardStatus::Down => write!(f, "DOWN"),
1892            GuardStatus::Bad => write!(f, "BAD"),
1893            GuardStatus::Good => write!(f, "GOOD"),
1894        }
1895    }
1896}
1897
1898/// Way in which the timeout value of a circuit is changing.
1899///
1900/// # Example
1901///
1902/// ```rust
1903/// use stem_rs::TimeoutSetType;
1904///
1905/// let timeout_type = TimeoutSetType::Computed;
1906/// println!("Timeout type: {}", timeout_type); // Prints "COMPUTED"
1907/// ```
1908#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1909pub enum TimeoutSetType {
1910    /// Tor has computed a new timeout based on prior circuits.
1911    Computed,
1912    /// Timeout reverted to its default.
1913    Reset,
1914    /// Timeout reverted to default until network connectivity recovers.
1915    Suspended,
1916    /// Throwing out timeout value from when the network was down.
1917    Discard,
1918    /// Resumed calculations to determine the proper timeout.
1919    Resume,
1920}
1921
1922impl fmt::Display for TimeoutSetType {
1923    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1924        match self {
1925            TimeoutSetType::Computed => write!(f, "COMPUTED"),
1926            TimeoutSetType::Reset => write!(f, "RESET"),
1927            TimeoutSetType::Suspended => write!(f, "SUSPENDED"),
1928            TimeoutSetType::Discard => write!(f, "DISCARD"),
1929            TimeoutSetType::Resume => write!(f, "RESUME"),
1930        }
1931    }
1932}
1933
1934/// Action being taken in a HS_DESC event.
1935///
1936/// # Example
1937///
1938/// ```rust
1939/// use stem_rs::HsDescAction;
1940///
1941/// let action = HsDescAction::Received;
1942/// println!("HS_DESC action: {}", action); // Prints "RECEIVED"
1943/// ```
1944#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1945pub enum HsDescAction {
1946    /// Uncached hidden service descriptor is being requested.
1947    Requested,
1948    /// Descriptor is being uploaded with HSPOST.
1949    Upload,
1950    /// Hidden service descriptor has been retrieved.
1951    Received,
1952    /// Descriptor was uploaded with HSPOST.
1953    Uploaded,
1954    /// Fetched descriptor was ignored (already have v0 descriptor).
1955    Ignore,
1956    /// We were unable to retrieve the descriptor.
1957    Failed,
1958    /// Hidden service descriptor was just created.
1959    Created,
1960}
1961
1962impl fmt::Display for HsDescAction {
1963    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1964        match self {
1965            HsDescAction::Requested => write!(f, "REQUESTED"),
1966            HsDescAction::Upload => write!(f, "UPLOAD"),
1967            HsDescAction::Received => write!(f, "RECEIVED"),
1968            HsDescAction::Uploaded => write!(f, "UPLOADED"),
1969            HsDescAction::Ignore => write!(f, "IGNORE"),
1970            HsDescAction::Failed => write!(f, "FAILED"),
1971            HsDescAction::Created => write!(f, "CREATED"),
1972        }
1973    }
1974}
1975
1976/// Reason for a hidden service descriptor fetch to fail.
1977///
1978/// # Example
1979///
1980/// ```rust
1981/// use stem_rs::HsDescReason;
1982///
1983/// let reason = HsDescReason::NotFound;
1984/// println!("HS_DESC reason: {}", reason); // Prints "NOT_FOUND"
1985/// ```
1986#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1987pub enum HsDescReason {
1988    /// Descriptor was unparseable.
1989    BadDesc,
1990    /// Hidden service directory refused to provide the descriptor.
1991    QueryRejected,
1992    /// Descriptor was rejected by the hidden service directory.
1993    UploadRejected,
1994    /// Descriptor with the given identifier wasn't found.
1995    NotFound,
1996    /// No hidden service directory was found.
1997    QueryNoHsDir,
1998    /// Request was throttled (rate limited).
1999    QueryRateLimited,
2000    /// Failure type is unknown.
2001    Unexpected,
2002}
2003
2004impl fmt::Display for HsDescReason {
2005    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2006        match self {
2007            HsDescReason::BadDesc => write!(f, "BAD_DESC"),
2008            HsDescReason::QueryRejected => write!(f, "QUERY_REJECTED"),
2009            HsDescReason::UploadRejected => write!(f, "UPLOAD_REJECTED"),
2010            HsDescReason::NotFound => write!(f, "NOT_FOUND"),
2011            HsDescReason::QueryNoHsDir => write!(f, "QUERY_NO_HSDIR"),
2012            HsDescReason::QueryRateLimited => write!(f, "QUERY_RATE_LIMITED"),
2013            HsDescReason::Unexpected => write!(f, "UNEXPECTED"),
2014        }
2015    }
2016}
2017
2018/// Type of authentication for a HS_DESC event.
2019///
2020/// # Example
2021///
2022/// ```rust
2023/// use stem_rs::HsAuth;
2024///
2025/// let auth = HsAuth::NoAuth;
2026/// println!("HS auth: {}", auth); // Prints "NO_AUTH"
2027/// ```
2028#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2029pub enum HsAuth {
2030    /// No authentication required.
2031    NoAuth,
2032    /// General hidden service authentication.
2033    BasicAuth,
2034    /// Authentication that hides service activity from unauthorized clients.
2035    StealthAuth,
2036    /// Unrecognized method of authentication.
2037    Unknown,
2038}
2039
2040impl fmt::Display for HsAuth {
2041    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2042        match self {
2043            HsAuth::NoAuth => write!(f, "NO_AUTH"),
2044            HsAuth::BasicAuth => write!(f, "BASIC_AUTH"),
2045            HsAuth::StealthAuth => write!(f, "STEALTH_AUTH"),
2046            HsAuth::Unknown => write!(f, "UNKNOWN"),
2047        }
2048    }
2049}
2050
2051/// Types of events that can be subscribed to via the control protocol.
2052///
2053/// Use with [`controller::Controller::set_events`] to subscribe to events.
2054///
2055/// # Example
2056///
2057/// ```rust
2058/// use stem_rs::EventType;
2059///
2060/// let event = EventType::Circ;
2061/// println!("Event type: {}", event); // Prints "CIRC"
2062/// ```
2063#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2064pub enum EventType {
2065    /// Circuit status changed.
2066    Circ,
2067    /// Stream status changed.
2068    Stream,
2069    /// OR connection status changed.
2070    OrConn,
2071    /// Bandwidth used in the last second.
2072    Bw,
2073    /// Debug-level log message.
2074    Debug,
2075    /// Info-level log message.
2076    Info,
2077    /// Notice-level log message.
2078    Notice,
2079    /// Warning-level log message.
2080    Warn,
2081    /// Error-level log message.
2082    Err,
2083    /// New descriptors available.
2084    NewDesc,
2085    /// Address mapping changed.
2086    AddrMap,
2087    /// New descriptors uploaded to us as an authority.
2088    AuthDir,
2089    /// Our descriptor changed.
2090    DescChanged,
2091    /// General status event.
2092    Status,
2093    /// Guard status changed.
2094    Guard,
2095    /// Network status changed.
2096    Ns,
2097    /// Per-stream bandwidth.
2098    StreamBw,
2099    /// Periodic client summary (bridge/relay only).
2100    ClientsSeen,
2101    /// New consensus available.
2102    NewConsensus,
2103    /// Circuit build timeout changed.
2104    BuildTimeoutSet,
2105    /// Signal received.
2106    Signal,
2107    /// Configuration changed.
2108    ConfChanged,
2109    /// Minor circuit event.
2110    CircMinor,
2111    /// Pluggable transport launched.
2112    TransportLaunched,
2113    /// Per-connection bandwidth.
2114    ConnBw,
2115    /// Per-circuit bandwidth.
2116    CircBw,
2117    /// Cell statistics.
2118    CellStats,
2119    /// Hidden service descriptor event.
2120    HsDesc,
2121    /// Hidden service descriptor content.
2122    HsDescContent,
2123    /// Network liveness changed.
2124    NetworkLiveness,
2125    /// Pluggable transport log message.
2126    PtLog,
2127    /// Pluggable transport status.
2128    PtStatus,
2129}
2130
2131impl fmt::Display for EventType {
2132    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2133        match self {
2134            EventType::Circ => write!(f, "CIRC"),
2135            EventType::Stream => write!(f, "STREAM"),
2136            EventType::OrConn => write!(f, "ORCONN"),
2137            EventType::Bw => write!(f, "BW"),
2138            EventType::Debug => write!(f, "DEBUG"),
2139            EventType::Info => write!(f, "INFO"),
2140            EventType::Notice => write!(f, "NOTICE"),
2141            EventType::Warn => write!(f, "WARN"),
2142            EventType::Err => write!(f, "ERR"),
2143            EventType::NewDesc => write!(f, "NEWDESC"),
2144            EventType::AddrMap => write!(f, "ADDRMAP"),
2145            EventType::AuthDir => write!(f, "AUTHDIR_NEWDESCS"),
2146            EventType::DescChanged => write!(f, "DESCCHANGED"),
2147            EventType::Status => write!(f, "STATUS_GENERAL"),
2148            EventType::Guard => write!(f, "GUARD"),
2149            EventType::Ns => write!(f, "NS"),
2150            EventType::StreamBw => write!(f, "STREAM_BW"),
2151            EventType::ClientsSeen => write!(f, "CLIENTS_SEEN"),
2152            EventType::NewConsensus => write!(f, "NEWCONSENSUS"),
2153            EventType::BuildTimeoutSet => write!(f, "BUILDTIMEOUT_SET"),
2154            EventType::Signal => write!(f, "SIGNAL"),
2155            EventType::ConfChanged => write!(f, "CONF_CHANGED"),
2156            EventType::CircMinor => write!(f, "CIRC_MINOR"),
2157            EventType::TransportLaunched => write!(f, "TRANSPORT_LAUNCHED"),
2158            EventType::ConnBw => write!(f, "CONN_BW"),
2159            EventType::CircBw => write!(f, "CIRC_BW"),
2160            EventType::CellStats => write!(f, "CELL_STATS"),
2161            EventType::HsDesc => write!(f, "HS_DESC"),
2162            EventType::HsDescContent => write!(f, "HS_DESC_CONTENT"),
2163            EventType::NetworkLiveness => write!(f, "NETWORK_LIVENESS"),
2164            EventType::PtLog => write!(f, "PT_LOG"),
2165            EventType::PtStatus => write!(f, "PT_STATUS"),
2166        }
2167    }
2168}
2169
2170/// Source of a status event.
2171///
2172/// # Example
2173///
2174/// ```rust
2175/// use stem_rs::StatusType;
2176///
2177/// let status = StatusType::General;
2178/// println!("Status type: {}", status); // Prints "GENERAL"
2179/// ```
2180#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2181pub enum StatusType {
2182    /// General Tor activity, not specifically as a client or relay.
2183    General,
2184    /// Related to our activity as a Tor client.
2185    Client,
2186    /// Related to our activity as a Tor relay.
2187    Server,
2188}
2189
2190impl fmt::Display for StatusType {
2191    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2192        match self {
2193            StatusType::General => write!(f, "GENERAL"),
2194            StatusType::Client => write!(f, "CLIENT"),
2195            StatusType::Server => write!(f, "SERVER"),
2196        }
2197    }
2198}
2199
2200/// Purpose for a Tor connection.
2201///
2202/// # Example
2203///
2204/// ```rust
2205/// use stem_rs::ConnectionType;
2206///
2207/// let conn_type = ConnectionType::Or;
2208/// println!("Connection type: {}", conn_type); // Prints "OR"
2209/// ```
2210#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2211pub enum ConnectionType {
2212    /// Carrying traffic within the Tor network.
2213    Or,
2214    /// Fetching or sending Tor descriptor data.
2215    Dir,
2216    /// Carrying traffic between Tor network and external destination.
2217    Exit,
2218}
2219
2220impl fmt::Display for ConnectionType {
2221    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2222        match self {
2223            ConnectionType::Or => write!(f, "OR"),
2224            ConnectionType::Dir => write!(f, "DIR"),
2225            ConnectionType::Exit => write!(f, "EXIT"),
2226        }
2227    }
2228}
2229
2230/// Bucket categories for TB_EMPTY events.
2231///
2232/// Token buckets are used for rate limiting in Tor.
2233///
2234/// # Example
2235///
2236/// ```rust
2237/// use stem_rs::TokenBucket;
2238///
2239/// let bucket = TokenBucket::Global;
2240/// println!("Token bucket: {}", bucket); // Prints "GLOBAL"
2241/// ```
2242#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2243pub enum TokenBucket {
2244    /// Global token bucket for overall bandwidth.
2245    Global,
2246    /// Relay token bucket for relay traffic.
2247    Relay,
2248    /// Bucket used for OR connections.
2249    OrConn,
2250}
2251
2252impl fmt::Display for TokenBucket {
2253    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2254        match self {
2255            TokenBucket::Global => write!(f, "GLOBAL"),
2256            TokenBucket::Relay => write!(f, "RELAY"),
2257            TokenBucket::OrConn => write!(f, "ORCONN"),
2258        }
2259    }
2260}
2261
2262/// Actions that directory authorities take with relay descriptors.
2263///
2264/// # Example
2265///
2266/// ```rust
2267/// use stem_rs::AuthDescriptorAction;
2268///
2269/// let action = AuthDescriptorAction::Accepted;
2270/// println!("Auth action: {}", action); // Prints "ACCEPTED"
2271/// ```
2272#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2273pub enum AuthDescriptorAction {
2274    /// Accepting the descriptor as the newest version.
2275    Accepted,
2276    /// Descriptor rejected without notifying the relay.
2277    Dropped,
2278    /// Relay notified that its descriptor has been rejected.
2279    Rejected,
2280}
2281
2282impl fmt::Display for AuthDescriptorAction {
2283    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2284        match self {
2285            AuthDescriptorAction::Accepted => write!(f, "ACCEPTED"),
2286            AuthDescriptorAction::Dropped => write!(f, "DROPPED"),
2287            AuthDescriptorAction::Rejected => write!(f, "REJECTED"),
2288        }
2289    }
2290}
2291
2292/// Bridge distribution methods.
2293///
2294/// Specifies how a bridge relay should be distributed to users.
2295///
2296/// # Example
2297///
2298/// ```rust
2299/// use stem_rs::BridgeDistribution;
2300///
2301/// let dist = BridgeDistribution::Https;
2302/// println!("Distribution: {}", dist); // Prints "https"
2303/// ```
2304#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2305pub enum BridgeDistribution {
2306    /// Distribute via any method.
2307    Any,
2308    /// Distribute via HTTPS (bridges.torproject.org).
2309    Https,
2310    /// Distribute via email.
2311    Email,
2312    /// Distribute via Moat (built into Tor Browser).
2313    Moat,
2314    /// Distribute via Hyphae.
2315    Hyphae,
2316}
2317
2318impl fmt::Display for BridgeDistribution {
2319    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2320        match self {
2321            BridgeDistribution::Any => write!(f, "any"),
2322            BridgeDistribution::Https => write!(f, "https"),
2323            BridgeDistribution::Email => write!(f, "email"),
2324            BridgeDistribution::Moat => write!(f, "moat"),
2325            BridgeDistribution::Hyphae => write!(f, "hyphae"),
2326        }
2327    }
2328}
2329
2330#[cfg(test)]
2331mod tests {
2332    use super::*;
2333
2334    #[test]
2335    fn test_runlevel_display() {
2336        assert_eq!(format!("{}", Runlevel::Debug), "DEBUG");
2337        assert_eq!(format!("{}", Runlevel::Info), "INFO");
2338        assert_eq!(format!("{}", Runlevel::Notice), "NOTICE");
2339        assert_eq!(format!("{}", Runlevel::Warn), "WARN");
2340        assert_eq!(format!("{}", Runlevel::Err), "ERR");
2341    }
2342
2343    #[test]
2344    fn test_signal_display() {
2345        assert_eq!(format!("{}", Signal::Reload), "RELOAD");
2346        assert_eq!(format!("{}", Signal::Hup), "HUP");
2347        assert_eq!(format!("{}", Signal::Shutdown), "SHUTDOWN");
2348        assert_eq!(format!("{}", Signal::Int), "INT");
2349        assert_eq!(format!("{}", Signal::Dump), "DUMP");
2350        assert_eq!(format!("{}", Signal::Usr1), "USR1");
2351        assert_eq!(format!("{}", Signal::Debug), "DEBUG");
2352        assert_eq!(format!("{}", Signal::Usr2), "USR2");
2353        assert_eq!(format!("{}", Signal::Halt), "HALT");
2354        assert_eq!(format!("{}", Signal::Term), "TERM");
2355        assert_eq!(format!("{}", Signal::Newnym), "NEWNYM");
2356        assert_eq!(format!("{}", Signal::ClearDnsCache), "CLEARDNSCACHE");
2357        assert_eq!(format!("{}", Signal::Heartbeat), "HEARTBEAT");
2358        assert_eq!(format!("{}", Signal::Active), "ACTIVE");
2359        assert_eq!(format!("{}", Signal::Dormant), "DORMANT");
2360    }
2361
2362    #[test]
2363    fn test_flag_display() {
2364        assert_eq!(format!("{}", Flag::Authority), "Authority");
2365        assert_eq!(format!("{}", Flag::BadExit), "BadExit");
2366        assert_eq!(format!("{}", Flag::BadDirectory), "BadDirectory");
2367        assert_eq!(format!("{}", Flag::Exit), "Exit");
2368        assert_eq!(format!("{}", Flag::Fast), "Fast");
2369        assert_eq!(format!("{}", Flag::Guard), "Guard");
2370        assert_eq!(format!("{}", Flag::HsDir), "HSDir");
2371        assert_eq!(format!("{}", Flag::Named), "Named");
2372        assert_eq!(format!("{}", Flag::NoEdConsensus), "NoEdConsensus");
2373        assert_eq!(format!("{}", Flag::Running), "Running");
2374        assert_eq!(format!("{}", Flag::Stable), "Stable");
2375        assert_eq!(format!("{}", Flag::StaleDesc), "StaleDesc");
2376        assert_eq!(format!("{}", Flag::Unnamed), "Unnamed");
2377        assert_eq!(format!("{}", Flag::V2Dir), "V2Dir");
2378        assert_eq!(format!("{}", Flag::V3Dir), "V3Dir");
2379        assert_eq!(format!("{}", Flag::Valid), "Valid");
2380    }
2381
2382    #[test]
2383    fn test_circ_status_display() {
2384        assert_eq!(format!("{}", CircStatus::Launched), "LAUNCHED");
2385        assert_eq!(format!("{}", CircStatus::Built), "BUILT");
2386        assert_eq!(format!("{}", CircStatus::GuardWait), "GUARD_WAIT");
2387        assert_eq!(format!("{}", CircStatus::Extended), "EXTENDED");
2388        assert_eq!(format!("{}", CircStatus::Failed), "FAILED");
2389        assert_eq!(format!("{}", CircStatus::Closed), "CLOSED");
2390    }
2391
2392    #[test]
2393    fn test_circ_build_flag_display() {
2394        assert_eq!(format!("{}", CircBuildFlag::OneHopTunnel), "ONEHOP_TUNNEL");
2395        assert_eq!(format!("{}", CircBuildFlag::IsInternal), "IS_INTERNAL");
2396        assert_eq!(format!("{}", CircBuildFlag::NeedCapacity), "NEED_CAPACITY");
2397        assert_eq!(format!("{}", CircBuildFlag::NeedUptime), "NEED_UPTIME");
2398    }
2399
2400    #[test]
2401    fn test_circ_purpose_display() {
2402        assert_eq!(format!("{}", CircPurpose::General), "GENERAL");
2403        assert_eq!(format!("{}", CircPurpose::HsClientIntro), "HS_CLIENT_INTRO");
2404        assert_eq!(format!("{}", CircPurpose::HsClientRend), "HS_CLIENT_REND");
2405        assert_eq!(
2406            format!("{}", CircPurpose::HsServiceIntro),
2407            "HS_SERVICE_INTRO"
2408        );
2409        assert_eq!(format!("{}", CircPurpose::HsServiceRend), "HS_SERVICE_REND");
2410        assert_eq!(format!("{}", CircPurpose::Testing), "TESTING");
2411        assert_eq!(format!("{}", CircPurpose::Controller), "CONTROLLER");
2412        assert_eq!(
2413            format!("{}", CircPurpose::MeasureTimeout),
2414            "MEASURE_TIMEOUT"
2415        );
2416        assert_eq!(format!("{}", CircPurpose::HsVanguards), "HS_VANGUARDS");
2417        assert_eq!(
2418            format!("{}", CircPurpose::PathBiasTesting),
2419            "PATH_BIAS_TESTING"
2420        );
2421        assert_eq!(
2422            format!("{}", CircPurpose::CircuitPadding),
2423            "CIRCUIT_PADDING"
2424        );
2425    }
2426
2427    #[test]
2428    fn test_circ_closure_reason_display() {
2429        assert_eq!(format!("{}", CircClosureReason::None), "NONE");
2430        assert_eq!(format!("{}", CircClosureReason::TorProtocol), "TORPROTOCOL");
2431        assert_eq!(format!("{}", CircClosureReason::Internal), "INTERNAL");
2432        assert_eq!(format!("{}", CircClosureReason::Requested), "REQUESTED");
2433        assert_eq!(format!("{}", CircClosureReason::Hibernating), "HIBERNATING");
2434        assert_eq!(
2435            format!("{}", CircClosureReason::ResourceLimit),
2436            "RESOURCELIMIT"
2437        );
2438        assert_eq!(
2439            format!("{}", CircClosureReason::ConnectFailed),
2440            "CONNECTFAILED"
2441        );
2442        assert_eq!(format!("{}", CircClosureReason::OrIdentity), "OR_IDENTITY");
2443        assert_eq!(
2444            format!("{}", CircClosureReason::OrConnClosed),
2445            "OR_CONN_CLOSED"
2446        );
2447        assert_eq!(format!("{}", CircClosureReason::Finished), "FINISHED");
2448        assert_eq!(format!("{}", CircClosureReason::Timeout), "TIMEOUT");
2449        assert_eq!(format!("{}", CircClosureReason::Destroyed), "DESTROYED");
2450        assert_eq!(format!("{}", CircClosureReason::NoPath), "NOPATH");
2451        assert_eq!(
2452            format!("{}", CircClosureReason::NoSuchService),
2453            "NOSUCHSERVICE"
2454        );
2455        assert_eq!(
2456            format!("{}", CircClosureReason::MeasurementExpired),
2457            "MEASUREMENT_EXPIRED"
2458        );
2459        assert_eq!(
2460            format!("{}", CircClosureReason::IpNowRedundant),
2461            "IP_NOW_REDUNDANT"
2462        );
2463    }
2464
2465    #[test]
2466    fn test_circ_event_display() {
2467        assert_eq!(format!("{}", CircEvent::PurposeChanged), "PURPOSE_CHANGED");
2468        assert_eq!(format!("{}", CircEvent::Cannibalized), "CANNIBALIZED");
2469    }
2470
2471    #[test]
2472    fn test_hidden_service_state_display() {
2473        assert_eq!(
2474            format!("{}", HiddenServiceState::HsciConnecting),
2475            "HSCI_CONNECTING"
2476        );
2477        assert_eq!(
2478            format!("{}", HiddenServiceState::HsciIntroSent),
2479            "HSCI_INTRO_SENT"
2480        );
2481        assert_eq!(format!("{}", HiddenServiceState::HsciDone), "HSCI_DONE");
2482        assert_eq!(
2483            format!("{}", HiddenServiceState::HscrConnecting),
2484            "HSCR_CONNECTING"
2485        );
2486        assert_eq!(
2487            format!("{}", HiddenServiceState::HscrEstablishedIdle),
2488            "HSCR_ESTABLISHED_IDLE"
2489        );
2490        assert_eq!(
2491            format!("{}", HiddenServiceState::HscrEstablishedWaiting),
2492            "HSCR_ESTABLISHED_WAITING"
2493        );
2494        assert_eq!(format!("{}", HiddenServiceState::HscrJoined), "HSCR_JOINED");
2495        assert_eq!(
2496            format!("{}", HiddenServiceState::HssiConnecting),
2497            "HSSI_CONNECTING"
2498        );
2499        assert_eq!(
2500            format!("{}", HiddenServiceState::HssiEstablished),
2501            "HSSI_ESTABLISHED"
2502        );
2503        assert_eq!(
2504            format!("{}", HiddenServiceState::HssrConnecting),
2505            "HSSR_CONNECTING"
2506        );
2507        assert_eq!(format!("{}", HiddenServiceState::HssrJoined), "HSSR_JOINED");
2508    }
2509
2510    #[test]
2511    fn test_stream_status_display() {
2512        assert_eq!(format!("{}", StreamStatus::New), "NEW");
2513        assert_eq!(format!("{}", StreamStatus::NewResolve), "NEWRESOLVE");
2514        assert_eq!(format!("{}", StreamStatus::Remap), "REMAP");
2515        assert_eq!(format!("{}", StreamStatus::SentConnect), "SENTCONNECT");
2516        assert_eq!(format!("{}", StreamStatus::SentResolve), "SENTRESOLVE");
2517        assert_eq!(format!("{}", StreamStatus::Succeeded), "SUCCEEDED");
2518        assert_eq!(format!("{}", StreamStatus::Failed), "FAILED");
2519        assert_eq!(format!("{}", StreamStatus::Detached), "DETACHED");
2520        assert_eq!(
2521            format!("{}", StreamStatus::ControllerWait),
2522            "CONTROLLER_WAIT"
2523        );
2524        assert_eq!(format!("{}", StreamStatus::Closed), "CLOSED");
2525    }
2526
2527    #[test]
2528    fn test_stream_closure_reason_display() {
2529        assert_eq!(format!("{}", StreamClosureReason::Misc), "MISC");
2530        assert_eq!(
2531            format!("{}", StreamClosureReason::ResolveFailed),
2532            "RESOLVEFAILED"
2533        );
2534        assert_eq!(
2535            format!("{}", StreamClosureReason::ConnectRefused),
2536            "CONNECTREFUSED"
2537        );
2538        assert_eq!(format!("{}", StreamClosureReason::ExitPolicy), "EXITPOLICY");
2539        assert_eq!(format!("{}", StreamClosureReason::Destroy), "DESTROY");
2540        assert_eq!(format!("{}", StreamClosureReason::Done), "DONE");
2541        assert_eq!(format!("{}", StreamClosureReason::Timeout), "TIMEOUT");
2542        assert_eq!(format!("{}", StreamClosureReason::NoRoute), "NOROUTE");
2543        assert_eq!(
2544            format!("{}", StreamClosureReason::Hibernating),
2545            "HIBERNATING"
2546        );
2547        assert_eq!(format!("{}", StreamClosureReason::Internal), "INTERNAL");
2548        assert_eq!(
2549            format!("{}", StreamClosureReason::ResourceLimit),
2550            "RESOURCELIMIT"
2551        );
2552        assert_eq!(format!("{}", StreamClosureReason::ConnReset), "CONNRESET");
2553        assert_eq!(
2554            format!("{}", StreamClosureReason::TorProtocol),
2555            "TORPROTOCOL"
2556        );
2557        assert_eq!(
2558            format!("{}", StreamClosureReason::NotDirectory),
2559            "NOTDIRECTORY"
2560        );
2561        assert_eq!(format!("{}", StreamClosureReason::End), "END");
2562        assert_eq!(
2563            format!("{}", StreamClosureReason::PrivateAddr),
2564            "PRIVATE_ADDR"
2565        );
2566    }
2567
2568    #[test]
2569    fn test_stream_source_display() {
2570        assert_eq!(format!("{}", StreamSource::Cache), "CACHE");
2571        assert_eq!(format!("{}", StreamSource::Exit), "EXIT");
2572    }
2573
2574    #[test]
2575    fn test_stream_purpose_display() {
2576        assert_eq!(format!("{}", StreamPurpose::DirFetch), "DIR_FETCH");
2577        assert_eq!(format!("{}", StreamPurpose::DirUpload), "DIR_UPLOAD");
2578        assert_eq!(format!("{}", StreamPurpose::DnsRequest), "DNS_REQUEST");
2579        assert_eq!(format!("{}", StreamPurpose::DirportTest), "DIRPORT_TEST");
2580        assert_eq!(format!("{}", StreamPurpose::User), "USER");
2581    }
2582
2583    #[test]
2584    fn test_or_status_display() {
2585        assert_eq!(format!("{}", OrStatus::New), "NEW");
2586        assert_eq!(format!("{}", OrStatus::Launched), "LAUNCHED");
2587        assert_eq!(format!("{}", OrStatus::Connected), "CONNECTED");
2588        assert_eq!(format!("{}", OrStatus::Failed), "FAILED");
2589        assert_eq!(format!("{}", OrStatus::Closed), "CLOSED");
2590    }
2591
2592    #[test]
2593    fn test_or_closure_reason_display() {
2594        assert_eq!(format!("{}", OrClosureReason::Done), "DONE");
2595        assert_eq!(
2596            format!("{}", OrClosureReason::ConnectRefused),
2597            "CONNECTREFUSED"
2598        );
2599        assert_eq!(format!("{}", OrClosureReason::Identity), "IDENTITY");
2600        assert_eq!(format!("{}", OrClosureReason::ConnectReset), "CONNECTRESET");
2601        assert_eq!(format!("{}", OrClosureReason::Timeout), "TIMEOUT");
2602        assert_eq!(format!("{}", OrClosureReason::NoRoute), "NOROUTE");
2603        assert_eq!(format!("{}", OrClosureReason::IoError), "IOERROR");
2604        assert_eq!(
2605            format!("{}", OrClosureReason::ResourceLimit),
2606            "RESOURCELIMIT"
2607        );
2608        assert_eq!(format!("{}", OrClosureReason::Misc), "MISC");
2609        assert_eq!(format!("{}", OrClosureReason::PtMissing), "PT_MISSING");
2610    }
2611
2612    #[test]
2613    fn test_guard_type_display() {
2614        assert_eq!(format!("{}", GuardType::Entry), "ENTRY");
2615    }
2616
2617    #[test]
2618    fn test_guard_status_display() {
2619        assert_eq!(format!("{}", GuardStatus::New), "NEW");
2620        assert_eq!(format!("{}", GuardStatus::Dropped), "DROPPED");
2621        assert_eq!(format!("{}", GuardStatus::Up), "UP");
2622        assert_eq!(format!("{}", GuardStatus::Down), "DOWN");
2623        assert_eq!(format!("{}", GuardStatus::Bad), "BAD");
2624        assert_eq!(format!("{}", GuardStatus::Good), "GOOD");
2625    }
2626
2627    #[test]
2628    fn test_timeout_set_type_display() {
2629        assert_eq!(format!("{}", TimeoutSetType::Computed), "COMPUTED");
2630        assert_eq!(format!("{}", TimeoutSetType::Reset), "RESET");
2631        assert_eq!(format!("{}", TimeoutSetType::Suspended), "SUSPENDED");
2632        assert_eq!(format!("{}", TimeoutSetType::Discard), "DISCARD");
2633        assert_eq!(format!("{}", TimeoutSetType::Resume), "RESUME");
2634    }
2635
2636    #[test]
2637    fn test_hs_desc_action_display() {
2638        assert_eq!(format!("{}", HsDescAction::Requested), "REQUESTED");
2639        assert_eq!(format!("{}", HsDescAction::Upload), "UPLOAD");
2640        assert_eq!(format!("{}", HsDescAction::Received), "RECEIVED");
2641        assert_eq!(format!("{}", HsDescAction::Uploaded), "UPLOADED");
2642        assert_eq!(format!("{}", HsDescAction::Ignore), "IGNORE");
2643        assert_eq!(format!("{}", HsDescAction::Failed), "FAILED");
2644        assert_eq!(format!("{}", HsDescAction::Created), "CREATED");
2645    }
2646
2647    #[test]
2648    fn test_hs_desc_reason_display() {
2649        assert_eq!(format!("{}", HsDescReason::BadDesc), "BAD_DESC");
2650        assert_eq!(format!("{}", HsDescReason::QueryRejected), "QUERY_REJECTED");
2651        assert_eq!(
2652            format!("{}", HsDescReason::UploadRejected),
2653            "UPLOAD_REJECTED"
2654        );
2655        assert_eq!(format!("{}", HsDescReason::NotFound), "NOT_FOUND");
2656        assert_eq!(format!("{}", HsDescReason::QueryNoHsDir), "QUERY_NO_HSDIR");
2657        assert_eq!(
2658            format!("{}", HsDescReason::QueryRateLimited),
2659            "QUERY_RATE_LIMITED"
2660        );
2661        assert_eq!(format!("{}", HsDescReason::Unexpected), "UNEXPECTED");
2662    }
2663
2664    #[test]
2665    fn test_hs_auth_display() {
2666        assert_eq!(format!("{}", HsAuth::NoAuth), "NO_AUTH");
2667        assert_eq!(format!("{}", HsAuth::BasicAuth), "BASIC_AUTH");
2668        assert_eq!(format!("{}", HsAuth::StealthAuth), "STEALTH_AUTH");
2669        assert_eq!(format!("{}", HsAuth::Unknown), "UNKNOWN");
2670    }
2671
2672    #[test]
2673    fn test_event_type_display() {
2674        assert_eq!(format!("{}", EventType::Circ), "CIRC");
2675        assert_eq!(format!("{}", EventType::Stream), "STREAM");
2676        assert_eq!(format!("{}", EventType::OrConn), "ORCONN");
2677        assert_eq!(format!("{}", EventType::Bw), "BW");
2678        assert_eq!(format!("{}", EventType::Debug), "DEBUG");
2679        assert_eq!(format!("{}", EventType::Info), "INFO");
2680        assert_eq!(format!("{}", EventType::Notice), "NOTICE");
2681        assert_eq!(format!("{}", EventType::Warn), "WARN");
2682        assert_eq!(format!("{}", EventType::Err), "ERR");
2683        assert_eq!(format!("{}", EventType::NewDesc), "NEWDESC");
2684        assert_eq!(format!("{}", EventType::AddrMap), "ADDRMAP");
2685        assert_eq!(format!("{}", EventType::AuthDir), "AUTHDIR_NEWDESCS");
2686        assert_eq!(format!("{}", EventType::DescChanged), "DESCCHANGED");
2687        assert_eq!(format!("{}", EventType::Status), "STATUS_GENERAL");
2688        assert_eq!(format!("{}", EventType::Guard), "GUARD");
2689        assert_eq!(format!("{}", EventType::Ns), "NS");
2690        assert_eq!(format!("{}", EventType::StreamBw), "STREAM_BW");
2691        assert_eq!(format!("{}", EventType::ClientsSeen), "CLIENTS_SEEN");
2692        assert_eq!(format!("{}", EventType::NewConsensus), "NEWCONSENSUS");
2693        assert_eq!(
2694            format!("{}", EventType::BuildTimeoutSet),
2695            "BUILDTIMEOUT_SET"
2696        );
2697        assert_eq!(format!("{}", EventType::Signal), "SIGNAL");
2698        assert_eq!(format!("{}", EventType::ConfChanged), "CONF_CHANGED");
2699        assert_eq!(format!("{}", EventType::CircMinor), "CIRC_MINOR");
2700        assert_eq!(
2701            format!("{}", EventType::TransportLaunched),
2702            "TRANSPORT_LAUNCHED"
2703        );
2704        assert_eq!(format!("{}", EventType::ConnBw), "CONN_BW");
2705        assert_eq!(format!("{}", EventType::CircBw), "CIRC_BW");
2706        assert_eq!(format!("{}", EventType::CellStats), "CELL_STATS");
2707        assert_eq!(format!("{}", EventType::HsDesc), "HS_DESC");
2708        assert_eq!(format!("{}", EventType::HsDescContent), "HS_DESC_CONTENT");
2709        assert_eq!(
2710            format!("{}", EventType::NetworkLiveness),
2711            "NETWORK_LIVENESS"
2712        );
2713        assert_eq!(format!("{}", EventType::PtLog), "PT_LOG");
2714        assert_eq!(format!("{}", EventType::PtStatus), "PT_STATUS");
2715    }
2716
2717    #[test]
2718    fn test_status_type_display() {
2719        assert_eq!(format!("{}", StatusType::General), "GENERAL");
2720        assert_eq!(format!("{}", StatusType::Client), "CLIENT");
2721        assert_eq!(format!("{}", StatusType::Server), "SERVER");
2722    }
2723
2724    #[test]
2725    fn test_connection_type_display() {
2726        assert_eq!(format!("{}", ConnectionType::Or), "OR");
2727        assert_eq!(format!("{}", ConnectionType::Dir), "DIR");
2728        assert_eq!(format!("{}", ConnectionType::Exit), "EXIT");
2729    }
2730
2731    #[test]
2732    fn test_token_bucket_display() {
2733        assert_eq!(format!("{}", TokenBucket::Global), "GLOBAL");
2734        assert_eq!(format!("{}", TokenBucket::Relay), "RELAY");
2735        assert_eq!(format!("{}", TokenBucket::OrConn), "ORCONN");
2736    }
2737
2738    #[test]
2739    fn test_auth_descriptor_action_display() {
2740        assert_eq!(format!("{}", AuthDescriptorAction::Accepted), "ACCEPTED");
2741        assert_eq!(format!("{}", AuthDescriptorAction::Dropped), "DROPPED");
2742        assert_eq!(format!("{}", AuthDescriptorAction::Rejected), "REJECTED");
2743    }
2744
2745    #[test]
2746    fn test_bridge_distribution_display() {
2747        assert_eq!(format!("{}", BridgeDistribution::Any), "any");
2748        assert_eq!(format!("{}", BridgeDistribution::Https), "https");
2749        assert_eq!(format!("{}", BridgeDistribution::Email), "email");
2750        assert_eq!(format!("{}", BridgeDistribution::Moat), "moat");
2751        assert_eq!(format!("{}", BridgeDistribution::Hyphae), "hyphae");
2752    }
2753
2754    #[test]
2755    fn test_error_display() {
2756        let err = Error::Protocol("test error".to_string());
2757        assert!(format!("{}", err).contains("test error"));
2758
2759        let err = Error::OperationFailed {
2760            code: "500".to_string(),
2761            message: "failed".to_string(),
2762        };
2763        assert!(format!("{}", err).contains("500"));
2764        assert!(format!("{}", err).contains("failed"));
2765
2766        let err = Error::Parse {
2767            location: "line 1".to_string(),
2768            reason: "invalid".to_string(),
2769        };
2770        assert!(format!("{}", err).contains("line 1"));
2771        assert!(format!("{}", err).contains("invalid"));
2772
2773        let err = Error::Download {
2774            url: "http://example.com".to_string(),
2775            reason: "timeout".to_string(),
2776        };
2777        assert!(format!("{}", err).contains("example.com"));
2778
2779        let err = Error::DownloadTimeout {
2780            url: "http://example.com".to_string(),
2781        };
2782        assert!(format!("{}", err).contains("example.com"));
2783
2784        let err = Error::Timeout;
2785        assert!(format!("{}", err).contains("timeout"));
2786
2787        let err = Error::SocketClosed;
2788        assert!(format!("{}", err).contains("closed"));
2789
2790        let err = Error::DescriptorUnavailable("test".to_string());
2791        assert!(format!("{}", err).contains("test"));
2792
2793        let err = Error::CircuitExtensionFailed("test".to_string());
2794        assert!(format!("{}", err).contains("test"));
2795
2796        let err = Error::UnsatisfiableRequest("test".to_string());
2797        assert!(format!("{}", err).contains("test"));
2798
2799        let err = Error::InvalidRequest("test".to_string());
2800        assert!(format!("{}", err).contains("test"));
2801
2802        let err = Error::InvalidArguments("test".to_string());
2803        assert!(format!("{}", err).contains("test"));
2804    }
2805
2806    #[test]
2807    fn test_auth_error_display() {
2808        let err = AuthError::NoMethods;
2809        assert!(format!("{}", err).contains("no authentication"));
2810
2811        let err = AuthError::IncorrectPassword;
2812        assert!(format!("{}", err).contains("password"));
2813
2814        let err = AuthError::CookieUnreadable("path".to_string());
2815        assert!(format!("{}", err).contains("path"));
2816
2817        let err = AuthError::IncorrectCookie;
2818        assert!(format!("{}", err).contains("cookie"));
2819
2820        let err = AuthError::IncorrectCookieSize;
2821        assert!(format!("{}", err).contains("cookie"));
2822
2823        let err = AuthError::ChallengeFailed;
2824        assert!(format!("{}", err).contains("challenge"));
2825
2826        let err = AuthError::ChallengeUnsupported;
2827        assert!(format!("{}", err).contains("challenge"));
2828
2829        let err = AuthError::SecurityFailure;
2830        assert!(format!("{}", err).contains("security"));
2831
2832        let err = AuthError::MissingPassword;
2833        assert!(format!("{}", err).contains("password"));
2834
2835        let err = AuthError::UnrecognizedMethods(vec!["test".to_string()]);
2836        assert!(format!("{}", err).contains("test"));
2837
2838        let err = AuthError::IncorrectSocketType;
2839        assert!(format!("{}", err).contains("socket"));
2840    }
2841
2842    #[test]
2843    fn test_enum_equality() {
2844        assert_eq!(Runlevel::Debug, Runlevel::Debug);
2845        assert_ne!(Runlevel::Debug, Runlevel::Info);
2846
2847        assert_eq!(Signal::Reload, Signal::Reload);
2848        assert_ne!(Signal::Reload, Signal::Shutdown);
2849
2850        assert_eq!(Flag::Exit, Flag::Exit);
2851        assert_ne!(Flag::Exit, Flag::Guard);
2852    }
2853
2854    #[test]
2855    fn test_enum_hash() {
2856        use std::collections::HashSet;
2857
2858        let mut set = HashSet::new();
2859        set.insert(Runlevel::Debug);
2860        set.insert(Runlevel::Info);
2861        assert!(set.contains(&Runlevel::Debug));
2862        assert!(!set.contains(&Runlevel::Warn));
2863
2864        let mut set = HashSet::new();
2865        set.insert(Signal::Newnym);
2866        assert!(set.contains(&Signal::Newnym));
2867    }
2868
2869    #[test]
2870    fn test_enum_clone() {
2871        let r = Runlevel::Debug;
2872        let r2 = r;
2873        assert_eq!(r, r2);
2874
2875        let s = Signal::Newnym;
2876        let s2 = s;
2877        assert_eq!(s, s2);
2878    }
2879
2880    #[test]
2881    fn test_enum_debug() {
2882        assert!(format!("{:?}", Runlevel::Debug).contains("Debug"));
2883        assert!(format!("{:?}", Signal::Newnym).contains("Newnym"));
2884        assert!(format!("{:?}", Flag::Exit).contains("Exit"));
2885    }
2886}