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}