use std::borrow::Cow;
use std::fmt;
use std::rc::Rc;
use std::sync::mpsc;
use thiserror::Error;
use crate::extensions::registry::ExtensionError;
use crate::extensions::simple::MissingReference;
use crate::extensions::{ExtensionRegistry, InsertError, SimpleExtensions};
pub const NONSPECIFIC: Option<&'static str> = None;
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Visibility {
Never,
Required,
Always,
}
#[derive(Debug, Clone)]
pub struct OutputOptions {
pub show_extension_urns: bool,
pub show_simple_extensions: bool,
pub show_simple_extension_anchors: Visibility,
pub show_emit: bool,
pub read_types: bool,
pub literal_types: Visibility,
pub fn_types: bool,
pub nullability: bool,
pub indent: String,
pub show_literal_binaries: bool,
}
impl Default for OutputOptions {
fn default() -> Self {
Self {
show_extension_urns: false,
show_simple_extensions: false,
show_simple_extension_anchors: Visibility::Required,
literal_types: Visibility::Required,
show_emit: false,
read_types: false,
fn_types: false,
nullability: false,
indent: " ".to_string(),
show_literal_binaries: false,
}
}
}
impl OutputOptions {
pub fn verbose() -> Self {
Self {
show_extension_urns: true,
show_simple_extensions: true,
show_simple_extension_anchors: Visibility::Always,
literal_types: Visibility::Always,
show_emit: false,
read_types: true,
fn_types: true,
nullability: true,
indent: " ".to_string(),
show_literal_binaries: true,
}
}
}
pub trait ErrorAccumulator: Clone {
fn push(&self, e: FormatError);
}
#[derive(Debug, Clone)]
pub struct ErrorQueue {
sender: mpsc::Sender<FormatError>,
receiver: Rc<mpsc::Receiver<FormatError>>,
}
impl Default for ErrorQueue {
fn default() -> Self {
let (sender, receiver) = mpsc::channel();
Self {
sender,
receiver: Rc::new(receiver),
}
}
}
impl From<ErrorQueue> for Vec<FormatError> {
fn from(v: ErrorQueue) -> Vec<FormatError> {
v.receiver.try_iter().collect()
}
}
impl ErrorAccumulator for ErrorQueue {
fn push(&self, e: FormatError) {
self.sender.send(e).unwrap();
}
}
impl fmt::Display for ErrorQueue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (i, e) in self.receiver.try_iter().enumerate() {
if i == 0 {
writeln!(f, "Warnings during conversion:")?;
}
let error_number = i + 1;
writeln!(f, " - {error_number}: {e}")?;
}
Ok(())
}
}
impl ErrorQueue {
pub fn errs(self) -> Result<(), ErrorList> {
let errors: Vec<FormatError> = self.receiver.try_iter().collect();
if errors.is_empty() {
Ok(())
} else {
Err(ErrorList(errors))
}
}
}
pub struct ErrorList(pub Vec<FormatError>);
impl ErrorList {
pub fn first(&self) -> &FormatError {
self.0
.first()
.expect("Expected at least one error in ErrorList")
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
}
impl fmt::Display for ErrorList {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (i, e) in self.0.iter().enumerate() {
if i > 0 {
writeln!(f)?;
}
write!(f, "{e}")?;
}
Ok(())
}
}
impl fmt::Debug for ErrorList {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (i, e) in self.0.iter().enumerate() {
if i == 0 {
writeln!(f, "Errors:")?;
}
writeln!(f, "! {e:?}")?;
}
Ok(())
}
}
impl std::error::Error for ErrorList {}
impl<'e> IntoIterator for &'e ErrorQueue {
type Item = FormatError;
type IntoIter = std::sync::mpsc::TryIter<'e, FormatError>;
fn into_iter(self) -> Self::IntoIter {
self.receiver.try_iter()
}
}
pub trait IndentTracker {
#[allow(dead_code)]
fn indent<W: fmt::Write>(&self, w: &mut W) -> fmt::Result;
fn push(self) -> Self;
}
#[derive(Debug, Copy, Clone)]
pub struct IndentStack<'a> {
count: u32,
indent: &'a str,
}
impl<'a> fmt::Display for IndentStack<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for _ in 0..self.count {
f.write_str(self.indent)?;
}
Ok(())
}
}
#[derive(Debug, Copy, Clone)]
pub struct ScopedContext<'a, Err: ErrorAccumulator> {
errors: &'a Err,
options: &'a OutputOptions,
extensions: &'a SimpleExtensions,
indent: IndentStack<'a>,
extension_registry: &'a ExtensionRegistry,
}
impl<'a> IndentStack<'a> {
pub fn new(indent: &'a str) -> Self {
Self { count: 0, indent }
}
}
impl<'a> IndentTracker for IndentStack<'a> {
fn indent<W: fmt::Write>(&self, w: &mut W) -> fmt::Result {
for _ in 0..self.count {
w.write_str(self.indent)?;
}
Ok(())
}
fn push(mut self) -> Self {
self.count += 1;
self
}
}
impl<'a, Err: ErrorAccumulator> ScopedContext<'a, Err> {
pub fn new(
options: &'a OutputOptions,
errors: &'a Err,
extensions: &'a SimpleExtensions,
extension_registry: &'a ExtensionRegistry,
) -> Self {
Self {
options,
errors,
extensions,
indent: IndentStack::new(options.indent.as_str()),
extension_registry,
}
}
}
#[derive(Error, Debug, Clone)]
pub enum FormatError {
#[error("Error adding simple extension: {0}")]
Insert(#[from] InsertError),
#[error("Error finding simple extension: {0}")]
Lookup(#[from] MissingReference),
#[error("Extension error: {0}")]
Extension(#[from] ExtensionError),
#[error("Error formatting output: {0}")]
Format(#[from] PlanError),
}
impl FormatError {
pub fn message(&self) -> &'static str {
match self {
FormatError::Lookup(MissingReference::MissingUrn(_)) => "uri",
FormatError::Lookup(MissingReference::MissingAnchor(k, _)) => k.name(),
FormatError::Lookup(MissingReference::MissingName(k, _)) => k.name(),
FormatError::Lookup(MissingReference::Mismatched(k, _, _)) => k.name(),
FormatError::Lookup(MissingReference::DuplicateName(k, _)) => k.name(),
FormatError::Extension(_) => "extension",
FormatError::Format(m) => m.message,
FormatError::Insert(InsertError::MissingMappingType) => "extension",
FormatError::Insert(InsertError::DuplicateUrnAnchor { .. }) => "uri",
FormatError::Insert(InsertError::DuplicateAnchor { .. }) => "extension",
FormatError::Insert(InsertError::MissingUrn { .. }) => "uri",
FormatError::Insert(InsertError::DuplicateAndMissingUrn { .. }) => "uri",
}
}
}
#[derive(Debug, Clone)]
pub struct PlanError {
pub message: &'static str,
pub lookup: Option<Cow<'static, str>>,
pub description: Cow<'static, str>,
pub error_type: FormatErrorType,
}
impl PlanError {
pub fn invalid(
message: &'static str,
specific: Option<impl Into<Cow<'static, str>>>,
description: impl Into<Cow<'static, str>>,
) -> Self {
Self {
message,
lookup: specific.map(|s| s.into()),
description: description.into(),
error_type: FormatErrorType::InvalidValue,
}
}
pub fn unimplemented(
message: &'static str,
specific: Option<impl Into<Cow<'static, str>>>,
description: impl Into<Cow<'static, str>>,
) -> Self {
Self {
message,
lookup: specific.map(|s| s.into()),
description: description.into(),
error_type: FormatErrorType::Unimplemented,
}
}
pub fn internal(
message: &'static str,
specific: Option<impl Into<Cow<'static, str>>>,
description: impl Into<Cow<'static, str>>,
) -> Self {
Self {
message,
lookup: specific.map(|s| s.into()),
description: description.into(),
error_type: FormatErrorType::Internal,
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)]
pub enum FormatErrorType {
InvalidValue,
Unimplemented,
Internal,
}
impl fmt::Display for FormatErrorType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FormatErrorType::InvalidValue => write!(f, "InvalidValue"),
FormatErrorType::Unimplemented => write!(f, "Unimplemented"),
FormatErrorType::Internal => write!(f, "Internal"),
}
}
}
impl std::error::Error for PlanError {}
impl fmt::Display for PlanError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{} Error writing {}: {}",
self.error_type, self.message, self.description
)
}
}
#[derive(Debug, Copy, Clone)]
pub struct ErrorToken(
pub &'static str,
);
impl fmt::Display for ErrorToken {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "!{{{}}}", self.0)
}
}
#[derive(Debug, Copy, Clone)]
pub struct MaybeToken<V: fmt::Display>(pub Result<V, ErrorToken>);
impl<V: fmt::Display> fmt::Display for MaybeToken<V> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.0 {
Ok(t) => t.fmt(f),
Err(e) => e.fmt(f),
}
}
}
pub trait Textify {
fn textify<S: Scope, W: fmt::Write>(&self, ctx: &S, w: &mut W) -> fmt::Result;
fn name() -> &'static str;
}
pub trait Scope: Sized {
type Errors: ErrorAccumulator;
type Indent: IndentTracker;
fn indent(&self) -> impl fmt::Display;
fn push_indent(&self) -> Self;
fn options(&self) -> &OutputOptions;
fn extensions(&self) -> &SimpleExtensions;
fn extension_registry(&self) -> &ExtensionRegistry;
fn errors(&self) -> &Self::Errors;
fn push_error(&self, e: FormatError) {
self.errors().push(e);
}
fn failure<E: Into<FormatError>>(&self, e: E) -> ErrorToken {
let e = e.into();
let token = ErrorToken(e.message());
self.push_error(e);
token
}
fn expect<'a, T: Textify>(&'a self, t: Option<&'a T>) -> MaybeToken<impl fmt::Display> {
match t {
Some(t) => MaybeToken(Ok(self.display(t))),
None => {
let err = PlanError::invalid(
T::name(),
NONSPECIFIC,
"Required field expected, None found",
);
let err_token = self.failure(err);
MaybeToken(Err(err_token))
}
}
}
fn expect_ok<'a, T: Textify, E: Into<FormatError>>(
&'a self,
result: Result<&'a T, E>,
) -> MaybeToken<impl fmt::Display + 'a> {
MaybeToken(match result {
Ok(t) => Ok(self.display(t)),
Err(e) => Err(self.failure(e)),
})
}
fn display<'a, T: Textify>(&'a self, value: &'a T) -> Displayable<'a, Self, T> {
Displayable { scope: self, value }
}
fn separated<'a, T: Textify, I: IntoIterator<Item = &'a T> + Clone>(
&'a self,
items: I,
separator: &'static str,
) -> Separated<'a, Self, T, I> {
Separated {
scope: self,
items,
separator,
}
}
fn option<'a, T: Textify>(&'a self, value: Option<&'a T>) -> OptionalDisplayable<'a, Self, T> {
OptionalDisplayable { scope: self, value }
}
fn optional<'a, T: Textify>(
&'a self,
value: &'a T,
option: bool,
) -> OptionalDisplayable<'a, Self, T> {
let value = if option { Some(value) } else { None };
OptionalDisplayable { scope: self, value }
}
}
#[derive(Clone)]
pub struct Separated<'a, S: Scope, T: Textify + 'a, I: IntoIterator<Item = &'a T> + Clone> {
scope: &'a S,
items: I,
separator: &'static str,
}
impl<'a, S: Scope, T: Textify, I: IntoIterator<Item = &'a T> + Clone> fmt::Display
for Separated<'a, S, T, I>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (i, item) in self.items.clone().into_iter().enumerate() {
if i > 0 {
f.write_str(self.separator)?;
}
item.textify(self.scope, f)?;
}
Ok(())
}
}
impl<'a, S: Scope, T: Textify, I: IntoIterator<Item = &'a T> + Clone + fmt::Debug> fmt::Debug
for Separated<'a, S, T, I>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Separated{{items: {:?}, separator: {:?}}}",
self.items, self.separator
)
}
}
#[derive(Copy, Clone)]
pub struct Displayable<'a, S: Scope, T: Textify> {
scope: &'a S,
value: &'a T,
}
impl<'a, S: Scope, T: Textify + fmt::Debug> fmt::Debug for Displayable<'a, S, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Displayable({:?})", self.value)
}
}
impl<'a, S: Scope, T: Textify> Displayable<'a, S, T> {
pub fn new(scope: &'a S, value: &'a T) -> Self {
Self { scope, value }
}
pub fn optional(self, option: bool) -> OptionalDisplayable<'a, S, T> {
let value = if option { Some(self.value) } else { None };
OptionalDisplayable {
scope: self.scope,
value,
}
}
}
impl<'a, S: Scope, T: Textify> fmt::Display for Displayable<'a, S, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.value.textify(self.scope, f)
}
}
#[derive(Copy, Clone)]
pub struct OptionalDisplayable<'a, S: Scope, T: Textify> {
scope: &'a S,
value: Option<&'a T>,
}
impl<'a, S: Scope, T: Textify> fmt::Display for OptionalDisplayable<'a, S, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.value {
Some(t) => t.textify(self.scope, f),
None => Ok(()),
}
}
}
impl<'a, S: Scope, T: Textify + fmt::Debug> fmt::Debug for OptionalDisplayable<'a, S, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "OptionalDisplayable({:?})", self.value)
}
}
impl<'a, Err: ErrorAccumulator> Scope for ScopedContext<'a, Err> {
type Errors = Err;
type Indent = IndentStack<'a>;
fn indent(&self) -> impl fmt::Display {
self.indent
}
fn push_indent(&self) -> Self {
Self {
indent: self.indent.push(),
..*self
}
}
fn options(&self) -> &OutputOptions {
self.options
}
fn errors(&self) -> &Self::Errors {
self.errors
}
fn extensions(&self) -> &SimpleExtensions {
self.extensions
}
fn extension_registry(&self) -> &ExtensionRegistry {
self.extension_registry
}
}