Skip to content

Commit

Permalink
Write and parse extensible_after for CHOICE and ENUMERATED #11
Browse files Browse the repository at this point in the history
  • Loading branch information
kellerkindt committed May 7, 2020
1 parent d92f66d commit 95d2953
Show file tree
Hide file tree
Showing 8 changed files with 440 additions and 178 deletions.
177 changes: 148 additions & 29 deletions asn1rs-model/src/ast/attribute.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,36 @@
use super::range::ident_or_literal_or_punct;
use super::range::MaybeRanged;
use super::tag::AttrTag;
use crate::model::{Range, Tag, Type};
use crate::model::{Choice, ChoiceVariant, Enumerated, EnumeratedVariant, Range, Tag, Type};
use std::fmt::Display;
use std::marker::PhantomData;
use std::ops::Deref;
use syn::parenthesized;
use syn::parse::{Parse, ParseBuffer};

#[derive(Debug, Default)]
pub(crate) struct AsnAttribute {
pub(crate) r#type: Option<Type>,
#[derive(Debug)]
pub(crate) struct AsnAttribute<C: Context> {
pub(crate) primary: C::Primary,
pub(crate) tag: Option<Tag>,
pub(crate) number: Option<usize>,
pub(crate) extensible_after: Option<String>,
_c: PhantomData<C>,
}

impl Parse for AsnAttribute {
impl<C: Context> AsnAttribute<C> {
pub fn new(primary: C::Primary) -> Self {
Self {
primary,
tag: None,
extensible_after: None,
_c: Default::default(),
}
}
}

impl<C: Context> Parse for AsnAttribute<C> {
fn parse<'a>(input: &'a ParseBuffer<'a>) -> syn::Result<Self> {
let mut asn = Self::default();
let mut asn = Self::new(C::Primary::parse(input)?);
eof_or_comma(&input, "Primary attribute must be separated by comma")?;

while !input.cursor().eof() {
let lowercase_ident = input
Expand All @@ -25,38 +41,25 @@ impl Parse for AsnAttribute {
.to_lowercase();

match lowercase_ident.as_str() {
// variant number
_ if asn.number.is_none() && lowercase_ident.chars().all(char::is_numeric) => {
asn.number = Some(
lowercase_ident
.parse()
.map_err(|e| input.error(format!("Invalid number: {}", e)))?,
);
}
// field type
_ if asn.r#type.is_none() => {
asn.r#type = Some(parse_type_pre_stepped(&lowercase_ident, input)?);
}
// --- additional properties
"tag" => {
"tag" if C::TAGGABLE => {
let tag = AttrTag::parse(input)?;
asn.tag = Some(tag.0);
}
"extensible_after" if C::EXTENSIBLE => {
let content;
parenthesized!(content in input);
let ident = content
.step(|s| s.ident().ok_or_else(|| content.error("Not a valid ident")))?;
asn.extensible_after = Some(ident.to_string());
}
attribute => {
return Err(
input.error(format!("Unexpected or repeated attribute: `{}`", attribute))
);
}
}

if !input.cursor().eof() && !input.peek(syn::token::Comma) {
return Err(input.error("Attributes must be separated by comma"));
} else if !input.cursor().eof() {
let _ = input.step(|c| {
c.punct()
.ok_or_else(|| input.error("Attributes must be separated by comma"))
})?;
}
eof_or_comma(input, "Attributes must be separated by comma")?;
}
Ok(asn)
}
Expand Down Expand Up @@ -104,3 +107,119 @@ fn parse_type_pre_stepped<'a>(
r#type => Err(input.error(format!("Unexpected attribute: `{}`", r#type))),
}
}

fn eof_or_comma<T: Display>(input: &ParseBuffer, msg: T) -> syn::Result<()> {
if !input.cursor().eof() && !input.peek(syn::token::Comma) {
Err(input.error(msg))
} else if !input.cursor().eof() {
// skip the comma
input
.step(|c| c.punct().ok_or_else(|| input.error(msg)))
.map(drop)
} else {
// eof
Ok(())
}
}

pub trait PrimaryContext: Sized {
fn parse(input: &ParseBuffer<'_>) -> syn::Result<Self>;
}

impl PrimaryContext for Type {
fn parse(input: &ParseBuffer<'_>) -> syn::Result<Self> {
let lowercase_ident = input
.step(|c| {
ident_or_literal_or_punct(*c)
.ok_or_else(|| c.error("Expected type, number or extension marker"))
})?
.to_string()
.to_lowercase();

Ok(parse_type_pre_stepped(&lowercase_ident, input)?)
}
}

impl PrimaryContext for Option<usize> {
fn parse(input: &ParseBuffer<'_>) -> syn::Result<Self> {
input
.step(|c| {
ident_or_literal_or_punct(*c)
.ok_or_else(|| c.error("Expected type, number or extension marker"))
})
.ok()
.as_ref()
.map(ToString::to_string)
.as_ref()
.map(String::as_str)
.map(str::to_lowercase)
.map(|lowercase_ident| {
lowercase_ident
.parse()
.map_err(|e| input.error(format!("Invalid number: {}", e)))
})
.transpose()
}
}

pub trait Context {
type Primary: PrimaryContext;
const EXTENSIBLE: bool;
const TAGGABLE: bool;
}

impl Context for Choice {
type Primary = Type;
const EXTENSIBLE: bool = true;
const TAGGABLE: bool = true;
}

impl Context for ChoiceVariant {
type Primary = Type;
const EXTENSIBLE: bool = false;
const TAGGABLE: bool = true;
}

impl Context for Enumerated {
type Primary = Type;
const EXTENSIBLE: bool = true;
const TAGGABLE: bool = true;
}

impl Context for EnumeratedVariant {
type Primary = Option<usize>;
const EXTENSIBLE: bool = false;
const TAGGABLE: bool = false;
}

pub struct Transparent;
impl Context for Transparent {
type Primary = Type;
const EXTENSIBLE: bool = false;
const TAGGABLE: bool = true;
}

pub struct DefinitionHeader(String);

impl Deref for DefinitionHeader {
type Target = str;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl Context for DefinitionHeader {
type Primary = Self;
const EXTENSIBLE: bool = true;
const TAGGABLE: bool = true;
}

impl PrimaryContext for DefinitionHeader {
fn parse(input: &ParseBuffer<'_>) -> syn::Result<Self> {
input
.step(|c| c.ident().ok_or_else(|| c.error("Expected type identifier")))
.map(|ident| ident.to_string())
.map(DefinitionHeader)
}
}
Loading

0 comments on commit 95d2953

Please sign in to comment.