asn1rs-model 0.2.2

Rust, Protobuf and SQL model definitions for asn1rs
Documentation
use crate::model::lor::{Error as ResolveError, ResolveState, Resolved, Resolver, Unresolved};
use crate::model::{Asn, Error, Model, PeekableTokens, Tag, TagProperty, Type};
use crate::parser::Token;
use std::convert::TryFrom;

use std::iter::Peekable;

#[derive(Debug, Clone, PartialOrd, PartialEq)]
pub struct Choice<RS: ResolveState = Resolved> {
    variants: Vec<ChoiceVariant<RS>>,
    extension_after: Option<usize>,
}

impl<RS: ResolveState> From<Vec<ChoiceVariant<RS>>> for Choice<RS> {
    fn from(variants: Vec<ChoiceVariant<RS>>) -> Self {
        Self {
            variants,
            extension_after: None,
        }
    }
}

impl<RS: ResolveState> Choice<RS> {
    pub fn from_variants(variants: impl Iterator<Item = ChoiceVariant<RS>>) -> Self {
        Self {
            variants: variants.collect(),
            extension_after: None,
        }
    }

    pub fn with_extension_after(mut self, extension_after: usize) -> Self {
        self.extension_after = Some(extension_after);
        self
    }

    pub fn with_maybe_extension_after(mut self, extension_after: Option<usize>) -> Self {
        self.extension_after = extension_after;
        self
    }

    pub fn len(&self) -> usize {
        self.variants.len()
    }

    pub fn is_empty(&self) -> bool {
        self.variants.is_empty()
    }

    pub fn variants(&self) -> impl Iterator<Item = &ChoiceVariant<RS>> {
        self.variants.iter()
    }

    pub fn is_extensible(&self) -> bool {
        self.extension_after.is_some()
    }

    pub fn extension_after_index(&self) -> Option<usize> {
        self.extension_after
    }
}

impl<T: Iterator<Item = Token>> TryFrom<&mut Peekable<T>> for Choice<Unresolved> {
    type Error = Error;

    fn try_from(iter: &mut Peekable<T>) -> Result<Self, Self::Error> {
        iter.next_separator_eq_or_err('{')?;
        let mut choice = Choice {
            variants: Vec::new(),
            extension_after: None,
        };

        loop {
            if let Ok(extension_marker) = iter.next_if_separator_and_eq('.') {
                if choice.variants.is_empty() || choice.extension_after.is_some() {
                    return Err(Error::invalid_position_for_extension_marker(
                        extension_marker,
                    ));
                } else {
                    iter.next_separator_eq_or_err('.')?;
                    iter.next_separator_eq_or_err('.')?;
                    choice.extension_after = Some(choice.variants.len() - 1);
                }
            } else {
                let name = iter.next_text_or_err()?;
                let (token, tag) = Model::<Asn<Unresolved>>::next_with_opt_tag(iter)?;
                let r#type = Model::<Asn<Unresolved>>::read_role_given_text(
                    iter,
                    token.into_text_or_else(Error::no_text)?,
                )?;
                choice.variants.push(ChoiceVariant { name, tag, r#type });
            }

            loop_ctrl_separator!(iter.next_or_err()?);
        }

        Ok(choice)
    }
}

impl Choice<Unresolved> {
    pub fn try_resolve<
        R: Resolver<<Resolved as ResolveState>::SizeType>
            + Resolver<<Resolved as ResolveState>::RangeType>
            + Resolver<<Resolved as ResolveState>::ConstType>
            + Resolver<Type<Unresolved>>,
    >(
        &self,
        resolver: &R,
    ) -> Result<Choice<Resolved>, ResolveError> {
        Ok(Choice {
            variants: self
                .variants
                .iter()
                .map(|v| v.try_resolve(resolver))
                .collect::<Result<Vec<_>, _>>()?,
            extension_after: self.extension_after,
        })
    }
}

#[derive(Debug, Clone, PartialOrd, PartialEq)]
pub struct ChoiceVariant<RS: ResolveState = Resolved> {
    pub name: String,
    pub tag: Option<Tag>,
    pub r#type: Type<RS>,
}

impl<RS: ResolveState> ChoiceVariant<RS> {
    #[cfg(test)]
    pub fn name_type<I: ToString>(name: I, r#type: Type<RS>) -> Self {
        ChoiceVariant {
            name: name.to_string(),
            tag: None,
            r#type,
        }
    }

    pub fn name(&self) -> &str {
        &self.name
    }

    pub fn r#type(&self) -> &Type<RS> {
        &self.r#type
    }
}

impl<RS: ResolveState> TagProperty for ChoiceVariant<RS> {
    fn tag(&self) -> Option<Tag> {
        self.tag
    }

    fn set_tag(&mut self, tag: Tag) {
        self.tag = Some(tag)
    }

    fn reset_tag(&mut self) {
        self.tag = None
    }
}

impl ChoiceVariant<Unresolved> {
    pub fn try_resolve<
        R: Resolver<<Resolved as ResolveState>::SizeType>
            + Resolver<<Resolved as ResolveState>::RangeType>
            + Resolver<<Resolved as ResolveState>::ConstType>
            + Resolver<Type<Unresolved>>,
    >(
        &self,
        resolver: &R,
    ) -> Result<ChoiceVariant<Resolved>, ResolveError> {
        Ok(ChoiceVariant {
            name: self.name.clone(),
            tag: self.tag,
            r#type: self.r#type.try_resolve(resolver)?,
        })
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::model::tag::tests::test_property;

    #[test]
    pub fn test_tag_property_choice_variant() {
        test_property(ChoiceVariant::<Resolved>::name_type(
            "VariantName".to_string(),
            Type::Boolean,
        ));
    }
}