use super::fragment::NfaFragment;
pub const COUNTED_THRESHOLD: u32 = 16;
pub const MAX_COUNTED_OCCURS: u32 = 10_000;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MaxOccurs {
Unbounded,
Bounded(u32),
}
impl MaxOccurs {
pub fn from_option(max: Option<u32>) -> Self {
match max {
Some(n) => MaxOccurs::Bounded(n),
None => MaxOccurs::Unbounded,
}
}
pub fn to_option(&self) -> Option<u32> {
match self {
MaxOccurs::Unbounded => None,
MaxOccurs::Bounded(n) => Some(*n),
}
}
pub fn is_effectively_unbounded(&self) -> bool {
match self {
MaxOccurs::Unbounded => true,
MaxOccurs::Bounded(n) => *n > MAX_COUNTED_OCCURS,
}
}
pub fn is_unbounded(&self) -> bool {
matches!(self, MaxOccurs::Unbounded)
}
}
impl Default for MaxOccurs {
fn default() -> Self {
MaxOccurs::Bounded(1)
}
}
pub fn apply_occurs(frag: NfaFragment, min: u32, max: MaxOccurs) -> NfaFragment {
let effective_max = if max.is_effectively_unbounded() {
None } else {
max.to_option()
};
match effective_max {
None if min > COUNTED_THRESHOLD => frag
.clone()
.repeat_counted(min, min)
.concat(frag.repeat_star()),
None => frag.repeat_range(min, None),
Some(m) if m <= COUNTED_THRESHOLD => frag.repeat_range(min, Some(m)),
Some(m) if min == 0 => frag.repeat_counted(0, m),
Some(m) if min <= COUNTED_THRESHOLD => frag
.clone()
.repeat_exact(min)
.concat(frag.repeat_counted(0, m - min)),
Some(m) => frag.repeat_counted(min, m),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_max_occurs_from_option() {
assert_eq!(MaxOccurs::from_option(Some(5)), MaxOccurs::Bounded(5));
assert_eq!(MaxOccurs::from_option(None), MaxOccurs::Unbounded);
}
#[test]
fn test_max_occurs_to_option() {
assert_eq!(MaxOccurs::Bounded(5).to_option(), Some(5));
assert_eq!(MaxOccurs::Unbounded.to_option(), None);
}
#[test]
fn test_max_occurs_effectively_unbounded() {
assert!(!MaxOccurs::Bounded(50).is_effectively_unbounded());
assert!(!MaxOccurs::Bounded(100).is_effectively_unbounded());
assert!(!MaxOccurs::Bounded(1000).is_effectively_unbounded());
assert!(!MaxOccurs::Bounded(MAX_COUNTED_OCCURS).is_effectively_unbounded());
assert!(MaxOccurs::Bounded(MAX_COUNTED_OCCURS + 1).is_effectively_unbounded());
assert!(MaxOccurs::Unbounded.is_effectively_unbounded());
}
#[test]
fn test_max_occurs_default() {
assert_eq!(MaxOccurs::default(), MaxOccurs::Bounded(1));
}
}