Skip to content

Commit cb8e01f

Browse files
committed
Недоделанная попытка добавить пути к ошибкам
1 parent ffa561d commit cb8e01f

File tree

4 files changed

+70
-35
lines changed

4 files changed

+70
-35
lines changed

src/error.rs

+34-6
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,49 @@
11
//! Contains errors that can occurs when creating kaitai struct model
22
33
use std::borrow::Cow;
4-
use std::convert::Infallible;
4+
use std::convert::{Infallible, TryInto};
55
use std::error::Error;
6-
use std::fmt::{Display, Formatter, Result};
6+
use std::fmt::{self, Display, Formatter};
77

88
use peg::error::ParseError;
99
use peg::str::LineCol;
1010

11+
/// Path to attribute in YAML
12+
#[derive(Default, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
13+
pub struct YamlPath(Vec<String>);
14+
impl YamlPath {
15+
pub fn extend<I: IntoIterator>(&self, segments: I) -> Self
16+
where I::Item: Into<String>,
17+
{
18+
let mut path = self.0.clone();
19+
path.extend(segments.into_iter().map(Into::into));
20+
Self(path)
21+
}
22+
pub fn validate<T, R>(self, value: T) -> Result<R, ModelError>
23+
where T: TryInto<R, Error = Cow<'static, str>>
24+
{
25+
value.try_into().map_err(|e| ModelError::Validation(self, e))
26+
}
27+
}
28+
impl Display for YamlPath {
29+
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
30+
for segment in &self.0 {
31+
write!(fmt, "/{}", segment)?;
32+
}
33+
Ok(())
34+
}
35+
}
36+
37+
pub type ValidationError = Cow<'static, str>;
38+
1139
/// Possible errors when creating kaitai struct model from YAML representation
1240
#[derive(Clone, Debug, PartialEq, Eq)]
1341
pub enum ModelError {
1442
/// Parser error of incorrect expression in field
1543
Expression(ParseError<LineCol>),//TODO: Add information about field
1644
/// Error of validating schema rules, such as absence of mandatory fields or
1745
/// excess fields.
18-
Validation(Cow<'static, str>),
46+
Validation(YamlPath, ValidationError),
1947
}
2048
impl From<ParseError<LineCol>> for ModelError {
2149
fn from(error: ParseError<LineCol>) -> Self { Self::Expression(error) }
@@ -26,12 +54,12 @@ impl From<Infallible> for ModelError {
2654
}
2755

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

3260
match self {
3361
Expression(err) => write!(f, "incorrect expression: {}", err),
34-
Validation(err) => write!(f, "invalid schema: {}", err),
62+
Validation(path, err) => write!(f, "{}: invalid schema: {}", path, err),
3563
}
3664
}
3765
}
@@ -42,7 +70,7 @@ impl Error for ModelError {
4270

4371
match self {
4472
Expression(err) => Some(err),
45-
Validation(_) => None,
73+
Validation(..) => None,
4674
}
4775
}
4876
}

src/model/expressions.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use bigdecimal::num_bigint::BigInt;
88
use bigdecimal::BigDecimal;
99
use serde_yml::Number;
1010

