1use std::borrow::Cow;
4use std::fmt;
5use std::rc::Rc;
6use std::sync::mpsc;
7
8use thiserror::Error;
9
10use crate::extensions::registry::ExtensionError;
11use crate::extensions::simple::MissingReference;
12use crate::extensions::{ExtensionRegistry, InsertError, SimpleExtensions};
13
14pub(crate) const NONSPECIFIC: Option<&'static str> = None;
15
16#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
17pub enum Visibility {
18 Never,
20 Required,
22 Always,
24}
25
26#[derive(Debug, Clone)]
28pub struct OutputOptions {
29 pub show_extension_urns: bool,
31 pub show_simple_extensions: bool,
34 pub show_simple_extension_anchors: Visibility,
39 pub show_emit: bool,
41
42 pub read_types: bool,
44 pub literal_types: Visibility,
47 pub nullability: bool,
49 pub indent: String,
51 pub show_literal_binaries: bool,
54 pub virtual_table_multiline_threshold: usize,
58}
59
60impl Default for OutputOptions {
61 fn default() -> Self {
62 Self {
63 show_extension_urns: false,
64 show_simple_extensions: false,
65 show_simple_extension_anchors: Visibility::Required,
66 literal_types: Visibility::Required,
67 show_emit: false,
68 read_types: false,
69 nullability: false,
70 indent: " ".to_string(),
71 show_literal_binaries: false,
72 virtual_table_multiline_threshold: 3,
73 }
74 }
75}
76
77impl OutputOptions {
78 pub fn verbose() -> Self {
81 Self {
82 show_extension_urns: true,
83 show_simple_extensions: true,
84 show_simple_extension_anchors: Visibility::Always,
85 literal_types: Visibility::Always,
86 show_emit: false,
88 read_types: true,
89 nullability: true,
90 indent: " ".to_string(),
91 show_literal_binaries: true,
92 virtual_table_multiline_threshold: 3,
93 }
94 }
95}
96pub(crate) trait ErrorAccumulator: Clone {
97 fn push(&self, e: FormatError);
98}
99
100#[derive(Debug, Clone)]
101pub(crate) struct ErrorQueue {
102 sender: mpsc::Sender<FormatError>,
103 receiver: Rc<mpsc::Receiver<FormatError>>,
104}
105
106impl Default for ErrorQueue {
107 fn default() -> Self {
108 let (sender, receiver) = mpsc::channel();
109 Self {
110 sender,
111 receiver: Rc::new(receiver),
112 }
113 }
114}
115
116impl From<ErrorQueue> for Vec<FormatError> {
117 fn from(v: ErrorQueue) -> Vec<FormatError> {
118 v.receiver.try_iter().collect()
119 }
120}
121
122impl ErrorAccumulator for ErrorQueue {
123 fn push(&self, e: FormatError) {
124 self.sender.send(e).unwrap();
125 }
126}
127
128impl fmt::Display for ErrorQueue {
129 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
130 for (i, e) in self.receiver.try_iter().enumerate() {
131 if i == 0 {
132 writeln!(f, "Warnings during conversion:")?;
133 }
134 let error_number = i + 1;
135 writeln!(f, " - {error_number}: {e}")?;
136 }
137 Ok(())
138 }
139}
140
141impl ErrorQueue {
142 #[cfg(test)]
143 pub(crate) fn errs(self) -> Result<(), ErrorList> {
144 let errors: Vec<FormatError> = self.receiver.try_iter().collect();
145 if errors.is_empty() {
146 Ok(())
147 } else {
148 Err(ErrorList(errors))
149 }
150 }
151}
152
153#[cfg(test)]
155pub(crate) struct ErrorList(pub(crate) Vec<FormatError>);
156
157#[cfg(test)]
158impl ErrorList {
159 pub(crate) fn first(&self) -> &FormatError {
160 self.0
161 .first()
162 .expect("Expected at least one error in ErrorList")
163 }
164
165 pub(crate) fn is_empty(&self) -> bool {
166 self.0.is_empty()
167 }
168}
169
170#[cfg(test)]
171impl fmt::Display for ErrorList {
172 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173 for (i, e) in self.0.iter().enumerate() {
174 if i > 0 {
175 writeln!(f)?;
176 }
177 write!(f, "{e}")?;
178 }
179 Ok(())
180 }
181}
182
183#[cfg(test)]
184impl fmt::Debug for ErrorList {
185 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186 for (i, e) in self.0.iter().enumerate() {
187 if i == 0 {
188 writeln!(f, "Errors:")?;
189 }
190 writeln!(f, "! {e:?}")?;
191 }
192 Ok(())
193 }
194}
195
196#[cfg(test)]
197impl std::error::Error for ErrorList {}
198
199impl<'e> IntoIterator for &'e ErrorQueue {
200 type Item = FormatError;
201 type IntoIter = std::sync::mpsc::TryIter<'e, FormatError>;
202
203 fn into_iter(self) -> Self::IntoIter {
204 self.receiver.try_iter()
205 }
206}
207
208pub(crate) trait IndentTracker {
209 #[allow(dead_code)]
211 fn indent<W: fmt::Write>(&self, w: &mut W) -> fmt::Result;
212 fn push(self) -> Self;
213}
214
215#[derive(Debug, Copy, Clone)]
216pub(crate) struct IndentStack<'a> {
217 count: u32,
218 indent: &'a str,
219}
220
221impl<'a> fmt::Display for IndentStack<'a> {
222 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
223 for _ in 0..self.count {
224 f.write_str(self.indent)?;
225 }
226 Ok(())
227 }
228}
229
230#[derive(Debug, Copy, Clone)]
231pub(crate) struct ScopedContext<'a, Err: ErrorAccumulator> {
232 errors: &'a Err,
233 options: &'a OutputOptions,
234 extensions: &'a SimpleExtensions,
235 indent: IndentStack<'a>,
236 extension_registry: &'a ExtensionRegistry,
237}
238
239impl<'a> IndentStack<'a> {
240 pub(crate) fn new(indent: &'a str) -> Self {
241 Self { count: 0, indent }
242 }
243}
244
245impl<'a> IndentTracker for IndentStack<'a> {
246 fn indent<W: fmt::Write>(&self, w: &mut W) -> fmt::Result {
247 for _ in 0..self.count {
248 w.write_str(self.indent)?;
249 }
250 Ok(())
251 }
252
253 fn push(mut self) -> Self {
254 self.count += 1;
255 self
256 }
257}
258
259impl<'a, Err: ErrorAccumulator> ScopedContext<'a, Err> {
260 pub(crate) fn new(
261 options: &'a OutputOptions,
262 errors: &'a Err,
263 extensions: &'a SimpleExtensions,
264 extension_registry: &'a ExtensionRegistry,
265 ) -> Self {
266 Self {
267 options,
268 errors,
269 extensions,
270 indent: IndentStack::new(options.indent.as_str()),
271 extension_registry,
272 }
273 }
274}
275
276#[derive(Error, Debug, Clone)]
278pub enum FormatError {
279 #[error("Error adding simple extension: {0}")]
282 Insert(#[from] InsertError),
283 #[error("Error finding simple extension: {0}")]
285 Lookup(#[from] MissingReference),
286 #[error("Extension error: {0}")]
288 Extension(#[from] ExtensionError),
289 #[error("Error formatting output: {0}")]
291 Format(#[from] PlanError),
292}
293
294impl FormatError {
295 pub fn message(&self) -> &'static str {
296 match self {
297 FormatError::Lookup(MissingReference::MissingUrn(_)) => "uri",
298 FormatError::Lookup(MissingReference::MissingAnchor(k, _)) => k.name(),
299 FormatError::Lookup(MissingReference::MissingName(k, _)) => k.name(),
300 FormatError::Lookup(MissingReference::Mismatched(k, _, _)) => k.name(),
301 FormatError::Lookup(MissingReference::DuplicateName(k, _)) => k.name(),
302 FormatError::Extension(_) => "extension",
303 FormatError::Format(m) => m.message,
304 FormatError::Insert(InsertError::MissingMappingType) => "extension",
305 FormatError::Insert(InsertError::DuplicateUrnAnchor { .. }) => "uri",
306 FormatError::Insert(InsertError::DuplicateAnchor { .. }) => "extension",
307 FormatError::Insert(InsertError::MissingUrn { .. }) => "uri",
308 FormatError::Insert(InsertError::DuplicateAndMissingUrn { .. }) => "uri",
309 FormatError::Insert(InsertError::InvalidName { .. }) => "extension",
310 }
311 }
312}
313
314#[derive(Debug, Clone)]
315pub struct PlanError {
316 pub message: &'static str,
318 pub lookup: Option<Cow<'static, str>>,
320 pub description: Cow<'static, str>,
322 pub error_type: FormatErrorType,
324}
325
326impl PlanError {
327 pub fn invalid(
328 message: &'static str,
329 specific: Option<impl Into<Cow<'static, str>>>,
330 description: impl Into<Cow<'static, str>>,
331 ) -> Self {
332 Self {
333 message,
334 lookup: specific.map(|s| s.into()),
335 description: description.into(),
336 error_type: FormatErrorType::InvalidValue,
337 }
338 }
339
340 pub fn unimplemented(
341 message: &'static str,
342 specific: Option<impl Into<Cow<'static, str>>>,
343 description: impl Into<Cow<'static, str>>,
344 ) -> Self {
345 Self {
346 message,
347 lookup: specific.map(|s| s.into()),
348 description: description.into(),
349 error_type: FormatErrorType::Unimplemented,
350 }
351 }
352
353 pub fn internal(
354 message: &'static str,
355 specific: Option<impl Into<Cow<'static, str>>>,
356 description: impl Into<Cow<'static, str>>,
357 ) -> Self {
358 Self {
359 message,
360 lookup: specific.map(|s| s.into()),
361 description: description.into(),
362 error_type: FormatErrorType::Internal,
363 }
364 }
365}
366
367#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)]
368pub enum FormatErrorType {
369 InvalidValue,
370 Unimplemented,
371 Internal,
372}
373
374impl fmt::Display for FormatErrorType {
375 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
376 match self {
377 FormatErrorType::InvalidValue => write!(f, "InvalidValue"),
378 FormatErrorType::Unimplemented => write!(f, "Unimplemented"),
379 FormatErrorType::Internal => write!(f, "Internal"),
380 }
381 }
382}
383
384impl std::error::Error for PlanError {}
385
386impl fmt::Display for PlanError {
387 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
388 write!(
389 f,
390 "{} Error writing {}: {}",
391 self.error_type, self.message, self.description
392 )
393 }
394}
395
396#[derive(Debug, Copy, Clone)]
397pub(crate) struct ErrorToken(
399 pub &'static str,
401);
402
403impl fmt::Display for ErrorToken {
404 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
405 write!(f, "!{{{}}}", self.0)
406 }
407}
408
409#[derive(Debug, Copy, Clone)]
410pub(crate) struct MaybeToken<V: fmt::Display>(pub(crate) Result<V, ErrorToken>);
411
412impl<V: fmt::Display> fmt::Display for MaybeToken<V> {
413 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
414 match &self.0 {
415 Ok(t) => t.fmt(f),
416 Err(e) => e.fmt(f),
417 }
418 }
419}
420
421pub(crate) trait Textify {
426 fn textify<S: Scope, W: fmt::Write>(&self, ctx: &S, w: &mut W) -> fmt::Result;
427
428 fn name() -> &'static str;
431}
432
433pub(crate) trait Scope: Sized {
444 type Errors: ErrorAccumulator;
446 type Indent: IndentTracker;
447
448 fn indent(&self) -> impl fmt::Display;
450 fn push_indent(&self) -> Self;
452
453 fn options(&self) -> &OutputOptions;
455 fn extensions(&self) -> &SimpleExtensions;
456
457 fn extension_registry(&self) -> &ExtensionRegistry;
459 fn errors(&self) -> &Self::Errors;
460
461 fn push_error(&self, e: FormatError) {
462 self.errors().push(e);
463 }
464
465 fn failure<E: Into<FormatError>>(&self, e: E) -> ErrorToken {
469 let e = e.into();
470 let token = ErrorToken(e.message());
471 self.push_error(e);
472 token
473 }
474
475 fn expect<'a, T: Textify>(&'a self, t: Option<&'a T>) -> MaybeToken<impl fmt::Display> {
476 match t {
477 Some(t) => MaybeToken(Ok(self.display(t))),
478 None => {
479 let err = PlanError::invalid(
480 T::name(),
481 NONSPECIFIC,
483 "Required field expected, None found",
484 );
485 let err_token = self.failure(err);
486 MaybeToken(Err(err_token))
487 }
488 }
489 }
490
491 fn display<'a, T: Textify>(&'a self, value: &'a T) -> Displayable<'a, Self, T> {
492 Displayable { scope: self, value }
493 }
494
495 fn separated<'a, T: Textify, I: IntoIterator<Item = &'a T> + Clone>(
505 &'a self,
506 items: I,
507 separator: &'static str,
508 ) -> Separated<'a, Self, T, I> {
509 Separated {
510 scope: self,
511 items,
512 separator,
513 }
514 }
515
516 #[allow(dead_code)]
518 fn optional<'a, T: Textify>(
519 &'a self,
520 value: &'a T,
521 option: bool,
522 ) -> OptionalDisplayable<'a, Self, T> {
523 let value = if option { Some(value) } else { None };
524 OptionalDisplayable { scope: self, value }
525 }
526}
527
528#[derive(Clone)]
529pub(crate) struct Separated<'a, S: Scope, T: Textify + 'a, I: IntoIterator<Item = &'a T> + Clone> {
530 scope: &'a S,
531 items: I,
532 separator: &'static str,
533}
534
535impl<'a, S: Scope, T: Textify, I: IntoIterator<Item = &'a T> + Clone> fmt::Display
536 for Separated<'a, S, T, I>
537{
538 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
539 for (i, item) in self.items.clone().into_iter().enumerate() {
540 if i > 0 {
541 f.write_str(self.separator)?;
542 }
543 item.textify(self.scope, f)?;
544 }
545 Ok(())
546 }
547}
548
549impl<'a, S: Scope, T: Textify, I: IntoIterator<Item = &'a T> + Clone + fmt::Debug> fmt::Debug
550 for Separated<'a, S, T, I>
551{
552 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
553 write!(
554 f,
555 "Separated{{items: {:?}, separator: {:?}}}",
556 self.items, self.separator
557 )
558 }
559}
560
561#[derive(Copy, Clone)]
562pub(crate) struct Displayable<'a, S: Scope, T: Textify> {
563 scope: &'a S,
564 value: &'a T,
565}
566
567impl<'a, S: Scope, T: Textify + fmt::Debug> fmt::Debug for Displayable<'a, S, T> {
568 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
569 write!(f, "Displayable({:?})", self.value)
570 }
571}
572
573impl<'a, S: Scope, T: Textify> fmt::Display for Displayable<'a, S, T> {
574 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
575 self.value.textify(self.scope, f)
576 }
577}
578
579#[derive(Copy, Clone)]
580#[allow(dead_code)]
581pub(crate) struct OptionalDisplayable<'a, S: Scope, T: Textify> {
582 scope: &'a S,
583 value: Option<&'a T>,
584}
585
586impl<'a, S: Scope, T: Textify> fmt::Display for OptionalDisplayable<'a, S, T> {
587 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
588 match &self.value {
589 Some(t) => t.textify(self.scope, f),
590 None => Ok(()),
591 }
592 }
593}
594
595impl<'a, S: Scope, T: Textify + fmt::Debug> fmt::Debug for OptionalDisplayable<'a, S, T> {
596 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
597 write!(f, "OptionalDisplayable({:?})", self.value)
598 }
599}
600
601impl<'a, Err: ErrorAccumulator> Scope for ScopedContext<'a, Err> {
602 type Errors = Err;
603 type Indent = IndentStack<'a>;
604
605 fn indent(&self) -> impl fmt::Display {
606 self.indent
607 }
608
609 fn push_indent(&self) -> Self {
610 Self {
611 indent: self.indent.push(),
612 ..*self
613 }
614 }
615
616 fn options(&self) -> &OutputOptions {
617 self.options
618 }
619
620 fn errors(&self) -> &Self::Errors {
621 self.errors
622 }
623
624 fn extensions(&self) -> &SimpleExtensions {
625 self.extensions
626 }
627
628 fn extension_registry(&self) -> &ExtensionRegistry {
629 self.extension_registry
630 }
631}