Skip to content

Commit

Permalink
Недоделанная попытка добавить пути к ошибкам
Browse files Browse the repository at this point in the history
  • Loading branch information
Mingun committed Jul 28, 2024
1 parent ffa561d commit cb8e01f
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 35 deletions.
40 changes: 34 additions & 6 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,49 @@
//! Contains errors that can occurs when creating kaitai struct model
use std::borrow::Cow;
use std::convert::Infallible;
use std::convert::{Infallible, TryInto};
use std::error::Error;
use std::fmt::{Display, Formatter, Result};
use std::fmt::{self, Display, Formatter};

use peg::error::ParseError;
use peg::str::LineCol;

/// Path to attribute in YAML
#[derive(Default, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct YamlPath(Vec<String>);
impl YamlPath {
pub fn extend<I: IntoIterator>(&self, segments: I) -> Self
where I::Item: Into<String>,
{
let mut path = self.0.clone();
path.extend(segments.into_iter().map(Into::into));
Self(path)
}
pub fn validate<T, R>(self, value: T) -> Result<R, ModelError>
where T: TryInto<R, Error = Cow<'static, str>>
{
value.try_into().map_err(|e| ModelError::Validation(self, e))
}
}
impl Display for YamlPath {
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
for segment in &self.0 {
write!(fmt, "/{}", segment)?;
}
Ok(())
}
}

pub type ValidationError = Cow<'static, str>;

/// Possible errors when creating kaitai struct model from YAML representation
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ModelError {
/// Parser error of incorrect expression in field
Expression(ParseError<LineCol>),//TODO: Add information about field
/// Error of validating schema rules, such as absence of mandatory fields or
/// excess fields.
Validation(Cow<'static, str>),
Validation(YamlPath, ValidationError),
}
impl From<ParseError<LineCol>> for ModelError {
fn from(error: ParseError<LineCol>) -> Self { Self::Expression(error) }
Expand All @@ -26,12 +54,12 @@ impl From<Infallible> for ModelError {
}

impl Display for ModelError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
use ModelError::*;

match self {
Expression(err) => write!(f, "incorrect expression: {}", err),
Validation(err) => write!(f, "invalid schema: {}", err),
Validation(path, err) => write!(f, "{}: invalid schema: {}", path, err),
}
}
}
Expand All @@ -42,7 +70,7 @@ impl Error for ModelError {

match self {
Expression(err) => Some(err),
Validation(_) => None,
Validation(..) => None,
}
}
}
10 changes: 5 additions & 5 deletions src/model/expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use bigdecimal::num_bigint::BigInt;
use bigdecimal::BigDecimal;
use serde_yml::Number;

