use crate::{Exchange, InstanceId, Span};
use std::fmt;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
pub enum OnMalformed {
#[default]
Abort,
Skip,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub struct ParseOptions {
pub on_malformed_record: OnMalformed,
pub check_references: bool,
}
impl ParseOptions {
#[must_use]
pub const fn strict() -> Self {
Self {
on_malformed_record: OnMalformed::Abort,
check_references: false,
}
}
#[must_use]
pub const fn lenient() -> Self {
Self {
on_malformed_record: OnMalformed::Skip,
check_references: false,
}
}
#[must_use]
pub const fn on_malformed_record(mut self, policy: OnMalformed) -> Self {
self.on_malformed_record = policy;
self
}
#[must_use]
pub const fn check_references(mut self, enabled: bool) -> Self {
self.check_references = enabled;
self
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Severity {
#[default]
Warning,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum DiagnosticKind {
SkippedRecord,
DuplicateId,
DanglingReference,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Diagnostic {
severity: Severity,
kind: DiagnosticKind,
span: Span,
instance: Option<InstanceId>,
detail: String,
}
impl Diagnostic {
pub(crate) fn skipped_record(span: Span, detail: impl Into<String>) -> Self {
Self {
severity: Severity::Warning,
kind: DiagnosticKind::SkippedRecord,
span,
instance: None,
detail: detail.into(),
}
}
pub(crate) fn duplicate_id(span: Span, id: InstanceId) -> Self {
Self {
severity: Severity::Warning,
kind: DiagnosticKind::DuplicateId,
span,
detail: format!("duplicate instance id {id}"),
instance: Some(id),
}
}
pub(crate) fn dangling_reference(span: Span, id: InstanceId) -> Self {
Self {
severity: Severity::Warning,
kind: DiagnosticKind::DanglingReference,
span,
detail: format!("reference to undefined instance {id}"),
instance: Some(id),
}
}
#[must_use]
pub const fn severity(&self) -> Severity {
self.severity
}
#[must_use]
pub const fn kind(&self) -> DiagnosticKind {
self.kind
}
#[must_use]
pub const fn instance(&self) -> Option<&InstanceId> {
self.instance.as_ref()
}
#[must_use]
pub const fn span(&self) -> Span {
self.span
}
#[must_use]
pub fn detail(&self) -> &str {
&self.detail
}
}
impl fmt::Display for Diagnostic {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
formatter,
"STEP warning at bytes {}..{}: {}",
self.span.start, self.span.end, self.detail
)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ParseOutcome {
pub exchange: Exchange,
pub diagnostics: Vec<Diagnostic>,
}
impl ParseOutcome {
#[must_use]
pub fn is_lossless(&self) -> bool {
!self
.diagnostics
.iter()
.any(|diagnostic| diagnostic.kind == DiagnosticKind::SkippedRecord)
}
}