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