idf_parser/
library.rs

1use crate::components::{
2    ElectricalComponent, MechanicalComponent, electrical_component, mechanical_component,
3};
4use crate::headers::{LibraryHeader, parse_library_header};
5use nom::Parser;
6use nom::multi::many0;
7
8#[derive(Debug, PartialEq, Clone, Default)]
9pub struct Library {
10    pub header: LibraryHeader,
11    pub electrical_components: Vec<ElectricalComponent>,
12    pub mechanical_components: Vec<MechanicalComponent>,
13}
14
15/// Parses a library emp file which contains detail on electrical and mechanical components.
16/// http://www.aertia.com/docs/priware/IDF_V30_Spec.pdf#page=29
17pub fn parse_library(input: &str) -> Result<Library, nom::Err<nom::error::Error<&str>>> {
18    // Sometimes mechanical components are first, sometimes electrical components are first.
19    let (body, header) = parse_library_header.parse(input)?;
20
21    // Check if body starts with ".ELECTRICAL" or ".MECHANICAL"
22    let (remaining, (electrical_components, mechanical_components)) =
23        if body.starts_with(".ELECTRICAL") {
24            let (remaining, (electrical_components, mechanical_components)) =
25                (many0(electrical_component), many0(mechanical_component)).parse(body)?;
26            (remaining, (electrical_components, mechanical_components))
27        } else if body.starts_with(".MECHANICAL") {
28            let (remaining, (mechanical_components, electrical_components)) =
29                (many0(mechanical_component), many0(electrical_component)).parse(body)?;
30            (remaining, (electrical_components, mechanical_components))
31        } else {
32            return Err(nom::Err::Error(nom::error::Error::new(
33                body,
34                nom::error::ErrorKind::Tag,
35            )));
36        };
37
38    let library = Library {
39        header,
40        electrical_components,
41        mechanical_components,
42    };
43
44    // Check nothing is remaining
45    if !remaining.is_empty() {
46        Err(nom::Err::Error(nom::error::Error::new(
47            remaining,
48            nom::error::ErrorKind::Tag,
49        )))
50    } else {
51        Ok(library)
52    }
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58    use crate::point::Point;
59    use std::collections::HashMap;
60    #[test]
61    fn test_library() {
62        let input = ".HEADER
63LIBRARY_FILE 3.0 \"Sample File Generator\" 10/22/96.16:41:37 1
64.END_HEADER
65.ELECTRICAL
66cs13_a pn-cap THOU 150.0
670 -55.0 55.0 0.0
680 -55.0 55.0 0.0
69PROP CAPACITANCE 100.0
70PROP TOLERANCE 5.0
71.END_ELECTRICAL
72.ELECTRICAL
73cc1210 pn-cc1210 THOU 67.0
740 -40.0 56.0 0.0
75PROP CAPACITANCE 0.1
76PROP TOLERANCE 5.0
77.END_ELECTRICAL
78.ELECTRICAL
79conn_din24 connector THOU 435.0
800 -1400.0 -500.0 0.0
81.END_ELECTRICAL
82.ELECTRICAL
83dip_14w pn-hs346-dip THOU 200.0
840 350.0 50.0 0.0
85.END_ELECTRICAL
86.ELECTRICAL
87plcc_20 pn-pal16l8-plcc THOU 14.0
880 -200.0 240.0 0.0
890 -240.0 200.0 0.0
90.END_ELECTRICAL";
91        let library = parse_library(input).unwrap();
92        assert_eq!(library.electrical_components.len(), 5);
93
94        let header = LibraryHeader {
95            version: 3,
96            system_id: "Sample File Generator".to_string(),
97            date: "10/22/96.16:41:37".to_string(),
98            file_version: 1,
99        };
100
101        let electrical_components = vec![
102            ElectricalComponent {
103                geometry_name: "cs13_a".to_string(),
104                part_number: "pn-cap".to_string(),
105                units: "THOU".to_string(),
106                height: 150.0,
107                outline: vec![
108                    Point {
109                        loop_label: 0,
110                        x: -55.0,
111                        y: 55.0,
112                        angle: 0.0,
113                    },
114                    Point {
115                        loop_label: 0,
116                        x: -55.0,
117                        y: 55.0,
118                        angle: 0.0,
119                    },
120                ],
121                properties: {
122                    let mut props = HashMap::new();
123                    props.insert("CAPACITANCE".to_string(), 100.0);
124                    props.insert("TOLERANCE".to_string(), 5.0);
125                    props
126                },
127            },
128            ElectricalComponent {
129                geometry_name: "cc1210".to_string(),
130                part_number: "pn-cc1210".to_string(),
131                units: "THOU".to_string(),
132                height: 67.0,
133                outline: vec![Point {
134                    loop_label: 0,
135                    x: -40.0,
136                    y: 56.0,
137                    angle: 0.0,
138                }],
139                properties: {
140                    let mut props = HashMap::new();
141                    props.insert("CAPACITANCE".to_string(), 0.1);
142                    props.insert("TOLERANCE".to_string(), 5.0);
143                    props
144                },
145            },
146            ElectricalComponent {
147                geometry_name: "conn_din24".to_string(),
148                part_number: "connector".to_string(),
149                units: "THOU".to_string(),
150                height: 435.0,
151                outline: vec![Point {
152                    loop_label: 0,
153                    x: -1400.0,
154                    y: -500.0,
155                    angle: 0.0,
156                }],
157                properties: HashMap::new(),
158            },
159            ElectricalComponent {
160                geometry_name: "dip_14w".to_string(),
161                part_number: "pn-hs346-dip".to_string(),
162                units: "THOU".to_string(),
163                height: 200.0,
164                outline: vec![Point {
165                    loop_label: 0,
166                    x: 350.0,
167                    y: 50.0,
168                    angle: 0.0,
169                }],
170                properties: HashMap::new(),
171            },
172            ElectricalComponent {
173                geometry_name: "plcc_20".to_string(),
174                part_number: "pn-pal16l8-plcc".to_string(),
175                units: "THOU".to_string(),
176                height: 14.0,
177                outline: vec![
178                    Point {
179                        loop_label: 0,
180                        x: -200.0,
181                        y: 240.0,
182                        angle: 0.0,
183                    },
184                    Point {
185                        loop_label: 0,
186                        x: -240.0,
187                        y: 200.0,
188                        angle: 0.0,
189                    },
190                ],
191                properties: HashMap::new(),
192            },
193        ];
194
195        let expected_library = Library {
196            header,
197            electrical_components,
198            mechanical_components: vec![],
199        };
200
201        assert_eq!(library, expected_library);
202    }
203}