use crate::error::ModelError;
use crate::error::{ModelError, YamlPath};
use crate::model::{EnumName, EnumValueName, FieldName, TypeName as TName};
use crate::parser::expressions::{
parse_single, BinaryOp, Node, Scope, SpecialName, TypeName, TypeRef, UnaryOp,
Expand Down Expand Up @@ -218,15 +218,15 @@ impl From<Number> for OwningNode {
Self::validate(Node::from(number))
}
}
impl TryFrom<Scalar> for OwningNode {
impl TryFrom<(YamlPath, Scalar)> for OwningNode {
type Error = ModelError;

fn try_from(scalar: Scalar) -> Result<Self, Self::Error> {
fn try_from(scalar: (YamlPath, Scalar)) -> Result<Self, Self::Error> {
use ModelError::*;
use Scalar::*;

match scalar {
Null => Err(Validation(
match scalar.1 {
Null => Err(Validation(scalar.0,
"Expected expression, but null found (note that `null` literal in YAML is \
equivalent of absence of any value, use 'null' if you want to refer to name `null`)".into())),
Bool(val) => Ok(Self::Bool(val)),
Expand Down
51 changes: 29 additions & 22 deletions src/model/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use indexmap::{indexmap, IndexMap};
use lazy_static::lazy_static;
use regex::Regex;

use crate::error::ModelError;
use crate::error::{ModelError, ValidationError, YamlPath};
use crate::model::expressions::OwningNode;
use crate::parser as p;
use crate::parser::expressions::{parse_process, parse_type_ref, AttrType};
Expand All @@ -35,6 +35,7 @@ pub use name::*;
/// contains helper structures, that contains only necessary subset of fields.
/// Advantages over just unnamed tuples is names of fields.
mod helpers {
use crate::error::YamlPath;
use crate::parser as p;

/// Wrapper for inheritable values
Expand Down Expand Up @@ -86,6 +87,8 @@ mod helpers {
pub eos_error: Option<bool>,

pub pad_right: Option<u8>,

pub path: YamlPath,
}

/// Transitional structure, that contains all data from parser structure,
Expand All @@ -97,6 +100,7 @@ mod helpers {
pub encoding: Inheritable<String>,
pub endian: Option<p::Variant<p::ByteOrder>>,
pub bit_endian: Option<p::BitOrder>,
pub path: YamlPath,
}
}

Expand Down Expand Up @@ -203,23 +207,24 @@ pub enum Variant<T> {
cases: IndexMap<OwningNode, T>,
},
}
impl<T, U: TryInto<T>> TryFrom<p::Variant<U>> for Variant<T>
impl<T, U: TryInto<T>> TryFrom<(YamlPath, p::Variant<U>)> for Variant<T>
where U::Error: Into<ModelError>,
{
type Error = ModelError;

fn try_from(data: p::Variant<U>) -> Result<Self, Self::Error> {
fn try_from(data: (YamlPath, p::Variant<U>)) -> Result<Self, Self::Error> {
use p::Variant::*;

match data {
match data.1 {
Fixed(val) => Ok(Variant::Fixed(val.try_into().map_err(Into::into)?)),
Choice { switch_on, cases } => {
let mut new_cases = IndexMap::with_capacity(cases.len());
for (k, v) in cases.into_iter() {
new_cases.insert(k.try_into()?, v.try_into().map_err(Into::into)?);
let path = data.0.extend(vec!["cases"]);//TODO: path to case
new_cases.insert((path, k).try_into()?, v.try_into().map_err(Into::into)?);
}
Ok(Variant::Choice {
switch_on: switch_on.try_into()?,
switch_on: (data.0.extend(vec!["switch_on"]), switch_on).try_into()?,
cases: new_cases,
})
}
Expand Down Expand Up @@ -304,16 +309,16 @@ impl Repeat {
},

#[cfg(feature = "compatible")]
(None, Some(_), None) => Err(Validation("missed `repeat: expr`".into())),
(None, Some(_), None) => Err(Validation(data.path, "missed `repeat: expr`".into())),
#[cfg(feature = "compatible")]
(None, None, Some(_)) => Err(Validation("missed `repeat: until`".into())),
(None, None, Some(_)) => Err(Validation(data.path, "missed `repeat: until`".into())),

(Some(Expr), None, _) => Err(Validation("missed `repeat-expr`".into())),
(Some(Until), _, None) => Err(Validation("missed `repeat-until`".into())),
(Some(Expr), None, _) => Err(Validation(data.path, "missed `repeat-expr`".into())),
(Some(Until), _, None) => Err(Validation(data.path, "missed `repeat-until`".into())),

(_, Some(_), Some(_)) => Err(Validation("either `repeat-expr` or `repeat-until` must be specified".into())),
(Some(_), _, Some(_)) => Err(Validation("`repeat-until` requires `repeat: until`".into())),
(Some(_), Some(_), _) => Err(Validation("`repeat-expr` requires `repeat: expr`".into())),
(_, Some(_), Some(_)) => Err(Validation(data.path, "either `repeat-expr` or `repeat-until` must be specified".into())),
(Some(_), _, Some(_)) => Err(Validation(data.path, "`repeat-until` requires `repeat: until`".into())),
(Some(_), Some(_), _) => Err(Validation(data.path, "`repeat-expr` requires `repeat: expr`".into())),
}
}
}
Expand Down Expand Up @@ -462,7 +467,7 @@ impl Size {
mandatory: mandatory.unwrap_or(true),
}),
// TODO: Emit warning instead here, but error also an option until warnings is not implemented
//(None, ..) => return Err(Validation("`consume`, `include` or `eos-error` has no effect without `terminator`".into())),
//(None, ..) => return Err(Validation(data.path, "`consume`, `include` or `eos-error` has no effect without `terminator`".into())),
(None, ..) => None,
};

Expand Down Expand Up @@ -501,11 +506,11 @@ impl Size {
(None, false, Some(t)) => Ok(Self::Until(t)),
// TODO: Warning or even error, if natural type size is less that size
(Some(size), false, until) => Ok(Self::Exact { count: Count::validate(size)?, until }),
(Some(_), true, _) => Err(Validation("only one of `size` or `size-eos: true` must be specified".into())),
(Some(_), true, _) => Err(Validation(data.path, "only one of `size` or `size-eos: true` must be specified".into())),
(None, false, None) => match type_ref.sizeof() {
// For unknown sized types use all remaining input
Unknown => Ok(Self::Eos(None)),
Unsized(_, _) => Err(Validation("`size`, `size-eos: true` or `terminator` must be specified".into())),
Unsized => Err(Validation(data.path, "`size`, `size-eos: true` or `terminator` must be specified".into())),
Sized(_) => Ok(Self::Natural),
},
}
Expand Down Expand Up @@ -704,14 +709,15 @@ impl TypeRef {
use ModelError::*;
use TypeRef::{Enum, F32, F64};

let path = props.path;
let endian = props.endian;
let endian = |t| match endian {
Some(e) => Ok(ByteOrder::try_from(e)?),
None => Err(Validation(format!("unable to use type `{:?}` without default endianness", t).into())),
Some(e) => Ok(ByteOrder::try_from((path.extend(vec!["endian"]), e))?),
None => Err(Validation(data.path, format!("unable to use type `{:?}` without default endianness", t).into())),
};
// Extract encoding of string
let encoding = |e: helpers::Inheritable<String>| match e {
Undefined => Err(Validation("string requires encoding".into())),
Undefined => Err(Validation(path, "string requires encoding".into())),
Default(enc) => Ok(enc),
Defined(enc) => Ok(enc),
};
Expand Down Expand Up @@ -780,8 +786,8 @@ impl TypeRef {
(None, None, Some(content), None) => Ok(TypeRef::Fixed(content.into())),
(None, None, None, None) => Ok(TypeRef::Bytes),

(_, Some(_), _, _) => Err(Validation("`encoding` can be specified only for `type: str` or `type: strz`".into())),
(_, _, Some(_), _) => Err(Validation("`contents` don't require type, its always byte array".into())),
(_, Some(_), _, _) => Err(Validation(data.path, "`encoding` can be specified only for `type: str` or `type: strz`".into())),
(_, _, Some(_), _) => Err(Validation(data.path, "`contents` don't require type, its always byte array".into())),
(_, _, _, Some(_)) => enum_err(),
}
}
Expand Down Expand Up @@ -936,6 +942,7 @@ impl Attribute {
eos_error: attr.eos_error,

pad_right: attr.pad_right,
path: data.path,
};
Ok(Self {
chunk: match attr.type_ {
Expand Down Expand Up @@ -1076,7 +1083,7 @@ impl TryFrom<p::Ksy> for Root {
Some(Bool(false)) => TypeName::valid("r#false"),
Some(Name(name)) => TypeName::validate(name)?,
};
let type_ = UserType::validate(data.root, data.meta.defaults.into())?;
let type_ = UserType::validate(data.root, data.meta.defaults.into(), YamlPath::default())?;

Ok(Self { name, type_ })
}
Expand Down
4 changes: 2 additions & 2 deletions src/model/name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
use std::ops::Deref;

use crate::error::ModelError;
use crate::error::{ModelError, ValidationError};
use crate::parser as p;
use crate::parser::expressions::parse_name;

Expand Down Expand Up @@ -52,7 +52,7 @@ impl<Tag> Name<Tag> {
/// Checks that the name contains only valid characters and creates a new one.
///
/// Valid names matches the following regexp: `$[a-zA-Z][a-zA-Z0-9_]*^`.
pub fn validate(name: p::Name) -> Result<Self, ModelError> {
pub fn validate(name: p::Name) -> Result<Self, ValidationError> {
Ok(Self::valid(parse_name(&name.0)?))
}
}
Expand Down

0 comments on commit cb8e01f

Please sign in to comment.