Skip to main content

liberty_io/
util.rs

1// Copyright (c) 2021-2021 Thomas Kramer.
2// SPDX-FileCopyrightText: 2022 Thomas Kramer <code@tkramer.ch>
3//
4// SPDX-License-Identifier: AGPL-3.0-or-later
5
6//! Utility functions for handling Liberty data.
7
8use crate::boolean::BooleanExpr;
9use crate::{Group, Value};
10use std::num::ParseFloatError;
11use std::str::FromStr;
12
13impl Group {
14    /// Find an attribute by name.
15    pub fn get_attribute(&self, name: &str) -> Option<&Vec<Value>> {
16        self.attributes
17            .get(name)
18            .and_then(|attributes| attributes.first())
19    }
20
21    /// Get the value of the first simple attribute with the given name.
22    /// Complex attributes with the same name are ignored.
23    pub fn get_simple_attribute(&self, name: &str) -> Option<&Value> {
24        self.attributes
25            .get(name)
26            .and_then(|attributes| attributes.first())
27            .filter(|values| values.len() == 1) // Take simple attributes only.
28            .and_then(|values| values.first())
29    }
30
31    /// Get the value of the first simple attribute with the given name.
32    /// Simple attributes with the same name are ignored.
33    pub fn get_complex_attribute(&self, name: &str) -> Option<&Vec<Value>> {
34        self.attributes
35            .get(name)
36            .and_then(|attributes| attributes.first())
37            .filter(|values| values.len() != 1) // Take simple attributes only.
38    }
39
40    /// Parse an attribute value as a Boolean function.
41    /// Returns `None` if the attribute cannot be parsed as a Boolean function.
42    fn get_boolean_function(&self, name: &str) -> Option<(BooleanExpr<usize>, Vec<String>)> {
43        self.get_simple_attribute(name).and_then(|attr| match attr {
44            Value::String(s) | Value::QuotedString(s) => {
45                crate::boolean::parse_boolean_function(s.chars().peekable()).ok()
46            }
47            _ => None,
48        })
49    }
50
51    /// Iterate over sub-groups with a specific name.
52    pub fn find_groups_by_name<'a: 'b, 'b>(
53        &'a self,
54        group_name: &'b str,
55    ) -> impl Iterator<Item = &'a Group> + 'b {
56        self.groups.iter().filter(move |g| g.name == group_name)
57    }
58
59    /// Select a group by the group name and its first argument.
60    /// Takes the first one in case there's multiple matches.
61    ///
62    /// For example a `pin(A) {...}` group can be selected with
63    /// `find_group_by_arg(group, "pin", "A")`.
64    pub fn find_group_by_arg(&self, group_name: &str, arg_name: &str) -> Option<&Group> {
65        self.find_groups_by_name(group_name).find(|g| {
66            !g.arguments.is_empty()
67                && if let Value::String(name) = &g.arguments[0] {
68                    name == arg_name
69                } else {
70                    false
71                }
72        })
73    }
74
75    /// Select a cell by name from a library group.
76    /// Takes the first one in case there's multiple matches.
77    ///
78    /// # Panics
79    /// Panics if the group is not a library group.
80    pub fn find_cell(&self, cell_name: &str) -> Option<&Group> {
81        assert_eq!(self.name, "library", "Group must be the top-level library.");
82        self.find_group_by_arg("cell", cell_name)
83    }
84
85    /// Select a pin by name from a cell group.
86    /// Takes the first one in case there's multiple matches.
87    ///
88    /// # Panics
89    /// Panics if the group is not a cell group.
90    pub fn find_pin(&self, pin_name: &str) -> Option<&Group> {
91        assert_eq!(self.name, "cell", "Must be a cell group.");
92        self.find_group_by_arg("pin", pin_name)
93    }
94}
95
96/// Parse a string of the form `"1.2, 1.3"` info a `Vec<f64>`.
97pub fn parse_float_array(s: &str) -> Result<Vec<f64>, ParseFloatError> {
98    // Strip away quotes.
99    let s = s.strip_prefix('"').unwrap_or(s);
100    let s = s.strip_suffix('"').unwrap_or(s);
101
102    let splitted = s.split(',');
103    splitted
104        .map(|s| {
105            // Strip whitespace.
106            let s = s.strip_prefix(|c: char| c.is_whitespace()).unwrap_or(s);
107            let s = s.strip_suffix(|c: char| c.is_whitespace()).unwrap_or(s);
108
109            // Parse to float.
110            f64::from_str(s)
111        })
112        .collect()
113}
114
115#[test]
116fn test_parse_float_array() {
117    let s = r#""1.2, 1.3, 1.4""#;
118    let v = parse_float_array(s).unwrap();
119    assert_eq!(v, vec![1.2, 1.3, 1.4])
120}