markdown_contract/declarative/errors.rs
1//! The loader's error: a declarative document that is malformed or uses a feature
2//! outside `mcVersion: 2`. The variants a host may act on are separate:
3//! [`DeclarativeError::UnsupportedVersion`], [`DeclarativeError::RetiredVersion`]
4//! (`mcVersion: 1`, which `markdown-contract migrate` rewrites) and
5//! [`DeclarativeError::CodeContractRef`].
6
7/// A declarative document that could not be compiled into a contract / config.
8#[derive(Debug, thiserror::Error)]
9pub enum DeclarativeError {
10 /// the YAML text itself does not parse
11 #[error("invalid YAML: {0}")]
12 InvalidYaml(String),
13
14 /// the document parses but is not a mapping with the required envelope
15 #[error("{0}")]
16 InvalidDocument(String),
17
18 /// an `mcVersion` outside the supported set — never best-effort parsed
19 #[error("unsupported mcVersion: {0} (this build supports 2)")]
20 UnsupportedVersion(String),
21
22 /// `mcVersion: 1` — retired; `markdown-contract migrate` rewrites a v1 document
23 #[error(
24 "mcVersion 1 is no longer supported; rewrite the file as mcVersion 2 with `markdown-contract migrate --write <file>` (the CLI of @osdlc/markdown-contract)"
25 )]
26 RetiredVersion,
27
28 /// a `kind` that is neither `contract` nor `config`
29 #[error("kind must be \"contract\" or \"config\" (got {0})")]
30 InvalidKind(String),
31
32 /// a schema node outside the closed v2 vocabulary
33 #[error("{0}")]
34 InvalidSchema(String),
35
36 /// a body-grammar node outside the v2 DSL (or a build-time grammar violation)
37 #[error("{0}")]
38 InvalidBody(String),
39
40 /// a `requires:` / `forbids:` match spec outside the closed vocabulary (or a
41 /// duplicate / contradiction)
42 #[error("{0}")]
43 InvalidTextSpec(String),
44
45 /// a config document outside the v2 DSL
46 #[error("{0}")]
47 InvalidConfig(String),
48
49 /// a config contract ref that is not a `.yaml` / `.yml` file, such as a `.ts` module
50 #[error(
51 "{path}: a contract ref must be a .yaml file (got '{target}'); referencing a code-authored .js/.ts contract is not supported"
52 )]
53 CodeContractRef {
54 /// where the ref sits in the config, such as `rules[0].contract`
55 path: String,
56 /// the file the ref resolves to, after the `contracts` name lookup
57 target: String,
58 },
59
60 /// a referenced contract file could not be read
61 #[error("failed to read contract ref '{target}': {reason}")]
62 ContractRefRead {
63 /// the file the ref resolves to
64 target: String,
65 /// why the read failed
66 reason: String,
67 },
68
69 /// a config or contract file passed to a `*_file` loader could not be read
70 #[error("failed to read '{path}': {reason}")]
71 FileRead {
72 /// the file
73 path: String,
74 /// why the read failed
75 reason: String,
76 },
77}
78
79#[cfg(test)]
80mod tests {
81 use super::DeclarativeError;
82
83 // The variants a host may act on render their messages verbatim.
84 #[test]
85 fn hook_variants_render_their_messages() {
86 let e = DeclarativeError::UnsupportedVersion("3".into());
87 assert_eq!(
88 e.to_string(),
89 "unsupported mcVersion: 3 (this build supports 2)"
90 );
91 let e = DeclarativeError::RetiredVersion;
92 assert_eq!(
93 e.to_string(),
94 "mcVersion 1 is no longer supported; rewrite the file as mcVersion 2 with `markdown-contract migrate --write <file>` (the CLI of @osdlc/markdown-contract)"
95 );
96 let e = DeclarativeError::CodeContractRef {
97 path: "rules[0].contract".into(),
98 target: "./task.contract.ts".into(),
99 };
100 assert!(e.to_string().contains(".js/.ts contract is not supported"));
101 }
102}