asn1rs-model 0.2.2

Rust, Protobuf and SQL model definitions for asn1rs
Documentation
use crate::model::lor::{Error as ResolveError, Resolver, TryResolve};
use crate::model::lor::{ResolveState, Unresolved};
use crate::model::Error;
use crate::model::{LitOrRef, PeekableTokens};
use crate::parser::Token;
use std::convert::TryFrom;
use std::fmt::{Debug, Display};
use std::iter::Peekable;

#[derive(Debug, Clone, PartialOrd, PartialEq)]
pub enum Size<T: Display + Debug + Clone = usize> {
    Any,
    Fix(T, bool),
    Range(T, T, bool),
}

impl<T: Display + Debug + Clone> Size<T> {
    pub fn min(&self) -> Option<&T> {
        match self {
            Size::Any => None,
            Size::Fix(min, _) => Some(min),
            Size::Range(min, _, _) => Some(min),
        }
    }

    pub fn max(&self) -> Option<&T> {
        match self {
            Size::Any => None,
            Size::Fix(max, _) => Some(max),
            Size::Range(_, max, _) => Some(max),
        }
    }

    pub fn extensible(&self) -> bool {
        match self {
            Size::Any => false,
            Size::Fix(_, extensible) => *extensible,
            Size::Range(_, _, extensible) => *extensible,
        }
    }

    pub fn to_constraint_string(&self) -> Option<String> {
        match self {
            Size::Any => None,
            Size::Fix(min, extensible) => Some(format!(
                "size({}{})",
                min,
                if *extensible { ",..." } else { "" }
            )),
            Size::Range(min, max, extensible) => Some(format!(
                "size({}..{}{})",
                min,
                max,
                if *extensible { ",..." } else { "" }
            )),
        }
    }
}

impl Size<usize> {
    pub fn reconsider_constraints(self) -> Self {
        if let Self::Range(min, max, extensible) = self {
            if min == 0 && max == i64::MAX as usize && !extensible {
                Self::Any
            } else if min == max {
                Self::Fix(min, extensible)
            } else {
                self
            }
        } else {
            self
        }
    }
}

impl<T: Iterator<Item = Token>> TryFrom<&mut Peekable<T>>
    for Size<<Unresolved as ResolveState>::SizeType>
{
    type Error = Error;

    fn try_from(iter: &mut Peekable<T>) -> Result<Self, Self::Error> {
        iter.next_text_eq_ignore_case_or_err("SIZE")?;
        iter.next_separator_eq_or_err('(')?;

        let start = iter.next_or_err()?;
        let start = start
            .text()
            .filter(|txt| !txt.eq_ignore_ascii_case("MIN"))
            .map(|t| match t.parse::<usize>() {
                Ok(lit) => LitOrRef::Lit(lit),
                Err(_) => LitOrRef::Ref(t.to_string()),
            })
            .filter(|lor| LitOrRef::Lit(0).ne(lor));

        if !iter.peek_is_separator_eq('.') {
            match iter.next_or_err()? {
                t if t.eq_separator(')') => Ok(Size::Fix(start.unwrap_or_default(), false)),
                t if t.eq_separator(',') => {
                    iter.next_separator_eq_or_err('.')?;
                    iter.next_separator_eq_or_err('.')?;
                    iter.next_separator_eq_or_err('.')?;
                    iter.next_separator_eq_or_err(')')?;
                    Ok(Size::Fix(start.unwrap_or_default(), true))
                }
                t => Err(Error::unexpected_token(t)),
            }
        } else {
            const MAX: usize = i64::MAX as usize;

            iter.next_separator_eq_or_err('.')?;
            iter.next_separator_eq_or_err('.')?;
            let end = iter.next_or_err()?;
            let end = end
                .text()
                .filter(|txt| !txt.eq_ignore_ascii_case("MAX"))
                .map(|t| match t.parse::<usize>() {
                    Ok(lit) => LitOrRef::Lit(lit),
                    Err(_) => LitOrRef::Ref(t.to_string()),
                })
                .filter(|lor| LitOrRef::Lit(MAX).ne(lor));

            let any = matches!(
                (&start, &end),
                (None, None) | (Some(LitOrRef::Lit(0)), None) | (None, Some(LitOrRef::Lit(MAX)))
            );

            if any {
                iter.next_separator_eq_or_err(')')?;
                Ok(Size::Any)
            } else {
                let start = start.unwrap_or_default();
                let end = end.unwrap_or_else(|| LitOrRef::Lit(i64::MAX as usize));
                let extensible = if iter.next_separator_eq_or_err(',').is_ok() {
                    iter.next_separator_eq_or_err('.')?;
                    iter.next_separator_eq_or_err('.')?;
                    iter.next_separator_eq_or_err('.')?;
                    true
                } else {
                    false
                };
                iter.next_separator_eq_or_err(')')?;
                if start == end {
                    Ok(Size::Fix(start, extensible))
                } else {
                    Ok(Size::Range(start, end, extensible))
                }
            }
        }
    }
}

impl TryResolve<usize, Size<usize>> for Size<LitOrRef<usize>> {
    fn try_resolve(&self, resolver: &impl Resolver<usize>) -> Result<Size<usize>, ResolveError> {
        Ok(match self {
            Size::Any => Size::Any,
            Size::Fix(len, ext) => Size::Fix(resolver.resolve(len)?, *ext),
            Size::Range(min, max, ext) => {
                Size::Range(resolver.resolve(min)?, resolver.resolve(max)?, *ext)
            }
        }
        .reconsider_constraints())
    }
}