idf_parser/
drilled_holes.rs

1use nom::branch::alt;
2use nom::sequence::delimited;
3
4use crate::outlines::owner;
5use crate::primitives::ws;
6use crate::{parse_section, ws_separated};
7use nom::IResult;
8use nom::Parser;
9use nom::bytes::complete::{is_not, tag};
10use nom::multi::many0;
11use nom::number::complete::float;
12
13/// Represents a drilled hole in the IDF format.
14/// http://www.aertia.com/docs/priware/IDF_V30_Spec.pdf#page=25
15#[derive(Debug, PartialEq, Clone, Default, PartialOrd)]
16pub struct Hole {
17    pub diameter: f32,
18    pub x: f32,                  // absolute x coordinate
19    pub y: f32,                  // absolute y coordinate
20    pub plating_style: String, // PTH: Plated (conducting) through hole, NPTH: Non-plated (non-conducting) through hole
21    pub associated_part: String, // BOARD, NOREFDES, PANEL, Reference designator
22    pub hole_type: String,     // PIN, VIA, MTG, TOOL, Other
23    pub owner: String,         // The owner of the hole ECAD, MCAD, UNOWNED
24}
25
26/// Parses a single drilled hole from the input string.
27/// http://www.aertia.com/docs/priware/IDF_V30_Spec.pdf#page=25
28///
29/// # Example
30///
31/// ```
32/// use idf_parser::drilled_holes::{drilled_hole, Hole};
33/// let input = "30.0 1600.0 100.0 PTH J1 PIN ECAD";
34///
35/// let (remaining, hole) = drilled_hole(input).unwrap();
36/// assert_eq!(hole.x, 1600.0);
37/// ```
38pub fn drilled_hole(input: &str) -> IResult<&str, Hole> {
39    let (remaining, (diameter, x, y, plating_style, associated_part, hole_type, owner)) =
40        ws_separated!((
41            float,                                                  // diameter
42            float,                                                  // x coordinate
43            float,                                                  // y coordinate
44            alt((tag("PTH"), tag("NPTH"))),                         // plating style
45            is_not(" "),                                            // associated part
46            alt((tag("PIN"), tag("VIA"), tag("MTG"), tag("TOOL"))), // hole type TODO add support user defined hole types
47            owner
48        ))
49        .parse(input)?;
50
51    let hole = Hole {
52        diameter,
53        x,
54        y,
55        plating_style: plating_style.to_string(),
56        associated_part: associated_part.to_string(),
57        hole_type: hole_type.to_string(),
58        owner: owner.to_string(),
59    };
60    Ok((remaining, hole))
61}
62
63/// Parses a section of drilled holes from the input string.
64/// http://www.aertia.com/docs/priware/IDF_V30_Spec.pdf#page=25
65///
66/// # Example
67///
68/// ```
69/// use idf_parser::drilled_holes::{parse_drilled_holes_section, Hole};
70///         let input = ".DRILLED_HOLES
71/// 30.0 1800.0 100.0 PTH J1 PIN ECAD
72/// 30.0 1700.0 100.0 PTH J1 PIN ECAD
73/// 30.0 1600.0 100.0 PTH J1 PIN ECAD
74/// 93.0 0.0 4800.0 NPTH BOARD TOOL MCAD
75/// 93.0 0.0 0.0 PTH BOARD MTG UNOWNED
76/// .END_DRILLED_HOLES";
77///
78/// let (remaining, holes) = parse_drilled_holes_section(input).unwrap();
79/// ```
80pub fn parse_drilled_holes_section(input: &str) -> IResult<&str, Vec<Hole>> {
81    parse_section!("DRILLED_HOLES", many0(drilled_hole)).parse(input)
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87    #[test]
88    fn test_drilled_hole() {
89        let input = "30.0 1800.0 100.0 PTH J1 PIN ECAD";
90
91        let (remaining, hole) = drilled_hole(input).unwrap();
92        assert_eq!(remaining, "");
93        assert_eq!(hole.diameter, 30.0);
94        assert_eq!(hole.x, 1800.0);
95        assert_eq!(hole.y, 100.0);
96        assert_eq!(hole.plating_style, "PTH");
97        assert_eq!(hole.associated_part, "J1");
98        assert_eq!(hole.hole_type, "PIN");
99        assert_eq!(hole.owner, "ECAD");
100    }
101
102    #[test]
103    fn test_drilled_holes_section() {
104        let input = ".DRILLED_HOLES
10530.0 1800.0 100.0 PTH J1 PIN ECAD
10630.0 1700.0 100.0 PTH J1 PIN ECAD
10730.0 1600.0 100.0 PTH J1 PIN ECAD
10893.0 0.0 4800.0 NPTH BOARD TOOL MCAD
10993.0 0.0 0.0 PTH NOREFDES MTG UNOWNED
110123.0 0.0 0.0 PTH NOREFDES VIA UNOWNED
111.END_DRILLED_HOLES";
112        let (remaining, holes) = parse_drilled_holes_section(input).unwrap();
113        assert_eq!(remaining, "");
114        assert_eq!(holes.len(), 6);
115        assert_eq!(holes[0].diameter, 30.0);
116        assert_eq!(holes[0].x, 1800.0);
117        assert_eq!(holes[0].y, 100.0);
118        assert_eq!(holes[0].plating_style, "PTH");
119        assert_eq!(holes[0].associated_part, "J1");
120        assert_eq!(holes[0].hole_type, "PIN");
121        assert_eq!(holes[0].owner, "ECAD");
122        assert_eq!(holes[1].plating_style, "PTH");
123        assert_eq!(holes[3].plating_style, "NPTH");
124        assert_eq!(holes[4].associated_part, "NOREFDES");
125        assert_eq!(holes[5].hole_type, "VIA");
126    }
127
128    #[test]
129    fn test_drilled_holes_section_2() {
130        let input = ".DRILLED_HOLES\r\n.END_DRILLED_HOLES\r\n";
131        let (remaining, holes) = parse_drilled_holes_section(input).unwrap();
132        assert_eq!(remaining, "");
133        assert_eq!(holes.len(), 0);
134    }
135}