hwpforge_smithy_md/
error.rs1use std::fmt;
4
5#[derive(Debug, thiserror::Error)]
7#[non_exhaustive]
8pub enum MdError {
9 #[error("invalid YAML frontmatter: {detail}")]
11 InvalidFrontmatter {
12 detail: String,
14 },
15
16 #[error("frontmatter block started with '---' but no closing marker was found")]
18 FrontmatterUnclosed,
19
20 #[error("template resolution failed: {detail}")]
22 TemplateResolution {
23 detail: String,
25 },
26
27 #[error("unsupported markdown structure: {detail}")]
29 UnsupportedStructure {
30 detail: String,
32 },
33
34 #[error("invalid lossless body: {detail}")]
36 LosslessParse {
37 detail: String,
39 },
40
41 #[error("missing required attribute '{attribute}' on <{element}>")]
43 LosslessMissingAttribute {
44 element: &'static str,
46 attribute: &'static str,
48 },
49
50 #[error("invalid attribute '{attribute}' on <{element}>: {value}")]
52 LosslessInvalidAttribute {
53 element: &'static str,
55 attribute: &'static str,
57 value: String,
59 },
60
61 #[error("file too large: {size} bytes exceeds {limit} byte limit")]
63 FileTooLarge {
64 size: u64,
66 limit: u64,
68 },
69
70 #[error("I/O error: {0}")]
72 Io(#[from] std::io::Error),
73
74 #[error("core error: {0}")]
76 Core(#[from] hwpforge_core::CoreError),
77
78 #[error("blueprint error: {0}")]
80 Blueprint(#[from] hwpforge_blueprint::error::BlueprintError),
81
82 #[error("foundation error: {0}")]
84 Foundation(#[from] hwpforge_foundation::FoundationError),
85}
86
87#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
89#[non_exhaustive]
90pub enum MdErrorCode {
91 InvalidFrontmatter = 6000,
93 FrontmatterUnclosed = 6001,
95 TemplateResolution = 6002,
97 UnsupportedStructure = 6003,
99 LosslessParse = 6008,
101 LosslessMissingAttribute = 6009,
103 LosslessInvalidAttribute = 6010,
105 FileTooLarge = 6011,
107 Io = 6004,
109 Core = 6005,
111 Blueprint = 6006,
113 Foundation = 6007,
115}
116
117impl fmt::Display for MdErrorCode {
118 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119 write!(f, "E{}", *self as u16)
120 }
121}
122
123impl MdError {
124 pub fn code(&self) -> MdErrorCode {
126 match self {
127 Self::InvalidFrontmatter { .. } => MdErrorCode::InvalidFrontmatter,
128 Self::FrontmatterUnclosed => MdErrorCode::FrontmatterUnclosed,
129 Self::TemplateResolution { .. } => MdErrorCode::TemplateResolution,
130 Self::UnsupportedStructure { .. } => MdErrorCode::UnsupportedStructure,
131 Self::LosslessParse { .. } => MdErrorCode::LosslessParse,
132 Self::LosslessMissingAttribute { .. } => MdErrorCode::LosslessMissingAttribute,
133 Self::LosslessInvalidAttribute { .. } => MdErrorCode::LosslessInvalidAttribute,
134 Self::FileTooLarge { .. } => MdErrorCode::FileTooLarge,
135 Self::Io(_) => MdErrorCode::Io,
136 Self::Core(_) => MdErrorCode::Core,
137 Self::Blueprint(_) => MdErrorCode::Blueprint,
138 Self::Foundation(_) => MdErrorCode::Foundation,
139 }
140 }
141}
142
143pub type MdResult<T> = Result<T, MdError>;
145
146#[cfg(test)]
147mod tests {
148 use super::*;
149
150 #[test]
151 fn code_display_format() {
152 assert_eq!(MdErrorCode::InvalidFrontmatter.to_string(), "E6000");
153 assert_eq!(MdErrorCode::LosslessParse.to_string(), "E6008");
154 assert_eq!(MdErrorCode::Foundation.to_string(), "E6007");
155 }
156
157 #[test]
158 fn code_mapping_for_frontmatter() {
159 let err = MdError::FrontmatterUnclosed;
160 assert_eq!(err.code(), MdErrorCode::FrontmatterUnclosed);
161 }
162
163 #[test]
164 fn unsupported_structure_variant_has_code() {
165 let err = MdError::UnsupportedStructure { detail: "definition list".to_string() };
166 assert_eq!(err.code(), MdErrorCode::UnsupportedStructure);
167 }
168
169 #[test]
170 fn lossless_attribute_error_code_mapping() {
171 let err = MdError::LosslessMissingAttribute { element: "img", attribute: "src" };
172 assert_eq!(err.code(), MdErrorCode::LosslessMissingAttribute);
173 }
174
175 #[test]
176 fn file_too_large_error_code_and_display() {
177 let err = MdError::FileTooLarge { size: 100_000_000, limit: 50_000_000 };
178 assert_eq!(err.code(), MdErrorCode::FileTooLarge);
179 assert!(err.to_string().contains("100000000"));
180 assert!(err.to_string().contains("50000000"));
181 }
182}