ligen-core 0.1.0

Language Interface Generator Core
Documentation
use crate::Identifier;
use crate::Literal;
use crate::Type;

#[derive(Debug)]
pub enum Attribute {
    Literal(Literal),
    Named(Identifier, Literal),
    Group(Identifier, Attributes)
}

impl Attribute {
    pub fn parse_args(args : &syn::AttributeArgs) -> Attribute {
        if args.len() == 1 {
            Attribute::parse_nested_meta(&args[0])
        } else {
            let mut attributes = Attributes::new();

            for arg in args {
                attributes.push(Attribute::parse_nested_meta(&arg))
            }

            Attribute::Group(Identifier::new(""), attributes)
        }
    }

    pub fn parse_nested_meta(nested_meta : &syn::NestedMeta) -> Attribute {
        match &nested_meta {
            syn::NestedMeta::Meta(meta) => {
                Attribute::parse_meta(&meta)
            },
            syn::NestedMeta::Lit(lit) => {
                Attribute::Literal(Literal::parse(&lit))
            }
        }
    }

    pub fn parse_meta(meta : &syn::Meta) -> Attribute {
        match meta {
            syn::Meta::Path(path) => {
                Attribute::Group(Type::parse_path(&path).identifier, Attributes::new())
            },
            syn::Meta::List(meta_list) => {
                let mut attributes = Attributes::new();

                for nested_meta in &meta_list.nested {
                    attributes.push(Attribute::parse_nested_meta(&nested_meta))
                }

                Attribute::Group(Type::parse_path(&meta_list.path).identifier, attributes)
            },
            syn::Meta::NameValue(name_value) => {
                Attribute::Named(Type::parse_path(&name_value.path).identifier, Literal::parse(&name_value.lit))
            }
        }
    }

    pub fn parse_attribute(attribute : &syn::Attribute) -> Attribute {
        let meta = attribute.parse_meta();
        Attribute::parse_meta(&meta.unwrap())
    }

    pub fn parse_attributes(attributes : &Vec<syn::Attribute>) -> Attribute {
        if attributes.len() == 1 {
            Attribute::parse_attribute(&attributes[0])
        } else {
            let mut attributes_out = Attributes::new();

            for attribute in attributes {
                attributes_out.push(Attribute::parse_attribute(&attribute))
            }

            Attribute::Group(Identifier::new(""), attributes_out)
        }
    }
}

#[derive(Debug)]
pub struct Attributes {
    pub attributes : Vec<Attribute>
}

impl Attributes {
    pub fn new() -> Self {
        Self {
            attributes : Vec::new()
        }
    }

    pub fn from_vec(attributes : Vec<Attribute>) -> Self {
        Self {
            attributes
        }
    }

    pub fn push(&mut self, attribute : Attribute) {
        self.attributes.push(attribute)
    }

    pub fn get_named(&self, name : &str) -> Option<&Literal> {
        for attribute in &self.attributes {
            match attribute {
                Attribute::Named(identifier, lit) => {
                    if identifier.name == name {
                        return Some(&lit)
                    }
                }
                _ => ()
            }
        }
        None
    }
}

pub trait LiteralConverter {
    fn as_bool(&self, default: bool) -> bool;
    fn as_string(&self, default: &str) -> String;
    fn as_char(&self, default: char) -> char;
    fn as_integer(&self, default: i64) -> i64;
    fn as_unsigned_integer(&self, default: u64) -> u64;
    fn as_float(&self, default: f64) -> f64;
}

impl LiteralConverter for Option<&Literal> {
    fn as_bool(&self, default: bool) -> bool {
        if let Some(literal) = self {
            if let Literal::Bool(value) = literal {
                *value
            } else { default }
        } else { default }
    }

    fn as_string(&self, default: &str) -> String {
        if let Some(literal) = self {
            if let Literal::String(value) = literal {
                value.clone()
            } else { String::from(default) }
        } else { String::from(default) }
    }

    fn as_char(&self, default: char) -> char {
        if let Some(literal) = self {
            if let Literal::Char(value) = literal {
                *value
            } else { default }
        } else { default }
    }

    fn as_integer(&self, default: i64) -> i64 {
        if let Some(literal) = self {
            if let Literal::Integer(value) = literal {
                *value
            } else { default }
        } else { default }
    }

    fn as_unsigned_integer(&self, default: u64) -> u64 {
        if let Some(literal) = self {
            if let Literal::UnsignedInteger(value) = literal {
                *value
            } else { default }
        } else { default }
    }

    fn as_float(&self, default: f64) -> f64 {
        if let Some(literal) = self {
            if let Literal::Float(value) = literal {
                *value
            } else { default }
        } else { default }
    }
}


impl fmt::Display for Attribute {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Attribute::Literal(literal) => write!(f, "\"{}\"", literal.to_string()),
            Attribute::Named(identifier, literal) => write!(f, "{} = \"{}\"", identifier.name, literal.to_string()),
            Attribute::Group(identifier, group) => {
                if group.attributes.len() > 0 {
                    write!(f, "{}({})", identifier.name, group)
                } else {
                    write!(f, "{}", identifier.name)
                }
            }
        }
    }
}

use std::fmt;
impl fmt::Display for Attributes {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut result = write!(f, "");
        for (i, attribute) in self.attributes.iter().enumerate() {
            let comma = if i == self.attributes.len() - 1 { "" } else { ", " };
            result = write!(f, "{}{}", attribute, comma);
        }
        result
    }
}