1use crate::decode::{DecodeDepth, 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(
74 "the {format} encoder needs a {needed}-depth document; this one records {recorded} depth"
75 )]
76 InsufficientDepth {
77 format: &'static str,
79 needed: DecodeDepth,
81 recorded: DecodeDepth,
83 },
84 #[error("not implemented yet: {0}")]
86 NotImplemented(String),
87 #[error("io error: {0}")]
89 Io(#[from] std::io::Error),
90}
91
92impl From<ResourceLimit> for CodecError {
93 fn from(limit: ResourceLimit) -> Self {
94 Self::ResourceLimit(limit)
95 }
96}
97
98impl CodecError {
99 pub fn malformed(message: impl std::fmt::Display) -> Self {
101 Self::Malformed(message.to_string())
102 }
103
104 pub const fn truncated(location: SourceLocation, operation: &'static str) -> Self {
106 Self::Truncated {
107 location,
108 operation,
109 }
110 }
111}
112
113impl From<TargetRefusal> for CodecError {
114 fn from(refusal: TargetRefusal) -> Self {
115 Self::UnsupportedTarget(Box::new(refusal))
116 }
117}
118
119impl From<crate::text::NamedEntryError> for CodecError {
123 fn from(refused: crate::text::NamedEntryError) -> Self {
124 Self::malformed(refused)
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use std::collections::BTreeMap;
131
132 use super::CodecError;
133 use crate::dialect::{DialectLayers, DialectMatch};
134
135 #[test]
136 fn malformed_constructor_formats_the_message_once() {
137 let error = CodecError::malformed(format_args!("field {} is invalid", 7));
138
139 assert_eq!(error.to_string(), "malformed container: field 7 is invalid");
140 }
141
142 #[test]
143 fn a_dialect_refusal_keeps_the_identification_it_refused() {
144 let error = CodecError::UnsupportedDialect {
145 dialects: Box::new(
146 DialectLayers::of(
147 DialectMatch::refused(crate::dialect_id!("acis:save-format-binary-other"))
148 .with_declared(BTreeMap::from([(
149 crate::nonblank_literal!("save_format"),
150 "700".to_owned(),
151 )])),
152 )
153 .with(DialectMatch::refused(crate::dialect_id!("sat:binary")))
154 .expect("distinct dialect layer keys"),
155 ),
156 message: "save format 700 has no read grammar".into(),
157 };
158
159 assert_eq!(
160 error.to_string(),
161 "unsupported acis dialect acis:save-format-binary-other: save format 700 has no read grammar"
162 );
163 let CodecError::UnsupportedDialect { dialects, .. } = &error else {
164 panic!("the variant just built is the one matched");
165 };
166 assert_eq!(
167 dialects.primary().dialect().as_str(),
168 "acis:save-format-binary-other"
169 );
170 assert_eq!(dialects.primary().declared()["save_format"], "700");
171 assert_eq!(dialects.iter().count(), 2);
172 }
173}