idf_parser/
headers.rs

1use nom::branch::alt;
2use nom::sequence::{delimited, terminated};
3
4use crate::primitives::{quote_string, ws};
5use crate::{parse_section, ws_separated};
6use nom::Err::Error;
7use nom::Parser;
8use nom::bytes::complete::{is_not, tag};
9use nom::character::complete::not_line_ending;
10use nom::error::ErrorKind;
11use nom::{IResult, error};
12
13#[derive(PartialEq, Debug, Clone, Default, PartialOrd)]
14pub struct LibraryHeader {
15    pub version: u32, // which IDF version, should be 3.0
16    pub system_id: String,
17    pub date: String, // We don't decompose this for now
18    pub file_version: u32,
19}
20
21#[derive(Debug, PartialEq, Clone, Default, PartialOrd)]
22pub struct BoardPanelHeader {
23    pub file_type: String, // BOARD_FILE or PANEL_FILE
24    pub version: u32,      // which IDF version, should be 3.0
25    pub system_id: String,
26    pub date: String, // We don't decompose this for now
27    pub file_version: u32,
28    pub board_name: String,
29    pub units: String,
30}
31
32/// Parses the first line of the header section.
33fn header_metadata(input: &str) -> IResult<&str, (String, u32, String, String, u32)> {
34    let (remaining, (file_type, version, system_id, date, file_version)) = (
35        ws(alt((
36            tag("PANEL_FILE"),
37            tag("LIBRARY_FILE"),
38            tag("BOARD_FILE"),
39        ))), // file type
40        ws(terminated(tag("3"), tag(".0"))),  // version
41        ws(alt((quote_string, is_not(" ")))), // system id
42        ws(is_not(" ")),                      // date
43        ws(not_line_ending),                  // file version
44    )
45        .parse(input)?;
46
47    Ok((
48        remaining,
49        (
50            file_type.to_string(),
51            version.parse::<u32>().unwrap(),
52            system_id.to_string(),
53            date.to_string(),
54            file_version.parse::<u32>().unwrap(),
55        ),
56    ))
57}
58
59/// Parses the header of a board or panel emn file.
60/// http://www.aertia.com/docs/priware/IDF_V30_Spec.pdf#page=8
61///
62/// # Example
63///
64/// ```
65/// use idf_parser::headers::{parse_board_or_panel_header, BoardPanelHeader};
66/// let input = ".HEADER
67/// BOARD_FILE 3.0 \"Sample File Generator\" 10/22/96.16:02:44 1
68/// sample_board THOU
69/// .END_HEADER";
70///
71/// let (remaining, header) = parse_board_or_panel_header(input).unwrap();
72/// assert_eq!(header.units, "THOU");
73/// ```
74pub fn parse_board_or_panel_header(input: &str) -> IResult<&str, BoardPanelHeader> {
75    let (remaining, (metadata, (board_name, units))) = parse_section!(
76        "HEADER",
77        (
78            header_metadata,
79            ws_separated!((is_not(" "), alt((tag("THOU"), tag("MM"))))),
80        )
81    )
82    .parse(input)?;
83
84    let (file_type, version, system_id, date, file_version) = metadata;
85
86    let header = BoardPanelHeader {
87        file_type,
88        version,
89        system_id,
90        date,
91        file_version,
92        board_name: board_name.to_string(),
93        units: units.to_string(),
94    };
95    Ok((remaining, header))
96}
97
98/// Parses the header of a library emn file.
99/// http://www.aertia.com/docs/priware/IDF_V30_Spec.pdf#page=29
100///
101/// # Example
102///
103/// ```
104/// use idf_parser::headers::{parse_library_header, LibraryHeader};
105///
106/// let input = ".HEADER
107/// LIBRARY_FILE 3.0 \"Sample File Generator\" 10/22/96.16:41:37 1
108/// .END_HEADER\n";
109///
110/// let (remaining, header) = parse_library_header(input).unwrap();
111/// assert_eq!(header.date, "10/22/96.16:41:37");
112/// ```
113pub fn parse_library_header(input: &str) -> IResult<&str, LibraryHeader> {
114    let (remaining, (file_type, version, system_id, date, file_version)) =
115        parse_section!("HEADER", header_metadata).parse(input)?;
116
117    if file_type != "LIBRARY_FILE" {
118        return Err(Error(error::Error::new(input, ErrorKind::Tag)));
119    }
120
121    let header = LibraryHeader {
122        version,
123        system_id,
124        date,
125        file_version,
126    };
127    Ok((remaining, header))
128}
129
130#[cfg(test)]
131mod tests {
132    use super::*;
133    #[test]
134    fn test_parse_header_metadata() {
135        let input = "BOARD_FILE 3.0 \"Sample File Generator\" 10/22/96.16:02:44 1\n";
136        let (remaining, (file_type, version, system_id, date, file_version)) =
137            header_metadata(input).unwrap();
138        assert_eq!(remaining, "");
139        assert_eq!(file_type, "BOARD_FILE");
140        assert_eq!(version, 3);
141        assert_eq!(system_id, "Sample File Generator");
142        assert_eq!(date, "10/22/96.16:02:44");
143        assert_eq!(file_version, 1);
144    }
145
146    #[test]
147    fn test_parse_board_header() {
148        let input = ".HEADER
149BOARD_FILE 3.0 \"Sample File Generator\" 10/22/96.16:02:44 1
150sample_board THOU
151.END_HEADER\n other nonsense";
152
153        let (remaining, header) = parse_board_or_panel_header(input).unwrap();
154        assert_eq!(remaining, "other nonsense");
155        let example = BoardPanelHeader {
156            file_type: "BOARD_FILE".to_string(),
157            version: 3,
158            system_id: "Sample File Generator".to_string(),
159            date: "10/22/96.16:02:44".to_string(),
160            file_version: 1,
161            board_name: "sample_board".to_string(),
162            units: "THOU".to_string(),
163        };
164
165        assert_eq!(header.file_type, example.file_type);
166    }
167
168    #[test]
169    fn test_parse_board_header_isol() {
170        let input = ".HEADER
171BOARD_FILE         3.0  \"allegro 16.2\"  2010/04/27.15:29:26  1
172ISOL_mk.brd  THOU
173.END_HEADER";
174
175        let (remaining, header) = parse_board_or_panel_header(input).unwrap();
176
177        let example = BoardPanelHeader {
178            file_type: "BOARD_FILE".to_string(),
179            version: 3,
180            system_id: "allegro 16.2".to_string(),
181            date: "2010/04/27.15:29:26".to_string(),
182            file_version: 1,
183            board_name: "ISOL_mk.brd".to_string(),
184            units: "THOU".to_string(),
185        };
186        assert_eq!(remaining, "");
187        assert_eq!(header, example);
188    }
189
190    #[test]
191    fn test_parse_board_header_beaglebone() {
192        let input = ".HEADER
193BOARD_FILE         3.0  allegro_16.5  2012/12/10.15:43:34  1
194BEAGLEBONE_REVC2.brd  THOU
195.END_HEADER";
196
197        let (remaining, header) = parse_board_or_panel_header(input).unwrap();
198
199        let example = BoardPanelHeader {
200            file_type: "BOARD_FILE".to_string(),
201            version: 3,
202            system_id: "allegro_16.5".to_string(),
203            date: "2012/12/10.15:43:34".to_string(),
204            file_version: 1,
205            board_name: "BEAGLEBONE_REVC2.brd".to_string(),
206            units: "THOU".to_string(),
207        };
208        assert_eq!(remaining, "");
209        assert_eq!(header, example);
210    }
211    #[test]
212    fn test_parse_library_header() {
213        let input = ".HEADER
214LIBRARY_FILE 3.0 \"Sample File Generator\" 10/22/96.16:41:37 1
215.END_HEADER\n";
216
217        let (remaining, header) = parse_library_header(input).unwrap();
218        assert_eq!(remaining, "");
219        assert_eq!(header.version, 3);
220        assert_eq!(header.system_id, "Sample File Generator");
221        assert_eq!(header.date, "10/22/96.16:41:37");
222        assert_eq!(header.file_version, 1);
223    }
224
225    #[test]
226    fn test_library_header_isol() {
227        let input = ".HEADER
228LIBRARY_FILE         3.0  \"allegro 16.2\"  2010/04/27.15:29:26  1
229.END_HEADER";
230        let (remaining, header) = parse_library_header(input).unwrap();
231
232        let example = LibraryHeader {
233            version: 3,
234            system_id: "allegro 16.2".to_string(),
235            date: "2010/04/27.15:29:26".to_string(),
236            file_version: 1,
237        };
238        assert_eq!(remaining, "");
239        assert_eq!(header, example);
240    }
241
242    #[test]
243    fn test_library_header_beaglebone() {
244        let input = ".HEADER
245LIBRARY_FILE         3.0  allegro_16.5  2012/12/10.15:43:34  1
246.END_HEADER";
247        let (remaining, header) = parse_library_header(input).unwrap();
248
249        let example = LibraryHeader {
250            version: 3,
251            system_id: "allegro_16.5".to_string(),
252            date: "2012/12/10.15:43:34".to_string(),
253            file_version: 1,
254        };
255        assert_eq!(remaining, "");
256        assert_eq!(header, example);
257    }
258}