bmx 0.0.2

Binary modeling expressions
Documentation
use std::{collections::HashMap, fmt, iter::FromIterator};

use crate::R;

#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct Ident(String);

impl Ident {
    pub fn as_str(&self) -> &str {
        &self.0
    }
}

pub type TypeExpr = crate::TypeExpr<Ident, crate::Expr<R<Ident>>>;
pub type Expr = crate::Expr<R<Ident>>;
pub type Field = crate::Field<TypeExpr>;
pub type NodeApp = crate::NodeApp<Ident, crate::Expr<R<Ident>>>;

// FIXME: Limit allowed identifiers
impl std::str::FromStr for Ident {
    type Err = std::convert::Infallible;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(Ident(s.into()))
    }
}

impl fmt::Display for Ident {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.as_str())
    }
}

#[derive(Debug, Clone)]
pub struct Path(Vec<Ident>);

impl Path {
    pub fn len(&self) -> usize {
        self.0.len()
    }

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

    pub fn split_branch(self) -> Result<(Ident, Path, Ident), Self> {
        if self.len() >= 2 {
            let mut components = self.0;
            let name = components.pop().unwrap();
            let root = components.remove(0);
            Ok((root, Path(components), name))
        } else {
            Err(self)
        }
    }
}

impl<'a> FromIterator<&'a Ident> for Path {
    fn from_iter<T>(iter: T) -> Path
    where
        T: IntoIterator<Item = &'a Ident>,
    {
        Path(iter.into_iter().cloned().collect())
    }
}

impl fmt::Display for Path {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for (i, ident) in self.0.iter().enumerate() {
            write!(f, "{}", ident)?;
            if i + 1 < self.0.len() {
                f.write_str(".")?;
            }
        }
        Ok(())
    }
}

impl std::str::FromStr for Path {
    type Err = std::convert::Infallible;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        // FIXME: limit identifiers
        Ok(Path(s.split('.').map(|s| s.parse().unwrap()).collect()))
    }
}

impl<'a> IntoIterator for &'a Path {
    type IntoIter = std::slice::Iter<'a, Ident>;
    type Item = &'a Ident;

    fn into_iter(self) -> Self::IntoIter {
        self.0.iter()
    }
}

impl IntoIterator for Path {
    type IntoIter = std::vec::IntoIter<Ident>;
    type Item = Ident;

    fn into_iter(self) -> Self::IntoIter {
        self.0.into_iter()
    }
}

#[derive(Debug, Clone)]
pub struct Root {
    pub name: Ident,
    pub description: Option<String>,
    pub fields: Vec<Field>,
}

#[derive(Debug, Clone)]
pub struct Branch {
    pub root: Ident,
    pub path: Path,
    pub name: Ident,
    pub description: Option<String>,
    pub condition: Expr,
    pub implies: Vec<NodeApp>,
    pub fields: Vec<Field>,
    pub properties: HashMap<String, lexpr::Value>,
}

#[derive(Debug, Clone)]
pub struct Enum {
    pub name: Ident,
    pub description: Option<String>,
    pub discriminants: Vec<Field>,
    pub variants: Vec<Variant>,
}

#[derive(Debug, Clone)]
pub struct Variant {
    pub name: Ident,
    pub condition: Expr,
    pub fields: Vec<Field>,
    pub properties: HashMap<String, lexpr::Value>,
}