use std::{fmt::Debug, fmt::Display};
use crate::{colormap::NumberRange, error::InvalidData, Location};
#[derive(Debug, Clone, PartialEq, thiserror::Error)]
pub enum Reason {
#[error("must be finite")]
NotFinite,
#[error("must be greater than zero")]
NotGreaterThanZero,
#[error("must be a unit vector but {0:?} length is {1}")]
NotUnitVector([f64; 3], f64),
#[error("vectors are not orthogonal: {0:?} {1:?}")]
NotOrthogonal([f64; 3], [f64; 3]),
#[error("sub-block counts {0:?} must be powers of two for octree mode")]
OctreeNotPowerOfTwo([u32; 3]),
#[error("grid count {0:?} exceeds maximum of 4,294,967,295")]
GridTooLarge(Vec<u64>),
#[error("is {0:?} which is not valid on {1} geometry")]
AttrLocationWrongForGeom(Location, &'static str),
#[error("is {0:?} which is not valid on {1} attributes")]
AttrLocationWrongForAttr(Location, &'static str),
#[error("length {0} does not match geometry ({1})")]
AttrLengthMismatch(u64, u64),
#[error("minimum is greater than maximum in {0}")]
MinMaxOutOfOrder(NumberRange),
#[error("array contains invalid data: {0}")]
InvalidData(InvalidData),
#[error("refers to non-existent archive member '{0}'")]
ZipMemberMissing(String),
#[error("must be unique but {0} is repeated")]
NotUnique(String),
#[error("contains duplicate of {0}")]
SoftNotUnique(String),
#[error("{0} more errors")]
MoreErrors(u32),
#[error("{0} more warnings")]
MoreWarnings(u32),
}
impl Reason {
pub fn is_error(&self) -> bool {
!matches!(self, Self::SoftNotUnique(_) | Reason::MoreWarnings(_))
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Problem {
pub reason: Reason,
pub ty: &'static str,
pub field: Option<&'static str>,
pub name: Option<String>,
}
impl Problem {
pub fn is_error(&self) -> bool {
self.reason.is_error()
}
}
impl Display for Problem {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let severity = if self.reason.is_error() {
"Error"
} else {
"Warning"
};
write!(f, "{severity}: '{}", self.ty)?;
if let Some(field) = self.field {
write!(f, "::{field}'")?;
} else {
write!(f, "'")?;
}
write!(f, " {}", self.reason)?;
if let Some(name) = &self.name {
write!(f, ", inside '{name}'")?;
}
Ok(())
}
}
#[derive(Debug, Default, Clone, PartialEq)]
pub struct Problems(Vec<Problem>);
impl Problems {
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn into_vec(self) -> Vec<Problem> {
self.0
}
pub fn iter(&self) -> impl Iterator<Item = &Problem> {
self.0.iter()
}
pub(crate) fn into_result(self) -> Result<Self, Self> {
if self.0.iter().any(|p| p.is_error()) {
Err(self)
} else {
Ok(self)
}
}
pub(crate) fn push(
&mut self,
reason: Reason,
ty: &'static str,
field: Option<&'static str>,
name: Option<String>,
) {
self.0.push(Problem {
reason,
ty,
field,
name,
})
}
}
impl Display for Problems {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let n_errors = self.0.iter().filter(|p| p.reason.is_error()).count();
let n_warnings = self.0.len() - n_errors;
match (n_errors, n_warnings) {
(0, 0) => write!(f, "OMF validation passed")?,
(0, _) => write!(f, "OMF validation passed with warnings:")?,
_ => write!(f, "OMF validation failed:")?,
}
for problem in self {
write!(f, "\n {problem}")?;
}
Ok(())
}
}
impl std::error::Error for Problems {}
impl IntoIterator for Problems {
type Item = Problem;
type IntoIter = std::vec::IntoIter<Problem>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl<'a> IntoIterator for &'a Problems {
type Item = &'a Problem;
type IntoIter = std::slice::Iter<'a, Problem>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
impl From<Problems> for Vec<Problem> {
fn from(value: Problems) -> Self {
value.into_vec()
}
}