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