Skip to main content

apimock_config/
error.rs

1//! Errors surfaced by the config crate.
2//!
3//! See `apimock_routing::error` for the rationale on per-crate error
4//! types. `ConfigError` wraps `RoutingError` via `#[from]` so rule-set
5//! load failures flow through without the caller pattern-matching on
6//! origin.
7//!
8//! # 5.1.0 additions
9//!
10//! - `WorkspaceError` — surfaced by `Workspace::load`.
11//! - `ApplyError` — surfaced by `Workspace::apply`.
12//! - `SaveError` — surfaced by `Workspace::save`.
13//!
14//! Each of the three "operation" errors wraps `ConfigError` via
15//! `#[from]` because the underlying cause of most workspace failures
16//! is a plain config load / write problem.
17
18use std::{io, path::PathBuf};
19
20use crate::view::NodeId;
21
22pub type ConfigResult<T> = Result<T, ConfigError>;
23
24#[derive(Debug, thiserror::Error)]
25pub enum ConfigError {
26    /// The config TOML file could not be read from disk.
27    #[error("failed to read config file `{path}`: {source}")]
28    ConfigRead {
29        path: PathBuf,
30        #[source]
31        source: io::Error,
32    },
33
34    /// The config TOML file was read, but could not be parsed.
35    #[error("invalid TOML in `{path}`{canonical_display}: {source}", canonical_display = match canonical {
36        Some(p) => format!(" ({})", p.display()),
37        None => String::new(),
38    })]
39    ConfigParse {
40        path: PathBuf,
41        canonical: Option<PathBuf>,
42        #[source]
43        source: toml::de::Error,
44    },
45
46    /// A path on disk could not be resolved.
47    #[error("failed to resolve path `{path}`: {source}")]
48    PathResolve {
49        path: PathBuf,
50        #[source]
51        source: io::Error,
52    },
53
54    /// Startup-time validation failed — each individual failure is
55    /// already logged at its call site.
56    #[error("configuration validation failed")]
57    Validation,
58
59    /// A rule-set file failed to load or parse. Wraps the routing
60    /// crate's error type.
61    #[error(transparent)]
62    RuleSet(#[from] apimock_routing::RoutingError),
63}
64
65/// Failure during `Workspace::load`. Currently a thin wrapper around
66/// `ConfigError` — kept as its own type so the `Workspace` API signals
67/// intent at the type level and has room to grow (e.g. "path is not a
68/// directory", "no root config found").
69#[derive(Debug, thiserror::Error)]
70pub enum WorkspaceError {
71    #[error(transparent)]
72    Config(#[from] ConfigError),
73
74    /// Root path was not found or was not a regular file/directory.
75    #[error("workspace root `{path}` is not a valid apimock workspace: {reason}")]
76    InvalidRoot { path: PathBuf, reason: String },
77}
78
79/// Failure during `Workspace::apply`.
80///
81/// # Why these particular variants
82///
83/// Every `EditCommand` variant targets a node by NodeId. The two
84/// failure modes are "that ID doesn't exist" and "the ID exists but
85/// refers to a node of the wrong kind for this command". Everything
86/// else (file-not-found when `AddRuleSet` with a missing path) is a
87/// validation issue reported via `ApplyResult::diagnostics`, not an
88/// error return.
89#[derive(Debug, thiserror::Error)]
90pub enum ApplyError {
91    /// The NodeId in the command wasn't found in the workspace.
92    #[error("unknown node id: {id}")]
93    UnknownNode { id: NodeId },
94
95    /// The NodeId exists but names a node of the wrong kind for this
96    /// command (e.g. `DeleteRule` pointing at a rule-set ID).
97    #[error("node {id} is not of the expected kind for this command: {reason}")]
98    WrongNodeKind { id: NodeId, reason: String },
99
100    /// Invalid command payload (e.g. `MoveRule` with `new_index` past
101    /// end of parent's rule list).
102    #[error("invalid edit payload: {reason}")]
103    InvalidPayload { reason: String },
104}
105
106/// Failure during `Workspace::save`.
107#[derive(Debug, thiserror::Error)]
108pub enum SaveError {
109    /// A TOML file failed to serialise.
110    #[error("failed to serialise `{path}`: {source}")]
111    Serialize {
112        path: PathBuf,
113        #[source]
114        source: toml::ser::Error,
115    },
116    /// Writing the serialised TOML to disk failed.
117    #[error("failed to write `{path}`: {source}")]
118    Write {
119        path: PathBuf,
120        #[source]
121        source: io::Error,
122    },
123    /// The workspace's internal state was inconsistent at save time —
124    /// usually a programmer error in the edit layer.
125    #[error("internal inconsistency: {reason}")]
126    Inconsistent { reason: String },
127}
128