diff --git a/tor-netdoc/src/parse.rs b/tor-netdoc/src/parse.rs index 204b85208..80542287b 100644 --- a/tor-netdoc/src/parse.rs +++ b/tor-netdoc/src/parse.rs @@ -149,38 +149,6 @@ impl<'a, T: Keyword> Section<'a, T> { } } -/// Check whether a single Item matches a TokenFmt rule, with respect -/// to its number of arguments. -/// -/// TODO: Move this to rules? -fn item_matches_fmt_args<'a, T: Keyword>(t: T, fmt: &TokenFmt, item: &Item<'a>) -> Result<()> { - let n_args = item.n_args(); - if let Some(max) = fmt.max_args { - if n_args > max { - return Err(Error::TooManyArguments(t.to_str(), item.pos())); - } - } - if let Some(min) = fmt.min_args { - if n_args < min { - return Err(Error::TooFewArguments(t.to_str(), item.pos())); - } - } - - Ok(()) -} - -/// Check whether a single Item matches a TokenFmt rule, with respect -/// to its object's presence and type. -/// -/// TODO: Move this to rules? -fn item_matches_fmt_obj<'a, T: Keyword>(t: T, fmt: &TokenFmt, item: &Item<'a>) -> Result<()> { - match (&fmt.obj, item.has_obj()) { - (ObjKind::NoObj, true) => Err(Error::UnexpectedObject(t.to_str(), item.pos())), - (ObjKind::RequireObj, false) => Err(Error::MissingObject(t.to_str(), item.pos())), - (_, _) => Ok(()), - } -} - impl SectionRules { /// Create a new SectionRules with no rules. /// @@ -265,8 +233,7 @@ impl SectionRules { // The number is right. Check each individual item. for item in t.as_slice() { let tok = T::from_idx(idx).unwrap(); - item_matches_fmt_args(tok, rule, item)?; - item_matches_fmt_obj(tok, rule, item)?; + rule.check_item(tok, item)? } } } diff --git a/tor-netdoc/src/rules.rs b/tor-netdoc/src/rules.rs index f16f78f5b..ea89a210a 100644 --- a/tor-netdoc/src/rules.rs +++ b/tor-netdoc/src/rules.rs @@ -1,3 +1,5 @@ +use crate::tokenize::Item; +use crate::{Error, Result}; use std::hash::Hash; pub trait Keyword: Hash + Eq + PartialEq + Copy + Clone { @@ -24,11 +26,49 @@ pub enum ObjKind { #[derive(Clone)] pub struct TokenFmt { pub kwd: T, - pub min_args: Option, - pub max_args: Option, + min_args: Option, + max_args: Option, pub required: bool, pub may_repeat: bool, - pub obj: ObjKind, + obj: ObjKind, +} + +impl TokenFmt { + /// Check whether a single Item matches this TokenFmt rule, with respect + /// to its number of arguments. + fn item_matches_args<'a>(&self, t: T, item: &Item<'a>) -> Result<()> { + let n_args = item.n_args(); + if let Some(max) = self.max_args { + if n_args > max { + return Err(Error::TooManyArguments(t.to_str(), item.pos())); + } + } + if let Some(min) = self.min_args { + if n_args < min { + return Err(Error::TooFewArguments(t.to_str(), item.pos())); + } + } + Ok(()) + } + + /// Check whether a single Item matches a TokenFmt rule, with respect + /// to its object's presence and type. + /// + /// TODO: Move this to rules? + fn item_matches_obj<'a>(&self, t: T, item: &Item<'a>) -> Result<()> { + match (&self.obj, item.has_obj()) { + (ObjKind::NoObj, true) => Err(Error::UnexpectedObject(t.to_str(), item.pos())), + (ObjKind::RequireObj, false) => Err(Error::MissingObject(t.to_str(), item.pos())), + (_, _) => Ok(()), + } + } + + /// Check whether a single item has the right number of arguments + /// and object. + pub fn check_item<'a>(&self, t: T, item: &Item<'a>) -> Result<()> { + self.item_matches_args(t, item)?; + self.item_matches_obj(t, item) + } } pub struct TokenFmtBuilder(TokenFmt);