1use crate::decode::{ResourceLimit, SourceLocation};
5use crate::dialect::DialectLayers;
6use crate::target::TargetRefusal;
7
8#[derive(Debug, thiserror::Error)]
13#[non_exhaustive]
14pub enum CodecError {
15 #[error("not the expected format: {0}")]
17 WrongFormat(String),
18 #[error("malformed container: {0}")]
20 Malformed(String),
21 #[error("invalid encoder input: {0}")]
23 InvalidInput(String),
24 #[error(
29 "truncated input during {} at space {} offset {}",
30 .operation, .location.space.index(), .location.offset
31 )]
32 Truncated {
33 location: SourceLocation,
35 operation: &'static str,
37 },
38 #[error(
43 "resource limit on {:?}: {:?} (limit {}, used {}, requested {})",
44 .0.dimension, .0.reason, .0.limit, .0.used, .0.additional
45 )]
46 ResourceLimit(ResourceLimit),
47 #[error("unsupported {} dialect {}: {message}", .dialects.primary().format(), .dialects.primary().dialect())]
63 UnsupportedDialect {
64 dialects: Box<DialectLayers>,
66 message: String,
68 },
69 #[error("{0}")]
71 UnsupportedTarget(Box<TargetRefusal>),
72 #[error("not implemented yet: {0}")]
74 NotImplemented(String),
75 #[error("io error: {0}")]
77 Io(#[from] std::io::Error),
78}
79
80impl From<ResourceLimit> for CodecError {
81 fn from(limit: ResourceLimit) -> Self {
82 Self::ResourceLimit(limit)
83 }
84}
85
86impl CodecError {
87 pub fn malformed(message: impl std::fmt::Display) -> Self {
89 Self::Malformed(message.to_string())
90 }
91
92 pub const fn truncated(location: SourceLocation, operation: &'static str) -> Self {
94 Self::Truncated {
95 location,
96 operation,
97 }
98 }
99}
100
101impl From<TargetRefusal> for CodecError {
102 fn from(refusal: TargetRefusal) -> Self {
103 Self::UnsupportedTarget(Box::new(refusal))
104 }
105}
106
107impl From<crate::text::NamedEntryError> for CodecError {
111 fn from(refused: crate::text::NamedEntryError) -> Self {
112 match refused {
113 crate::text::NamedEntryError::ResourceRefusal(limit) => Self::ResourceLimit(limit),
114 refused => Self::malformed(refused),
115 }
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use std::collections::BTreeMap;
122
123 use super::CodecError;
124 use crate::dialect::{DialectLayers, DialectMatch};
125
126 #[test]
127 fn malformed_constructor_formats_the_message_once() {
128 let error = CodecError::malformed(format_args!("field {} is invalid", 7));
129
130 assert_eq!(error.to_string(), "malformed container: field 7 is invalid");
131 }
132
133 #[test]
134 fn a_dialect_refusal_keeps_the_identification_it_refused() {
135 let error = CodecError::UnsupportedDialect {
136 dialects: Box::new(
137 DialectLayers::of(
138 DialectMatch::refused(crate::dialect_id!("acis:save-format-binary-other"))
139 .with_declared(BTreeMap::from([(
140 crate::nonblank_literal!("save_format"),
141 "700".to_owned(),
142 )])),
143 )
144 .with(DialectMatch::refused(crate::dialect_id!("sat:binary")))
145 .expect("distinct dialect layer keys"),
146 ),
147 message: "save format 700 has no read grammar".into(),
148 };
149
150 assert_eq!(
151 error.to_string(),
152 "unsupported acis dialect acis:save-format-binary-other: save format 700 has no read grammar"
153 );
154 let CodecError::UnsupportedDialect { dialects, .. } = &error else {
155 panic!("the variant just built is the one matched");
156 };
157 assert_eq!(
158 dialects.primary().dialect().as_str(),
159 "acis:save-format-binary-other"
160 );
161 assert_eq!(dialects.primary().declared()["save_format"], "700");
162 assert_eq!(dialects.iter().count(), 2);
163 }
164}