11-
use crate::error::ModelError;
11+
use crate::error::{ModelError, YamlPath};
1212
use crate::model::{EnumName, EnumValueName, FieldName, TypeName as TName};
1313
use crate::parser::expressions::{
1414
parse_single, BinaryOp, Node, Scope, SpecialName, TypeName, TypeRef, UnaryOp,
@@ -218,15 +218,15 @@ impl From<Number> for OwningNode {
218218
Self::validate(Node::from(number))
219219
}
220220
}
221-
impl TryFrom<Scalar> for OwningNode {
221+
impl TryFrom<(YamlPath, Scalar)> for OwningNode {
222222
type Error = ModelError;
223223

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

228-
match scalar {
229-
Null => Err(Validation(
228+
match scalar.1 {
229+
Null => Err(Validation(scalar.0,
230230
"Expected expression, but null found (note that `null` literal in YAML is \
231231
equivalent of absence of any value, use 'null' if you want to refer to name `null`)".into())),
232232
Bool(val) => Ok(Self::Bool(val)),

src/model/mod.rs

+29-22
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use indexmap::{indexmap, IndexMap};
1919
use lazy_static::lazy_static;
2020
use regex::Regex;
2121

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

4041
/// Wrapper for inheritable values
@@ -86,6 +87,8 @@ mod helpers {
8687
pub eos_error: Option<bool>,
8788

8889
pub pad_right: Option<u8>,
90+
91+
pub path: YamlPath,
8992
}
9093

9194
/// Transitional structure, that contains all data from parser structure,
@@ -97,6 +100,7 @@ mod helpers {
97100
pub encoding: Inheritable<String>,
98101
pub endian: Option<p::Variant<p::ByteOrder>>,
99102
pub bit_endian: Option<p::BitOrder>,
103+
pub path: YamlPath,
100104
}
101105
}
102106

@@ -203,23 +207,24 @@ pub enum Variant<T> {
203207
cases: IndexMap<OwningNode, T>,
204208
},
205209
}
206-
impl<T, U: TryInto<T>> TryFrom<p::Variant<U>> for Variant<T>
210+
impl<T, U: TryInto<T>> TryFrom<(YamlPath, p::Variant<U>)> for Variant<T>
207211
where U::Error: Into<ModelError>,
208212
{
209213
type Error = ModelError;
210214

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

214-
match data {
218+
match data.1 {
215219
Fixed(val) => Ok(Variant::Fixed(val.try_into().map_err(Into::into)?)),
216220
Choice { switch_on, cases } => {
217221
let mut new_cases = IndexMap::with_capacity(cases.len());
218222
for (k, v) in cases.into_iter() {
219-
new_cases.insert(k.try_into()?, v.try_into().map_err(Into::into)?);
223+
let path = data.0.extend(vec!["cases"]);//TODO: path to case
224+
new_cases.insert((path, k).try_into()?, v.try_into().map_err(Into::into)?);
220225
}
221226
Ok(Variant::Choice {
222-
switch_on: switch_on.try_into()?,
227+
switch_on: (data.0.extend(vec!["switch_on"]), switch_on).try_into()?,
223228
cases: new_cases,
224229
})
225230
}
@@ -304,16 +309,16 @@ impl Repeat {
304309
},
305310

306311
#[cfg(feature = "compatible")]
307-
(None, Some(_), None) => Err(Validation("missed `repeat: expr`".into())),
312+
(None, Some(_), None) => Err(Validation(data.path, "missed `repeat: expr`".into())),
308313
#[cfg(feature = "compatible")]
309-
(None, None, Some(_)) => Err(Validation("missed `repeat: until`".into())),
314+
(None, None, Some(_)) => Err(Validation(data.path, "missed `repeat: until`".into())),
310315

311-
(Some(Expr), None, _) => Err(Validation("missed `repeat-expr`".into())),
312-
(Some(Until), _, None) => Err(Validation("missed `repeat-until`".into())),
316+
(Some(Expr), None, _) => Err(Validation(data.path, "missed `repeat-expr`".into())),
317+
(Some(Until), _, None) => Err(Validation(data.path, "missed `repeat-until`".into())),
313318

314-
(_, Some(_), Some(_)) => Err(Validation("either `repeat-expr` or `repeat-until` must be specified".into())),
315-
(Some(_), _, Some(_)) => Err(Validation("`repeat-until` requires `repeat: until`".into())),
316-
(Some(_), Some(_), _) => Err(Validation("`repeat-expr` requires `repeat: expr`".into())),
319+
(_, Some(_), Some(_)) => Err(Validation(data.path, "either `repeat-expr` or `repeat-until` must be specified".into())),
320+
(Some(_), _, Some(_)) => Err(Validation(data.path, "`repeat-until` requires `repeat: until`".into())),
321+
(Some(_), Some(_), _) => Err(Validation(data.path, "`repeat-expr` requires `repeat: expr`".into())),
317322
}
318323
}
319324
}
@@ -462,7 +467,7 @@ impl Size {
462467
mandatory: mandatory.unwrap_or(true),
463468
}),
464469
// TODO: Emit warning instead here, but error also an option until warnings is not implemented
465-
//(None, ..) => return Err(Validation("`consume`, `include` or `eos-error` has no effect without `terminator`".into())),
470+
//(None, ..) => return Err(Validation(data.path, "`consume`, `include` or `eos-error` has no effect without `terminator`".into())),
466471
(None, ..) => None,
467472
};
468473

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

712+
let path = props.path;
707713
let endian = props.endian;
708714
let endian = |t| match endian {
709-
Some(e) => Ok(ByteOrder::try_from(e)?),
710-
None => Err(Validation(format!("unable to use type `{:?}` without default endianness", t).into())),
715+
Some(e) => Ok(ByteOrder::try_from((path.extend(vec!["endian"]), e))?),
716+
None => Err(Validation(data.path, format!("unable to use type `{:?}` without default endianness", t).into())),
711717
};
712718
// Extract encoding of string
713719
let encoding = |e: helpers::Inheritable<String>| match e {
714-
Undefined => Err(Validation("string requires encoding".into())),
720+
Undefined => Err(Validation(path, "string requires encoding".into())),
715721
Default(enc) => Ok(enc),
716722
Defined(enc) => Ok(enc),
717723
};
@@ -780,8 +786,8 @@ impl TypeRef {
780786
(None, None, Some(content), None) => Ok(TypeRef::Fixed(content.into())),
781787
(None, None, None, None) => Ok(TypeRef::Bytes),
782788

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

938944
pad_right: attr.pad_right,
945+
path: data.path,
939946
};
940947
Ok(Self {
941948
chunk: match attr.type_ {
@@ -1076,7 +1083,7 @@ impl TryFrom<p::Ksy> for Root {
10761083
Some(Bool(false)) => TypeName::valid("r#false"),
10771084
Some(Name(name)) => TypeName::validate(name)?,
10781085
};
1079-
let type_ = UserType::validate(data.root, data.meta.defaults.into())?;
1086+
let type_ = UserType::validate(data.root, data.meta.defaults.into(), YamlPath::default())?;
10801087

10811088
Ok(Self { name, type_ })
10821089
}

src/model/name.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use std::hash::{Hash, Hasher};
99
use std::marker::PhantomData;
1010
use std::ops::Deref;
1111

12-
use crate::error::ModelError;
12+
use crate::error::{ModelError, ValidationError};
1313
use crate::parser as p;
1414
use crate::parser::expressions::parse_name;
1515

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

0 commit comments

Comments
 (0)