1use std::path::PathBuf;
5
6#[derive(Debug, thiserror::Error)]
8#[non_exhaustive]
9pub enum Error {
10 #[error("reading {path}: {source}")]
11 Io {
12 path: PathBuf,
13 #[source]
14 source: std::io::Error,
15 },
16
17 #[error("{path}: {source}")]
19 Config {
20 path: PathBuf,
21 #[source]
22 source: toml::de::Error,
23 },
24
25 #[error(
29 "externals.relations: \"{from}\" (under external \"{external}\") names no element in the surveyed model"
30 )]
31 UnknownRelationSource { external: String, from: String },
32
33 #[error(
35 "externals: id \"{id}\" is not an identifier (letters, digits and _, not starting with a digit, not a keyword)"
36 )]
37 InvalidExternalId { id: String },
38
39 #[error(
42 "externals: \"{id}\" declares kind \"{kind}\", which the survey generates ({surveyed}); give an external its own kind"
43 )]
44 ExternalKindIsSurveyed {
45 id: String,
46 kind: String,
47 surveyed: String,
48 },
49
50 #[error("element ids \"{first}\" and \"{second}\" both become the LikeC4 id \"{id}\"")]
53 DuplicateId {
54 id: String,
55 first: String,
56 second: String,
57 },
58
59 #[error("theme: \"{kind}\" is not an element kind of the surveyed model (have {have})")]
62 UnknownThemeKind { kind: String, have: String },
63
64 #[error("{path}: {entry} is not a top-level entry asbuilt reads (tables: {known})")]
70 UnknownTopLevel {
71 path: PathBuf,
72 entry: String,
73 known: String,
74 },
75
76 #[error("no supported stack detected at {root}")]
78 NoFrontend { root: PathBuf },
79
80 #[error("{frontend} front-end: {source}")]
82 Frontend {
83 frontend: String,
84 #[source]
85 source: Box<dyn std::error::Error + Send + Sync + 'static>,
86 },
87}
88
89pub type Result<T> = std::result::Result<T, Error>;
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 #[test]
96 fn an_unknown_relation_source_names_the_external_and_the_from() {
97 let msg = Error::UnknownRelationSource {
98 external: "node_driver".into(),
99 from: "playwright_rs.nope".into(),
100 }
101 .to_string();
102 assert!(msg.contains("node_driver"), "{msg}");
103 assert!(msg.contains("playwright_rs.nope"), "{msg}");
104 }
105
106 #[test]
107 fn a_duplicate_id_names_both_originals_and_the_collision() {
108 let msg = Error::DuplicateId {
109 id: "foo_bar".into(),
110 first: "foo-bar".into(),
111 second: "foo_bar".into(),
112 }
113 .to_string();
114 assert!(
115 msg.contains("\"foo-bar\"") && msg.contains("\"foo_bar\""),
116 "{msg}"
117 );
118 }
119}