use std::collections::{BTreeMap, HashMap, VecDeque};
use std::convert::TryInto;
use std::mem::discriminant;
use crate::context::LookupContext;
use crate::ivt::*;
use crate::normalize::normalize_size_range;
use crate::util::{mismatch, ValidateError, ValidateResult};
use crate::value::Value;
#[derive(Clone, Debug, Default)]
struct GenericMap<'a> {
map: HashMap<String, &'a Node>,
past_ctx: Option<&'a Context<'a>>,
}
#[derive(Clone)]
struct Context<'a> {
lookup: &'a dyn LookupContext,
generic_map: GenericMap<'a>,
depth: u32,
}
impl std::fmt::Debug for Context<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Context")
.field("generic_map", &self.generic_map)
.finish()
}
}
struct NodeContext<'a> {
node: &'a Node,
ctx: Context<'a>,
}
impl<'a> Context<'a> {
const MAX_DEPTH: u32 = 50;
fn inc_depth(&self) -> TempResult<u32> {
if self.depth >= Self::MAX_DEPTH {
Err(ValidateError::Structural("hit recursion limit".into()))
} else {
Ok(self.depth + 1)
}
}
fn lookup_rule(&'a self, rule: &'a Rule) -> TempResult<NodeContext<'a>> {
let generic_node: Option<&'a Node> = self.generic_map.map.get(&rule.name).cloned();
if let Some(node) = generic_node {
let ctx = match self.generic_map.past_ctx {
Some(ctx) => ctx.clone(),
None => self.blank()?,
};
return Ok(NodeContext { node, ctx });
}
let rule_def: &RuleDef = self.lookup.lookup_rule(&rule.name)?;
let ctx = self.derive(rule_def, rule)?;
Ok(NodeContext {
node: &rule_def.node,
ctx,
})
}
fn blank(&self) -> TempResult<Context<'a>> {
Ok(Context {
lookup: self.lookup,
generic_map: GenericMap::default(),
depth: self.inc_depth()?,
})
}
fn derive(&'a self, rule_def: &'a RuleDef, rule: &'a Rule) -> TempResult<Context<'a>> {
let parms_len = rule_def.generic_parms.len();
let args_len = rule.generic_args.len();
if parms_len != args_len {
return Err(ValidateError::GenericError);
}
let map: HashMap<String, &'a Node> = rule_def
.generic_parms
.iter()
.cloned()
.zip(rule.generic_args.iter())
.collect();
let generic_map = GenericMap {
map,
past_ctx: Some(self),
};
Ok(Context {
lookup: self.lookup,
generic_map,
depth: self.inc_depth()?,
})
}
}
#[allow(dead_code, reason = "Prevent warnings if all features are disabled")]
pub(crate) fn do_validate(
value: &Value,
rule_def: &RuleDef,
ctx: &dyn LookupContext,
) -> ValidateResult {
if !rule_def.generic_parms.is_empty() {
return Err(ValidateError::GenericError);
}
let ctx = Context {
lookup: ctx,
generic_map: GenericMap::default(),
depth: 0,
};
let node = &rule_def.node;
validate(value, node, &ctx)
}
type ValueMap = BTreeMap<Value, Value>;
type TempResult<T> = Result<T, ValidateError>;
struct WorkingMap {
map: ValueMap,
snaps: VecDeque<VecDeque<(Value, Value)>>,
}
impl WorkingMap {
fn new(value_map: &ValueMap) -> WorkingMap {
WorkingMap {
map: value_map.clone(),
snaps: VecDeque::new(),
}
}
fn snapshot(&mut self) {
self.snaps.push_back(VecDeque::new());
}
fn rewind(&mut self) {
let mut top_snap = self.snaps.pop_back().unwrap();
self.map.extend(top_snap.drain(..));
}
fn commit(&mut self) {
self.snaps.pop_back().unwrap();
}
fn peek_at(&self, key: &Value) -> Option<&Value> {
self.map.get(key)
}
fn remove(&mut self, key: &Value) {
let value = self.map.remove(key).unwrap();
if let Some(snap) = self.snaps.back_mut() {
snap.push_back((key.clone(), value));
}
}
}
#[derive(Debug)]
struct WorkingArray {
array: VecDeque<Value>,
snaps: VecDeque<VecDeque<Value>>,
}
impl WorkingArray {
fn new(array: &[Value]) -> WorkingArray {
let deque: VecDeque<Value> = array.iter().cloned().collect();
WorkingArray {
array: deque,
snaps: VecDeque::new(),
}
}
fn snapshot(&mut self) {
let new_snap: VecDeque<Value> = VecDeque::new();
self.snaps.push_back(new_snap);
}
fn rewind(&mut self) {
let mut top_snap = self.snaps.pop_back().unwrap();
for element in top_snap.drain(..).rev() {
self.array.push_front(element);
}
}
fn commit(&mut self) {
self.snaps.pop_back().unwrap();
}
fn peek_front(&self) -> Option<&Value> {
self.array.front()
}
fn pop_front(&mut self) {
let element = self.array.pop_front().unwrap();
if let Some(snap) = self.snaps.back_mut() {
snap.push_back(element);
}
}
}
#[allow(
dead_code,
reason = "Prevent warnings if both ciborium and serde_json are disabled"
)]
fn validate(value: &Value, node: &Node, ctx: &Context) -> ValidateResult {
match node {
Node::Literal(l) => validate_literal(l, value),
Node::PreludeType(p) => validate_prelude_type(*p, value),
Node::Choice(c) => validate_choice(c, value, ctx),
Node::Map(m) => validate_map(m, value, ctx),
Node::Array(a) => validate_array(a, value, ctx),
Node::Rule(r) => validate_rule(r, value, ctx),
Node::Group(g) => validate_standalone_group(g, value, ctx),
Node::KeyValue(_) => Err(ValidateError::Structural("unexpected KeyValue".into())),
Node::Occur(_) => Err(ValidateError::Structural("unexpected Occur".into())),
Node::Unwrap(_) => Err(ValidateError::Structural("unexpected Unwrap".into())),
Node::Range(r) => validate_range(r, value, ctx),
Node::Control(ctl) => validate_control(ctl, value, ctx),
Node::Choiceify(r) => validate_choiceify(r, value, ctx),
Node::ChoiceifyInline(a) => validate_choiceify_inline(a, value, ctx),
}
}
fn validate_map_key<'a>(
value_map: &'a mut WorkingMap,
node: &Node,
ctx: &Context,
) -> TempResult<(Value, &'a Value)> {
match node {
Node::Literal(l) => map_search_literal(l, value_map),
_ => map_search(node, value_map, ctx),
}
}
fn validate_choice(choice: &Choice, value: &Value, ctx: &Context) -> ValidateResult {
for node in &choice.options {
match validate(value, node, ctx) {
Ok(()) => {
return Ok(());
}
Err(e) => {
if e.is_fatal() {
return Err(e);
}
}
}
}
let expected = format!("choice of {}", choice.options.len());
Err(mismatch(expected))
}
fn validate_rule(rule: &Rule, value: &Value, ctx: &Context) -> ValidateResult {
let answer = ctx.lookup_rule(rule)?;
validate(value, answer.node, &answer.ctx)
}
impl From<&Literal> for Value {
fn from(l: &Literal) -> Value {
match l {
Literal::Bool(b) => Value::Bool(*b),
Literal::Int(i) => Value::Integer(*i),
Literal::Float(f) => Value::from_float(*f),
Literal::Text(t) => Value::Text(t.clone()),
Literal::Bytes(b) => Value::Bytes(b.clone()),
}
}
}
fn validate_literal(literal: &Literal, value: &Value) -> ValidateResult {
if *value == Value::from(literal) {
return Ok(());
}
Err(mismatch(format!("{}", literal)))
}
fn map_search_literal<'a>(
literal: &Literal,
working_map: &'a mut WorkingMap,
) -> TempResult<(Value, &'a Value)> {
let search_key = Value::from(literal);
match working_map.peek_at(&search_key) {
Some(val) => Ok((search_key, val)),
None => {
Err(mismatch(format!("map{{{}}}", literal)))
}
}
}
fn map_search<'a>(
node: &Node,
working_map: &'a mut WorkingMap,
ctx: &Context,
) -> TempResult<(Value, &'a Value)> {
for (key, value) in &working_map.map {
let attempt = validate(key, node, ctx);
if attempt.is_ok() {
return Ok((key.clone(), value));
}
}
Err(mismatch(format!("map{{{}}}", node)))
}
fn validate_prelude_type(ty: PreludeType, value: &Value) -> ValidateResult {
match (ty, value) {
(PreludeType::Any, _) => Ok(()),
(PreludeType::Nil, Value::Null) => Ok(()),
(PreludeType::Nil, _) => Err(mismatch("nil")),
(PreludeType::Bool, Value::Bool(_)) => Ok(()),
(PreludeType::Bool, _) => Err(mismatch("bool")),
(PreludeType::Int, Value::Integer(_)) => Ok(()),
(PreludeType::Int, _) => Err(mismatch("int")),
(PreludeType::Uint, Value::Integer(x)) if *x >= 0 => Ok(()),
(PreludeType::Uint, _) => Err(mismatch("uint")),
(PreludeType::Nint, Value::Integer(x)) if *x < 0 => Ok(()),
(PreludeType::Nint, _) => Err(mismatch("nint")),
(PreludeType::Float, Value::Float(_)) => Ok(()),
(PreludeType::Float, _) => Err(mismatch("float")),
(PreludeType::Tstr, Value::Text(_)) => Ok(()),
(PreludeType::Tstr, _) => Err(mismatch("tstr")),
(PreludeType::Bstr, Value::Bytes(_)) => Ok(()),
(PreludeType::Bstr, _) => Err(mismatch("bstr")),
}
}
fn validate_array(ar: &Array, value: &Value, ctx: &Context) -> ValidateResult {
match value {
Value::Array(a) => validate_array_part2(ar, a, ctx),
_ => Err(mismatch("array")),
}
}
fn validate_array_part2(ar: &Array, value_array: &[Value], ctx: &Context) -> ValidateResult {
let mut working_array = WorkingArray::new(value_array);
for member in &ar.members {
validate_array_member(member, &mut working_array, ctx)?;
}
if working_array.array.is_empty() {
Ok(())
} else {
Err(mismatch("shorter array"))
}
}
fn validate_array_member(
member: &Node,
working_array: &mut WorkingArray,
ctx: &Context,
) -> ValidateResult {
match member {
Node::Occur(o) => validate_array_occur(o, working_array, ctx),
Node::KeyValue(kv) => {
validate_array_value(&kv.value, working_array, ctx)
}
Node::Rule(r) => {
let answer = ctx.lookup_rule(r)?;
validate_array_member(answer.node, working_array, &answer.ctx)
}
Node::Unwrap(r) => {
let answer = ctx.lookup_rule(r)?;
validate_array_unwrap(answer.node, working_array, &answer.ctx)
}
Node::Choice(c) => {
for option in &c.options {
match validate_array_member(option, working_array, ctx) {
Ok(()) => {
return Ok(());
}
Err(e) => {
if e.is_fatal() {
return Err(e);
}
}
}
}
let expected = format!("choice of {}", c.options.len());
Err(mismatch(expected))
}
Node::Group(g) => {
working_array.snapshot();
for group_member in &g.members {
match validate_array_member(group_member, working_array, ctx) {
Ok(_) => {
}
Err(e) => {
working_array.rewind();
return Err(e);
}
}
}
working_array.commit();
Ok(())
}
m => validate_array_value(m, working_array, ctx),
}
}
fn validate_array_unwrap(
node: &Node,
working_array: &mut WorkingArray,
ctx: &Context,
) -> ValidateResult {
match node {
Node::Array(a) => {
for member in &a.members {
validate_array_member(member, working_array, ctx)?;
}
Ok(())
}
_ => Err(mismatch("unwrap array")),
}
}
fn validate_array_occur(
occur: &Occur,
working_array: &mut WorkingArray,
ctx: &Context,
) -> ValidateResult {
let (lower_limit, upper_limit) = occur.limits();
let mut count: usize = 0;
loop {
match validate_array_member(&occur.node, working_array, ctx) {
Ok(_) => (),
Err(e) => {
if e.is_mismatch() {
break;
}
return Err(e);
}
}
count += 1;
if count >= upper_limit {
break;
}
}
if count < lower_limit {
return Err(mismatch(format!("more array element [{}]", occur)));
}
Ok(())
}
fn validate_array_value(
node: &Node,
working_array: &mut WorkingArray,
ctx: &Context,
) -> ValidateResult {
match working_array.peek_front() {
Some(val) => {
validate(val, node, ctx)?;
working_array.pop_front();
Ok(())
}
None => Err(mismatch(format!("array element {}", node))),
}
}
fn validate_map(m: &Map, value: &Value, ctx: &Context) -> ValidateResult {
match value {
Value::Map(vm) => validate_map_part2(m, vm, ctx),
_ => Err(mismatch("map")),
}
}
fn validate_map_part2(m: &Map, value_map: &ValueMap, ctx: &Context) -> ValidateResult {
let mut working_map = WorkingMap::new(value_map);
for member in &m.members {
validate_map_member(member, &mut working_map, ctx).map_err(|e| {
e.erase_mapcut()
})?;
}
if working_map.map.is_empty() {
Ok(())
} else {
Err(mismatch("shorter map"))
}
}
fn validate_map_member(
member: &Node,
working_map: &mut WorkingMap,
ctx: &Context,
) -> ValidateResult {
match member {
Node::Occur(o) => validate_map_occur(o, working_map, ctx),
Node::KeyValue(kv) => validate_map_keyvalue(kv, working_map, ctx),
Node::Rule(r) => {
let answer = ctx.lookup_rule(r)?;
validate_map_member(answer.node, working_map, &answer.ctx)
}
Node::Unwrap(r) => {
let answer = ctx.lookup_rule(r)?;
validate_map_unwrap(answer.node, working_map, &answer.ctx)
}
Node::Group(g) => {
working_map.snapshot();
for group_member in &g.members {
match validate_map_member(group_member, working_map, ctx) {
Ok(_) => {
}
Err(e) => {
working_map.rewind();
return Err(e.erase_mapcut());
}
}
}
working_map.commit();
Ok(())
}
Node::Choice(c) => validate_map_choice(&c.options, working_map, ctx),
Node::Choiceify(r) => validate_map_choiceify(r, working_map, ctx),
Node::ChoiceifyInline(a) => validate_map_choice(&a.members, working_map, ctx),
Node::Literal(_) => Err(ValidateError::Structural("literal map member".into())),
Node::PreludeType(_) => Err(ValidateError::Structural("prelude type map member".into())),
Node::Map(_) => Err(ValidateError::Structural("map as map member".into())),
Node::Array(_) => Err(ValidateError::Structural("array as map member".into())),
Node::Range(_) => Err(ValidateError::Structural("range as map member".into())),
Node::Control(_) => Err(ValidateError::Structural("control op as map member".into())),
}
}
fn validate_map_choice(
options: &[Node],
working_map: &mut WorkingMap,
ctx: &Context,
) -> ValidateResult {
for option in options {
match validate_map_member(option, working_map, ctx) {
Ok(()) => {
return Ok(());
}
Err(e) => {
if !e.is_mismatch() {
return Err(e);
}
}
}
}
let expected = format!("choice of {}", options.len());
Err(mismatch(expected))
}
fn validate_map_choiceify_members(
choices: &[Node],
working_map: &mut WorkingMap,
ctx: &Context,
) -> ValidateResult {
for item in choices {
let validate_result = match item {
Node::KeyValue(kv) => {
validate_map_member(&kv.value, working_map, ctx)
}
Node::Rule(rule) => {
validate_map_choiceify(rule, working_map, ctx)
}
_ => {
validate_map_member(item, working_map, ctx)
}
};
match validate_result {
Ok(()) => {
return Ok(());
}
Err(e) => {
if !e.is_mismatch() {
return Err(e);
}
}
}
}
let expected = format!("choiceified group of {}", choices.len());
Err(mismatch(expected))
}
fn validate_map_choiceify(
rule: &Rule,
working_map: &mut WorkingMap,
ctx: &Context,
) -> ValidateResult {
let NodeContext { node, ctx } = ctx.lookup_rule(rule)?;
match node {
Node::Group(g) => validate_map_choiceify_members(&g.members, working_map, &ctx),
Node::Rule(r) => validate_map_choiceify(r, working_map, &ctx),
_ => Err(ValidateError::Structural(
"improper map choiceify target".into(),
)),
}
}
fn validate_map_unwrap(node: &Node, working_map: &mut WorkingMap, ctx: &Context) -> ValidateResult {
match node {
Node::Map(m) => {
for member in &m.members {
validate_map_member(member, working_map, ctx)?;
}
Ok(())
}
_ => Err(mismatch("unwrap map")),
}
}
fn validate_map_occur(
occur: &Occur,
working_map: &mut WorkingMap,
ctx: &Context,
) -> ValidateResult {
let (lower_limit, upper_limit) = occur.limits();
let mut count: usize = 0;
loop {
match validate_map_member(&occur.node, working_map, ctx) {
Ok(_) => (),
Err(e) => {
if e.is_mismatch() {
break;
}
return Err(e);
}
}
count += 1;
if count >= upper_limit {
break;
}
}
if count < lower_limit {
return Err(mismatch(format!("map{{{}}}]", occur)));
}
Ok(())
}
fn validate_map_keyvalue(
kv: &KeyValue,
working_map: &mut WorkingMap,
ctx: &Context,
) -> ValidateResult {
let key_node = &kv.key;
let val_node = &kv.value;
let cut = kv.cut;
let (working_key, working_val) = validate_map_key(working_map, key_node, ctx)?;
match validate(working_val, val_node, ctx) {
Ok(()) => {
working_map.remove(&working_key);
Ok(())
}
Err(e) => {
match (cut, e) {
(true, ValidateError::Mismatch(m)) => {
Err(ValidateError::MapCut(m))
}
(_, x) => Err(x),
}
}
}
}
fn validate_standalone_group(g: &Group, value: &Value, ctx: &Context) -> ValidateResult {
match g.members.len() {
1 => {
validate(value, &g.members[0], ctx)
}
_ => Err(ValidateError::Unsupported("standalone group".into())),
}
}
fn deref_range_rule(node: &Node, ctx: &Context) -> TempResult<Literal> {
match node {
Node::Literal(l) => Ok(l.clone()),
Node::Rule(r) => {
let answer = ctx.lookup_rule(r)?;
deref_range_rule(answer.node, &answer.ctx)
}
_ => Err(ValidateError::Structural(
"confusing type on range operator".into(),
)),
}
}
fn check_range<T: PartialOrd>(start: T, end: T, value: T, inclusive: bool) -> bool {
if value < start {
return false;
}
if inclusive {
value <= end
} else {
value < end
}
}
fn validate_range(range: &Range, value: &Value, ctx: &Context) -> ValidateResult {
let start = deref_range_rule(&range.start, ctx)?;
let end = deref_range_rule(&range.end, ctx)?;
match (&start, &end, &value) {
(Literal::Int(i1), Literal::Int(i2), Value::Integer(v)) => {
if check_range(i1, i2, v, range.inclusive) {
Ok(())
} else {
Err(mismatch(format!("{}", range)))
}
}
(Literal::Float(f1), Literal::Float(f2), Value::Float(v)) => {
if check_range(f1, f2, &v.0, range.inclusive) {
Ok(())
} else {
Err(mismatch(format!("{}", range)))
}
}
_ => {
if discriminant(&start) == discriminant(&end) {
Err(mismatch(format!("{}", range)))
} else {
Err(ValidateError::Structural(
"mismatched types on range operator".into(),
))
}
}
}
}
fn chase_rules<'a, F, R>(node: &'a Node, ctx: &'a Context<'a>, f: F) -> TempResult<R>
where
F: Fn(&Node) -> TempResult<R>,
R: 'static,
{
if let Node::Rule(rule) = node {
let answer = ctx.lookup_rule(rule)?;
chase_rules(answer.node, &answer.ctx, f)
} else {
f(node)
}
}
fn validate_control(ctl: &Control, value: &Value, ctx: &Context) -> ValidateResult {
match ctl {
Control::Size(ctl_size) => validate_control_size(ctl_size, value, ctx),
Control::Lt(ctl_lt) => validate_control_lt(ctl_lt, value, ctx),
Control::Le(ctl_le) => validate_control_le(ctl_le, value, ctx),
Control::Gt(ctl_gt) => validate_control_gt(ctl_gt, value, ctx),
Control::Ge(ctl_ge) => validate_control_ge(ctl_ge, value, ctx),
Control::Regexp(re) => validate_control_regexp(re, value),
Control::Cbor(ctl_cbor) => validate_control_cbor(ctl_cbor, value, ctx),
}
}
#[cfg(not(feature = "ciborium"))]
fn validate_control_cbor(_ctl_cbor: &CtlOpCbor, _value: &Value, _ctx: &Context) -> ValidateResult {
Err(ValidateError::Unsupported(
"'.cbor' control operator; enable ciborium feature to support.".into(),
))
}
#[cfg(feature = "ciborium")]
fn validate_control_cbor(ctl_cbor: &CtlOpCbor, value: &Value, ctx: &Context) -> ValidateResult {
use ciborium::Value as CBOR_Value;
use std::convert::TryFrom;
match value {
Value::Bytes(bytes) => {
let cbor_value: CBOR_Value = ciborium::from_reader(bytes.as_slice())
.map_err(|e| ValidateError::ValueError(format!("{}", e)))?;
let nested_value = Value::try_from(cbor_value)?;
validate(&nested_value, ctl_cbor.node.as_ref(), ctx)
}
_ => Err::<(), ValidateError>(mismatch("Bytes")),
}
}
fn validate_control_size(ctl: &CtlOpSize, value: &Value, ctx: &Context) -> ValidateResult {
let size: Range = chase_rules(&ctl.size, ctx, normalize_size_range)?;
chase_rules(&ctl.target, ctx, |target_node| {
match target_node {
Node::PreludeType(PreludeType::Uint) => validate_size_uint(&size, value),
Node::PreludeType(PreludeType::Tstr) => validate_size_tstr(&size, value),
Node::PreludeType(PreludeType::Bstr) => validate_size_bstr(&size, value),
_ => {
let msg = format!("bad .size target type ({})", target_node);
Err(ValidateError::Structural(msg))
}
}
})
}
fn validate_control_lt(ctl: &CtlOpLt, value: &Value, ctx: &Context) -> ValidateResult {
let lt: i128 = chase_rules(&ctl.lt, ctx, |lt_node| match lt_node {
Node::Literal(Literal::Int(i)) => Ok(*i),
_ => {
let msg = format!("bad .lt argument type ({})", lt_node);
Err(ValidateError::Structural(msg))
}
})?;
chase_rules(&ctl.target, ctx, |target_node| {
match target_node {
Node::PreludeType(PreludeType::Uint) => validate_lt_uint(lt, value),
Node::PreludeType(PreludeType::Nint) => validate_lt_nint(lt, value),
Node::PreludeType(PreludeType::Int) => validate_lt_int(lt, value),
Node::PreludeType(PreludeType::Float) => Err(ValidateError::Structural(
".lt for float is not supported yet".into(),
)),
_ => {
let msg = format!("bad .lt target type ({})", target_node);
Err(ValidateError::Structural(msg))
}
}
})
}
fn validate_control_le(ctl: &CtlOpLe, value: &Value, ctx: &Context) -> ValidateResult {
let le: i128 = chase_rules(&ctl.le, ctx, |le_node| match le_node {
Node::Literal(Literal::Int(i)) => Ok(*i),
_ => {
let msg = format!("bad .le argument type ({})", le_node);
Err(ValidateError::Structural(msg))
}
})?;
chase_rules(&ctl.target, ctx, |target_node| {
match target_node {
Node::PreludeType(PreludeType::Uint) => validate_le_uint(le, value),
Node::PreludeType(PreludeType::Nint) => validate_le_nint(le, value),
Node::PreludeType(PreludeType::Int) => validate_le_int(le, value),
Node::PreludeType(PreludeType::Float) => Err(ValidateError::Structural(
".le for float is not supported yet".into(),
)),
_ => {
let msg = format!("bad .le target type ({})", target_node);
Err(ValidateError::Structural(msg))
}
}
})
}
fn validate_control_gt(ctl: &CtlOpGt, value: &Value, ctx: &Context) -> ValidateResult {
let gt: i128 = chase_rules(&ctl.gt, ctx, |gt_node| match gt_node {
Node::Literal(Literal::Int(i)) => Ok(*i),
_ => {
let msg = format!("bad .gt argument type ({})", gt_node);
Err(ValidateError::Structural(msg))
}
})?;
chase_rules(&ctl.target, ctx, |target_node| {
match target_node {
Node::PreludeType(PreludeType::Uint) => validate_gt_uint(gt, value),
Node::PreludeType(PreludeType::Nint) => validate_gt_nint(gt, value),
Node::PreludeType(PreludeType::Int) => validate_gt_int(gt, value),
Node::PreludeType(PreludeType::Float) => Err(ValidateError::Structural(
".gt for float is not supported yet".into(),
)),
_ => {
let msg = format!("bad .gt target type ({})", target_node);
Err(ValidateError::Structural(msg))
}
}
})
}
fn validate_control_ge(ctl: &CtlOpGe, value: &Value, ctx: &Context) -> ValidateResult {
let ge: i128 = chase_rules(&ctl.ge, ctx, |ge_node| match ge_node {
Node::Literal(Literal::Int(i)) => Ok(*i),
_ => {
let msg = format!("bad .ge argument type ({})", ge_node);
Err(ValidateError::Structural(msg))
}
})?;
chase_rules(&ctl.target, ctx, |target_node| {
match target_node {
Node::PreludeType(PreludeType::Uint) => validate_ge_uint(ge, value),
Node::PreludeType(PreludeType::Nint) => validate_ge_nint(ge, value),
Node::PreludeType(PreludeType::Int) => validate_ge_int(ge, value),
Node::PreludeType(PreludeType::Float) => Err(ValidateError::Structural(
".ge for float is not supported yet".into(),
)),
_ => {
let msg = format!("bad .ge target type ({})", target_node);
Err(ValidateError::Structural(msg))
}
}
})
}
fn validate_control_regexp(re: &CtlOpRegexp, value: &Value) -> ValidateResult {
match value {
Value::Text(text) => {
if re.re.is_match(text) {
Ok(())
} else {
Err(mismatch("regex mismatch"))
}
}
_ => Err(mismatch("tstr")),
}
}
fn validate_size_uint(size: &Range, value: &Value) -> ValidateResult {
let (start_i, end_i) = match (size.start.as_ref(), size.end.as_ref()) {
(Node::Literal(Literal::Int(a)), Node::Literal(Literal::Int(b))) => (*a, *b),
_ => {
return Err(ValidateError::Structural(
"bad .size range endpoints for uint".into(),
))
}
};
let start_u: u64 = start_i
.try_into()
.map_err(|_| ValidateError::Structural(format!("bad .size limit {}", start_i)))?;
let end_u: u64 = end_i
.try_into()
.map_err(|_| ValidateError::Structural(format!("bad .size limit {}", end_i)))?;
if start_u > end_u || (start_u == end_u && !size.inclusive) {
return Err(mismatch("uint over .size limit"));
}
match value {
Value::Integer(x) => {
if *x < 0 {
return Err(mismatch(".size on negative integer"));
}
if start_u == end_u && size.inclusive {
let n = end_u;
if n >= 16 {
return Ok(());
}
let limit = 1i128 << (n * 8);
return if *x < limit {
Ok(())
} else {
Err(mismatch("uint over .size limit"))
};
}
let v: u128 = *x as u128;
let needed: u64 = if v == 0 {
0
} else {
let bits = 128u32 - v.leading_zeros(); (bits as u64).div_ceil(8)
};
if needed < start_u {
return Err(mismatch("uint under .size limit"));
}
let ok_max = if size.inclusive {
needed <= end_u
} else {
needed < end_u
};
if ok_max {
Ok(())
} else {
Err(mismatch("uint over .size limit"))
}
}
_ => Err(mismatch("uint")),
}
}
fn validate_size_tstr(size: &Range, value: &Value) -> ValidateResult {
let (start_i, end_i) = match (size.start.as_ref(), size.end.as_ref()) {
(Node::Literal(Literal::Int(a)), Node::Literal(Literal::Int(b))) => (*a, *b),
_ => {
return Err(ValidateError::Structural(
"bad .size range endpoints for tstr".into(),
))
}
};
let min_u: u64 = start_i
.try_into()
.map_err(|_| ValidateError::Structural(format!("bad .size limit {}", start_i)))?;
let max_u: u64 = end_i
.try_into()
.map_err(|_| ValidateError::Structural(format!("bad .size limit {}", end_i)))?;
if min_u > max_u || (min_u == max_u && !size.inclusive) {
return Err(mismatch("tstr over .size limit"));
}
match value {
Value::Text(s) => {
let len_u: u64 = s.len() as u64;
if len_u < min_u {
return Err(mismatch("tstr under .size limit"));
}
let ok_max = if size.inclusive {
len_u <= max_u
} else {
len_u < max_u
};
if !ok_max {
return Err(mismatch("tstr over .size limit"));
}
Ok(())
}
_ => Err(mismatch("tstr")),
}
}
fn validate_size_bstr(size: &Range, value: &Value) -> ValidateResult {
let (start_i, end_i) = match (size.start.as_ref(), size.end.as_ref()) {
(Node::Literal(Literal::Int(a)), Node::Literal(Literal::Int(b))) => (*a, *b),
_ => {
return Err(ValidateError::Structural(
"bad .size range endpoints for bstr".into(),
))
}
};
let min_u: u64 = start_i
.try_into()
.map_err(|_| ValidateError::Structural(format!("bad .size limit {}", start_i)))?;
let max_u: u64 = end_i
.try_into()
.map_err(|_| ValidateError::Structural(format!("bad .size limit {}", end_i)))?;
if min_u > max_u || (min_u == max_u && !size.inclusive) {
return Err(mismatch("bstr over .size limit"));
}
match value {
Value::Bytes(b) => {
let len_u: u64 = b.len().try_into().unwrap_or(u64::MAX);
if len_u < min_u {
return Err(mismatch("bstr under .size limit"));
}
let ok_max = if size.inclusive {
len_u <= max_u
} else {
len_u < max_u
};
if !ok_max {
return Err(mismatch("bstr over .size limit"));
}
Ok(())
}
_ => Err(mismatch("bstr")),
}
}
fn validate_lt_uint(lt: i128, value: &Value) -> ValidateResult {
match value {
Value::Integer(x) => {
if *x < 0 {
Err(mismatch("uint"))
} else if *x < lt {
Ok(())
} else {
Err(mismatch("uint over .lt limit"))
}
}
_ => Err(mismatch("uint")),
}
}
fn validate_lt_nint(lt: i128, value: &Value) -> ValidateResult {
match value {
Value::Integer(x) => {
if *x >= 0 {
Err(mismatch("nint"))
} else if *x < lt {
Ok(())
} else {
Err(mismatch("nint over .lt limit"))
}
}
_ => Err(mismatch("nint")),
}
}
fn validate_lt_int(lt: i128, value: &Value) -> ValidateResult {
match value {
Value::Integer(x) => {
if *x < lt {
Ok(())
} else {
Err(mismatch("int over .lt limit"))
}
}
_ => Err(mismatch("int")),
}
}
fn validate_le_uint(lt: i128, value: &Value) -> ValidateResult {
match value {
Value::Integer(x) => {
if *x < 0 {
Err(mismatch("uint"))
} else if *x <= lt {
Ok(())
} else {
Err(mismatch("uint over .le limit"))
}
}
_ => Err(mismatch("uint")),
}
}
fn validate_le_nint(lt: i128, value: &Value) -> ValidateResult {
match value {
Value::Integer(x) => {
if *x >= 0 {
Err(mismatch("nint"))
} else if *x <= lt {
Ok(())
} else {
Err(mismatch("nint over .lt limit"))
}
}
_ => Err(mismatch("nint")),
}
}
fn validate_le_int(lt: i128, value: &Value) -> ValidateResult {
match value {
Value::Integer(x) => {
if *x <= lt {
Ok(())
} else {
Err(mismatch("int over .le limit"))
}
}
_ => Err(mismatch("int")),
}
}
fn validate_gt_uint(lt: i128, value: &Value) -> ValidateResult {
match value {
Value::Integer(x) => {
if *x < 0 {
Err(mismatch("uint"))
} else if *x > lt {
Ok(())
} else {
Err(mismatch("uint under .gt limit"))
}
}
_ => Err(mismatch("uint")),
}
}
fn validate_gt_nint(lt: i128, value: &Value) -> ValidateResult {
match value {
Value::Integer(x) => {
if *x >= 0 {
Err(mismatch("nint"))
} else if *x > lt {
Ok(())
} else {
Err(mismatch("nint over .gt limit"))
}
}
_ => Err(mismatch("nint")),
}
}
fn validate_gt_int(lt: i128, value: &Value) -> ValidateResult {
match value {
Value::Integer(x) => {
if *x > lt {
Ok(())
} else {
Err(mismatch("int over .ge limit"))
}
}
_ => Err(mismatch("int")),
}
}
fn validate_ge_uint(lt: i128, value: &Value) -> ValidateResult {
match value {
Value::Integer(x) => {
if *x < 0 {
Err(mismatch("uint"))
} else if *x >= lt {
Ok(())
} else {
Err(mismatch("uint under .ge limit"))
}
}
_ => Err(mismatch("uint")),
}
}
fn validate_ge_nint(lt: i128, value: &Value) -> ValidateResult {
match value {
Value::Integer(x) => {
if *x >= 0 {
Err(mismatch("nint"))
} else if *x >= lt {
Ok(())
} else {
Err(mismatch("nint over .ge limit"))
}
}
_ => Err(mismatch("nint")),
}
}
fn validate_ge_int(lt: i128, value: &Value) -> ValidateResult {
match value {
Value::Integer(x) => {
if *x >= lt {
Ok(())
} else {
Err(mismatch("int over .ge limit"))
}
}
_ => Err(mismatch("int")),
}
}
fn validate_choiceify_members(choices: &[Node], value: &Value, ctx: &Context) -> ValidateResult {
for item in choices {
let validate_result = match item {
Node::KeyValue(kv) => validate(value, &kv.value, ctx),
Node::Rule(rule) => {
validate_choiceify(rule, value, ctx)
}
_ => {
validate(value, item, ctx)
}
};
match validate_result {
Ok(()) => {
return Ok(());
}
Err(e) => {
if e.is_fatal() {
return Err(e);
}
}
}
}
let expected = format!("choiceified group of {}", choices.len());
Err(mismatch(expected))
}
fn validate_choiceify(rule: &Rule, value: &Value, ctx: &Context) -> ValidateResult {
let NodeContext { node, ctx } = ctx.lookup_rule(rule)?;
match node {
Node::Group(g) => validate_choiceify_members(&g.members, value, &ctx),
Node::Rule(r) => validate_choiceify(r, value, &ctx),
_ => Err(ValidateError::Structural(
"improper choiceify target".into(),
)),
}
}
fn validate_choiceify_inline(array: &Array, value: &Value, ctx: &Context) -> ValidateResult {
validate_choiceify_members(&array.members, value, ctx)
}