Skip to main content

prosaic_project/
error.rs

1//! Project loading and bundling errors.
2
3use thiserror::Error;
4
5#[derive(Debug, Error)]
6pub enum ProjectError {
7    #[error("manifest missing at `{path}`: expected a `prosaic.toml` at the project root")]
8    ManifestMissing { path: String },
9
10    #[error("malformed TOML in `{file}`: {cause}")]
11    TomlParse { file: String, cause: String },
12
13    #[error("malformed JSON in `{file}`: {cause}")]
14    JsonParse { file: String, cause: String },
15
16    #[error("template `{key}` validation failed: {reason}")]
17    TemplateValidation { key: String, reason: String },
18
19    #[error("partial `{name}` validation failed: {reason}")]
20    PartialValidation { name: String, reason: String },
21
22    #[error("scenario `{name}` failed: {reason}")]
23    ScenarioValidation { name: String, reason: String },
24
25    #[error("fixture `{name}` failed: {reason}")]
26    FixtureValidation { name: String, reason: String },
27
28    #[error(
29        "vocab dependency `{crate_name}` not registered: enable the `{crate_name}` feature or add it to `prosaic.toml`"
30    )]
31    UnregisteredVocab { crate_name: String },
32
33    #[error("template `{key}` references unknown partial `{partial}`")]
34    UnknownPartial { key: String, partial: String },
35
36    #[error("template `{key}` uses unknown pipe `{pipe}` (known pipes: {known})")]
37    UnknownPipe {
38        key: String,
39        pipe: String,
40        known: String,
41    },
42
43    #[error("io error reading `{path}`: {cause}")]
44    Io { path: String, cause: String },
45
46    #[error("style profile invalid: {reason}")]
47    ManifestStyle { reason: String },
48
49    #[error("engine error: {0}")]
50    Engine(#[from] prosaic_core::ProsaicError),
51}
52
53#[cfg(test)]
54mod tests {
55    use super::ProjectError;
56
57    #[test]
58    fn missing_manifest_displays_path() {
59        let e = ProjectError::ManifestMissing {
60            path: "/tmp/foo/prosaic.toml".to_string(),
61        };
62        let msg = format!("{e}");
63        assert!(msg.contains("prosaic.toml"));
64        assert!(msg.contains("/tmp/foo"));
65    }
66
67    #[test]
68    fn malformed_toml_displays_filename_and_inner() {
69        let e = ProjectError::TomlParse {
70            file: "templates/code.modified.toml".to_string(),
71            cause: "expected `=` after key".to_string(),
72        };
73        let msg = format!("{e}");
74        assert!(msg.contains("code.modified.toml"));
75        assert!(msg.contains("expected `=`"));
76    }
77
78    #[test]
79    fn template_validation_includes_key() {
80        let e = ProjectError::TemplateValidation {
81            key: "code.modified".to_string(),
82            reason: "no variants registered".to_string(),
83        };
84        let msg = format!("{e}");
85        assert!(msg.contains("code.modified"));
86        assert!(msg.contains("no variants"));
87    }
88}