reda-lib 0.1.0

Liberty file library
Documentation
mod semantic;
use std::{collections::HashMap, path::Path};
use reda_lib_macro::model;
pub use semantic::{FromGroup, IntoGroup};
use crate::{ast::{self, Value}, error::LibError};

pub trait Model {
    fn attrs(&self) -> &HashMap<String, Value>;
    fn groups(&self) -> &[Group];
    fn attr(&self, name: &str) -> Option<&Value> {
        self.attrs().get(name)
    }
    fn kind_groups(&self, kind: &str) -> impl IntoIterator<Item = &Group> {
        self.groups().into_iter().filter(move |g| g.kind == kind)
    }
}

#[model]
#[derive(Debug, PartialEq, Clone, Default)]
pub struct Library {
    pub name: String,
    pub cells: Vec<Cell>,
}

impl Library {
    pub fn new<S: Into<String>>(name: S) -> Self {
        Self { 
            name: name.into(),
            ..Default::default()
        }
    }

    pub fn load_file<P: AsRef<Path>>(path: P) -> Result<Self, LibError> {
        let context = std::fs::read_to_string(path)?;
        let ast = ast::parse_liberty(&context)?;
        Self::from_ast(ast)
    }

    pub fn cell(&self, name: &str) -> Option<&Cell> {
        self.cells.iter().find(|c| c.name == name)
    }
}

#[model]
#[derive(Debug, PartialEq, Clone, Default)]
pub struct Cell {
    pub name: String,
    pub pins: Vec<Pin>,
}

impl Cell {
    pub fn new<S: Into<String>>(name: S) -> Self {
        Self { 
            name: name.into(),
            ..Default::default()
        }
    }

    pub fn pin(&self, name: &str) -> Option<&Pin> {
        self.pins.iter().find(|p| p.name == name)
    }
}

#[model]
#[derive(Debug, PartialEq, Clone, Default)]
pub struct Pin {
    pub name: String,
    pub direction: PinDirection,
    pub function: Option<String>,
}

#[derive(Debug, PartialEq, Clone, Default)]
pub enum PinDirection {
    #[default]
    Input,
    Output,
    Inout,
    Internal,
}

impl Pin {
    pub fn new<S: Into<String>>(name: S) -> Self {
        Self { 
            name: name.into(),
            ..Default::default()
        }
    }
}

#[derive(Debug, PartialEq, Clone, Default)]
pub struct Group {
    pub kind: String,
    pub params: Vec<String>,

    pub attrs: HashMap<String, Value>,
    pub groups: Vec<Group>,
}

impl Group {
    pub fn new<S: Into<String>, P: Into<Vec<String>>>(kind: S, params: P) -> Self {
        Self {
            kind: kind.into(),
            params: params.into(),
            ..Default::default()
        }
    }
}

impl Model for Group {
    fn attrs(&self) -> &HashMap<String, Value> {
        &self.attrs
    }

    fn groups(&self) -> &[Group] {
        &self.groups
    }
}

impl Group {
    pub fn expect_name(&mut self) -> Result<String, LibError> {
        if self.params.len() != 1 {
            Err(LibError::LibraryParamLenUnmatch)?
        }
        Ok(self.params[0].clone())
    }

    pub fn expect_ident_attr(&mut self, attr_name: &str) -> Result<String, LibError> {
        if let Some(value) = self.attrs.remove(attr_name) {
            let s = value.into_ident()?;
            Ok(s)
        } else {
            Err(LibError::MissAttr(attr_name.to_string()))
        }
    }

    pub fn expect_ident_attr_opt(&mut self, attr_name: &str) -> Result<Option<String>, LibError> {
        if let Some(value) = self.attrs.remove(attr_name) {
            let s = value.into_ident()?;
            Ok(Some(s))
        } else {
            Ok(None)
        }
    }

    pub fn expect_string_attr_opt(&mut self, attr_name: &str) -> Result<Option<String>, LibError> {
        if let Some(value) = self.attrs.remove(attr_name) {
            let s = value.into_string()?;
            Ok(Some(s))
        } else {
            Ok(None)
        }
    }
}

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

    #[test]
    fn test_cells() {
        let lib = Library::load_file("./data/cells.lib").unwrap();
        let and2 = lib.cell("AND2").unwrap();
        let pin = and2.pin("o").unwrap();
        println!("{:#?}", pin.function);
    }
}