mockforge_sdk/
error.rs

1//! Error types for the MockForge SDK
2
3use thiserror::Error;
4
5/// SDK Result type
6pub type Result<T> = std::result::Result<T, Error>;
7
8/// SDK Error types
9#[derive(Error, Debug)]
10pub enum Error {
11    /// Server already started
12    #[error("Mock server is already running on port {0}. Call stop() before starting again.")]
13    ServerAlreadyStarted(u16),
14
15    /// Server not started
16    #[error("Mock server has not been started yet. Call start() first.")]
17    ServerNotStarted,
18
19    /// Port already in use
20    #[error("Port {0} is already in use. Try using a different port or enable auto_port().")]
21    PortInUse(u16),
22
23    /// Port discovery failed
24    #[error("Port discovery failed: {0}\nTip: Try expanding the port range using port_range(start, end).")]
25    PortDiscoveryFailed(String),
26
27    /// Invalid configuration
28    #[error("Invalid configuration: {0}\nCheck your configuration file or builder settings.")]
29    InvalidConfig(String),
30
31    /// Invalid stub
32    #[error("Invalid stub: {0}\nEnsure method, path, and response body are properly set.")]
33    InvalidStub(String),
34
35    /// Stub not found
36    #[error("Stub not found for {method} {path}. Available stubs: {available}")]
37    StubNotFound {
38        /// HTTP method that was requested
39        method: String,
40        /// Path that was requested
41        path: String,
42        /// Comma-separated list of available stubs
43        available: String,
44    },
45
46    /// HTTP error
47    #[error("HTTP error: {0}\nThis may indicate a network or protocol issue.")]
48    Http(#[from] axum::http::Error),
49
50    /// IO error
51    #[error("IO error: {0}\nCheck file permissions and network connectivity.")]
52    Io(#[from] std::io::Error),
53
54    /// JSON serialization error
55    #[error("JSON serialization error: {0}\nEnsure your request/response body is valid JSON.")]
56    Json(#[from] serde_json::Error),
57
58    /// MockForge core error
59    #[error("MockForge core error: {0}")]
60    Core(#[from] mockforge_core::Error),
61
62    /// Server startup timeout
63    #[error("Server failed to start within {timeout_secs} seconds.\nCheck logs for details or increase timeout.")]
64    StartupTimeout {
65        /// Number of seconds waited before timeout
66        timeout_secs: u64,
67    },
68
69    /// Server shutdown timeout
70    #[error("Server failed to stop within {timeout_secs} seconds.\nSome connections may still be active.")]
71    ShutdownTimeout {
72        /// Number of seconds waited before timeout
73        timeout_secs: u64,
74    },
75
76    /// Admin API error
77    #[error("Admin API error ({operation}): {message}\nEndpoint: {endpoint}")]
78    AdminApiError {
79        /// The operation that failed (e.g., "create_mock", "list_mocks")
80        operation: String,
81        /// The error message from the server or client
82        message: String,
83        /// The API endpoint that was called
84        endpoint: String,
85    },
86
87    /// General error
88    #[error("{0}")]
89    General(String),
90}
91
92impl Error {
93    /// Create an admin API error with context
94    ///
95    /// # Examples
96    ///
97    /// ```rust
98    /// use mockforge_sdk::Error;
99    ///
100    /// let err = Error::admin_api_error(
101    ///     "create_mock",
102    ///     "Invalid JSON",
103    ///     "/api/mocks"
104    /// );
105    /// ```
106    pub fn admin_api_error(
107        operation: impl Into<String>,
108        message: impl Into<String>,
109        endpoint: impl Into<String>,
110    ) -> Self {
111        Error::AdminApiError {
112            operation: operation.into(),
113            message: message.into(),
114            endpoint: endpoint.into(),
115        }
116    }
117
118    /// Create a stub not found error with available stubs
119    ///
120    /// # Examples
121    ///
122    /// ```rust
123    /// use mockforge_sdk::Error;
124    ///
125    /// let err = Error::stub_not_found(
126    ///     "GET",
127    ///     "/api/missing",
128    ///     vec!["GET /api/users".to_string()]
129    /// );
130    /// ```
131    pub fn stub_not_found(
132        method: impl Into<String>,
133        path: impl Into<String>,
134        available: Vec<String>,
135    ) -> Self {
136        Error::StubNotFound {
137            method: method.into(),
138            path: path.into(),
139            available: if available.is_empty() {
140                "none".to_string()
141            } else {
142                available.join(", ")
143            },
144        }
145    }
146
147    /// Format error for logging (single line, no ANSI colors)
148    ///
149    /// Useful for structured logging where multi-line messages aren't desired.
150    ///
151    /// # Examples
152    ///
153    /// ```rust
154    /// use mockforge_sdk::Error;
155    ///
156    /// let err = Error::ServerNotStarted;
157    /// let log_msg = err.to_log_string();
158    /// // Use in logging: log::error!("{}", log_msg);
159    /// ```
160    pub fn to_log_string(&self) -> String {
161        format!("{}", self).replace('\n', " | ")
162    }
163}