Skip to main content

skilltest_core/
error.rs

1//! Error type for the core library. The mapping from these errors to process
2//! exit codes lives in the CLI (see `exit.rs` for the documented codes).
3
4use std::path::PathBuf;
5
6/// Result alias used throughout the core library.
7pub type Result<T> = std::result::Result<T, Error>;
8
9/// Everything that can go wrong while loading configuration, parsing skill or
10/// test-case definitions, talking to a provider, or running evals.
11///
12/// Variants are grouped so the CLI can map them onto stable exit codes: input
13/// problems (`Config`, `Yaml`, `Skill`, `Validation`) are the user's to fix;
14/// `Provider` problems are environmental.
15#[derive(Debug, thiserror::Error)]
16pub enum Error {
17    /// A file the user pointed us at could not be read.
18    #[error("could not read {path}: {source}")]
19    Io {
20        path: PathBuf,
21        #[source]
22        source: std::io::Error,
23    },
24
25    /// A YAML document (config or test case) failed to parse.
26    #[error("invalid YAML in {path}: {source}")]
27    Yaml {
28        path: PathBuf,
29        #[source]
30        source: serde_yaml::Error,
31    },
32
33    /// A test case or config was syntactically valid YAML but semantically
34    /// wrong (e.g. a numeric eval with `min > max`).
35    #[error("invalid test definition: {0}")]
36    Invalid(String),
37
38    /// The provider command could not be spawned or did not behave. `kind`, when
39    /// set, classifies the failure (e.g. `"auth"`, `"rate_limit"`,
40    /// `"model_not_found"`, `"quota"`) so the CLI can distinguish a broken
41    /// environment from a broken skill.
42    #[error("provider error ({context}): {message}")]
43    Provider {
44        context: String,
45        message: String,
46        kind: Option<String>,
47    },
48
49    /// A skill definition failed validation. Carries the human-readable
50    /// findings so the CLI can print them.
51    #[error("skill validation failed with {} finding(s)", .0.len())]
52    Validation(Vec<String>),
53}
54
55impl Error {
56    /// Construct a [`Error::Provider`] with no classification.
57    pub fn provider(context: impl Into<String>, message: impl std::fmt::Display) -> Self {
58        Error::Provider {
59            context: context.into(),
60            message: message.to_string(),
61            kind: None,
62        }
63    }
64
65    /// Construct a classified [`Error::Provider`] (e.g. `kind = "auth"`).
66    pub fn provider_classified(
67        context: impl Into<String>,
68        message: impl std::fmt::Display,
69        kind: impl Into<String>,
70    ) -> Self {
71        Error::Provider {
72            context: context.into(),
73            message: message.to_string(),
74            kind: Some(kind.into()),
75        }
76    }
77}