reda-lib 0.2.0

Liberty file library
Documentation
use std::fmt::Display;
use std::{collections::HashMap, str::FromStr};
use crate::error::{LibErrorContext, LibError};
use crate::model::{LibAst, LibCell, LibGroup, LibLibrary, LibPin, LibPinDirection};

pub trait FromLibGroup: Sized {
    type Error: Sized;
    fn from_group(group: LibGroup) -> Result<Self, Self::Error>;
}

impl LibLibrary {
    pub fn from_ast(ast: LibAst) -> Result<Self, LibError> {
        let group = LibGroup::from_ast(ast).context(|| "semantic root ast to group")?;
        Self::from_group(group)
    }
}

impl FromLibGroup for LibLibrary {
    type Error = LibError;
    fn  from_group(group: LibGroup) -> Result<Self, LibError> {
        let mut group = group;
        let name = group.expect_name().context(|| "group name")?;
        let (cell_groups, groups) = group.groups.into_iter().partition(|g| g.kind == "cell");
        
        // collect cells
        let mut cells = Vec::new();
        for cell_group in cell_groups {
            cells.push(LibCell::from_group(cell_group).context(|| "semantic cell")?);
        }

        Ok(Self {
            name,
            attrs: group.attrs,
            groups,
            cells,
        })
    }
}

impl FromLibGroup for LibCell {
    type Error = LibError;
    fn from_group(group: LibGroup) -> Result<Self, LibError> {
        let mut group = group;
        let name = group.expect_name().context(|| "cell name")?;
        let err_context = |msg: &str| {
            format!("{} in {} cell", msg, &name)
        };

        let (pin_groups, groups) = group.groups.into_iter().partition(|g| g.kind == "pin");

        // collect cells
        let mut pins = Vec::new();
        for cell_group in pin_groups {
            pins.push(LibPin::from_group(cell_group).context(|| err_context("semantic pin"))?);
        }

        Ok(Self {
            name,
            pins,
            attrs: group.attrs,
            groups,
        })
    }
}

impl FromLibGroup for LibPin {
    type Error = LibError;
    fn from_group(group: LibGroup) -> Result<Self, LibError> {
        let mut group = group;
        let name = group.expect_name().context(|| "pin name")?;
        let err_context = |msg: &str| {
            format!("{} in `{}` pin", msg, &name)
        };

        let direction = match group.expect_ident_attr_opt("direction")
            .context(|| err_context("semantic `direction` attr"))? 
        {
            Some(s) => LibPinDirection::from_str(&s)?,
            None => LibPinDirection::default(),
        };
        let function = group.expect_string_attr_opt("function")
            .context(|| err_context("semantic `function` attr"))?;
    
        Ok(Self {
            name,
            direction,
            function,
            attrs: group.attrs,
            groups: group.groups,
        })
    }
}

impl FromStr for LibPinDirection {
    type Err = LibError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "input" => Ok(Self::Input),
            "output" => Ok(Self::Output),
            "inout" => Ok(Self::Inout),
            "internal" => Ok(Self::Internal),
            s => Err(LibError::UnsupportDirection(s.to_string()))
        }   
    }
}

impl Display for LibPinDirection {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Input => write!(f, "input"),
            Self::Output => write!(f, "output"),
            Self::Inout => write!(f, "inout"),
            Self::Internal => write!(f, "internal"),
        }
    }
}

impl LibGroup {
    pub fn from_ast(ast: LibAst) -> Result<Self, LibError> {
        match ast {
            LibAst::LibGroup { kind, params, items } => {
                let mut attrs = HashMap::new();
                let mut groups = Vec::new();
                
                for item_ast in items {
                    if let LibAst::Attr { key, value } = item_ast {
                        attrs.insert(key, value);
                    } else {
                        groups.push(LibGroup::from_ast(item_ast)?);
                    }
                }

                Ok(Self {
                    kind, 
                    params,
                    attrs,
                    groups
                })
            }
            LibAst::Attr { key:_, value:_ } => Err(LibError::ExceptLibGroup),
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_pin_into_group() {
        let mut pin = LibPin::new("my_pin");
        pin.groups.push(LibGroup::new("gtype", vec!["gname".to_string()]));
        // let group = pin.into_group();
        // assert_eq!(group.type_, "pin");
        // assert_eq!(group.name, "my_pin");
        // assert_eq!(group.groups.len(), 1);
    }

    #[test]
    fn test_pin_from_group() {
        let mut group = LibGroup::new("pin", vec!["a".to_string()]);
        group.groups.push(LibGroup::new("gtype", vec!["gname".to_string()]));
        let pin = LibPin::from_group(group).unwrap();
        assert_eq!(pin.name, "a");
        assert_eq!(pin.groups.len(), 1);
    }

    #[test]
    fn test_cell_into_group() {
        let mut cell = LibCell::new("my_cell");
        cell.groups.push(LibGroup::new("gtype", vec!["gname".to_string()]));
        cell.pins.push(LibPin::new("a"));
        cell.pins.push(LibPin::new("b"));
        // let group = cell.into_group();
        // assert_eq!(group.type_, "cell");
        // assert_eq!(group.name, "my_cell");
        // assert_eq!(group.groups.len(), 3);
    }

    #[test]
    fn test_cell_from_group() {
        let mut group = LibGroup::new("cell", vec!["AND2".to_string()]);
        group.groups.push(LibGroup::new("gtype", vec!["gname".to_string()]));
        group.groups.push(LibGroup::new("pin", vec!["a".to_string()]));
        group.groups.push(LibGroup::new("pin", vec!["b".to_string()]));
        let cell = LibCell::from_group(group).unwrap();
        assert_eq!(cell.name, "AND2");
        assert_eq!(cell.groups.len(), 1);
        assert_eq!(cell.pins.len(), 2);
    }

    #[test]
    fn test_library_into_group() {
        let mut lib = LibLibrary::new("my_lib");
        lib.groups.push(LibGroup::new("gtype", vec!["gname".to_string()]));
        lib.cells.push(LibCell::new("AND2"));
        lib.cells.push(LibCell::new("NAND2"));
        // let group = lib.into_group();
        // assert_eq!(group.type_, "library");
        // assert_eq!(group.name, "my_lib");
        // assert_eq!(group.groups.len(), 3);
    }

    #[test]
    fn test_lib_from_group() {
        let mut group = LibGroup::new("library", vec!["mylib".to_string()]);
        group.groups.push(LibGroup::new("gtype", vec!["gname".to_string()]));
        let mut cell = LibGroup::new("cell", vec!["AND2".to_string()]);
        cell.groups.push(LibGroup::new("pin", vec!["a".to_string()]));
        cell.groups.push(LibGroup::new("pin", vec!["b".to_string()]));
        group.groups.push(cell);
        let lib = LibLibrary::from_group(group).unwrap();
        assert_eq!(lib.name, "mylib");
        assert_eq!(lib.groups.len(), 1);
        assert_eq!(lib.cells.len(), 1);
        let converted_cell = lib.cell("AND2").unwrap();
        assert_eq!(converted_cell.name, "AND2");
        assert_eq!(converted_cell.groups.len(), 0);
        assert_eq!(converted_cell.pins.len(), 2);
    }
}