1use nom::branch::alt;
2
3use crate::primitives::ws;
4use crate::{parse_section, ws_separated};
5use nom::IResult;
6use nom::Parser;
7use nom::bytes::complete::{is_not, tag};
8use nom::character::complete::not_line_ending;
9use nom::multi::many0;
10use nom::number::complete::float;
11use nom::sequence::delimited;
12
13#[derive(Debug, PartialEq, Clone, Default, PartialOrd)]
15pub struct ComponentPlacement {
16 pub package_name: String,
17 pub part_number: String,
18 pub reference_designator: String, pub x: f32,
20 pub y: f32,
21 pub mounting_offset: f32, pub rotation_angle: f32, pub board_side: String, pub placement_status: String, }
26
27pub fn component_placement(input: &str) -> IResult<&str, ComponentPlacement> {
38 let (
39 remaining,
40 (
41 package_name,
42 part_number,
43 reference_designator,
44 x,
45 y,
46 mounting_offset,
47 rotation_angle,
48 board_side,
49 placement_status,
50 ),
51 ) = ws_separated!((
52 is_not(" "), is_not(" "), not_line_ending, float, float, float, float, alt((tag("TOP"), tag("BOTTOM"))), alt((tag("PLACED"), tag("UNPLACED"), tag("ECAD"), tag("MCAD"),)) ))
62 .parse(input)?;
63
64 let component_placement = ComponentPlacement {
65 package_name: package_name.to_string(),
66 part_number: part_number.to_string(),
67 reference_designator: reference_designator.to_string(),
68 x,
69 y,
70 mounting_offset,
71 rotation_angle,
72 board_side: board_side.to_string(),
73 placement_status: placement_status.to_string(),
74 };
75
76 Ok((remaining, component_placement))
77}
78
79pub fn parse_component_placement_section(input: &str) -> IResult<&str, Vec<ComponentPlacement>> {
94 parse_section!("PLACEMENT", ws(many0(component_placement))).parse(input)
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100 #[test]
101 fn test_component_placement() {
102 let input = "cs13_a pn-cap C1\n4000.0 1000.0 100.0 0.0 TOP PLACED";
103 let expected = ComponentPlacement {
104 package_name: "cs13_a".to_string(),
105 part_number: "pn-cap".to_string(),
106 reference_designator: "C1".to_string(),
107 x: 4000.0,
108 y: 1000.0,
109 mounting_offset: 100.0,
110 rotation_angle: 0.0,
111 board_side: "TOP".to_string(),
112 placement_status: "PLACED".to_string(),
113 };
114 let result = component_placement(input);
115 let (_, component_placement) = result.unwrap();
117
118 assert_eq!(component_placement, expected);
119 }
120 #[test]
121 fn test_component_placement_section() {
122 let input = ".PLACEMENT
123cs13_a pn-cap C1
1244000.0 1000.0 100.0 0.0 TOP PLACED
125cc1210 pn-cc1210 C2
1263000.0 3500.0 0.0 0.0 TOP UNPLACED
127cc1210 pn-cc1210 C3
1283200.0 1800.0 0.0 0.0 BOTTOM MCAD
129dip_14w pn-hs346-dip U4
1302200.0 2500.0 0.0 270.0 TOP ECAD
131.END_PLACEMENT";
132
133 let expected = vec![
134 ComponentPlacement {
135 package_name: "cs13_a".to_string(),
136 part_number: "pn-cap".to_string(),
137 reference_designator: "C1".to_string(),
138 x: 4000.0,
139 y: 1000.0,
140 mounting_offset: 100.0,
141 rotation_angle: 0.0,
142 board_side: "TOP".to_string(),
143 placement_status: "PLACED".to_string(),
144 },
145 ComponentPlacement {
146 package_name: "cc1210".to_string(),
147 part_number: "pn-cc1210".to_string(),
148 reference_designator: "C2".to_string(),
149 x: 3000.0,
150 y: 3500.0,
151 mounting_offset: 0.0,
152 rotation_angle: 0.0,
153 board_side: "TOP".to_string(),
154 placement_status: "UNPLACED".to_string(),
155 },
156 ComponentPlacement {
157 package_name: "cc1210".to_string(),
158 part_number: "pn-cc1210".to_string(),
159 reference_designator: "C3".to_string(),
160 x: 3200.0,
161 y: 1800.0,
162 mounting_offset: 0.0,
163 rotation_angle: 0.0,
164 board_side: "BOTTOM".to_string(),
165 placement_status: "MCAD".to_string(),
166 },
167 ComponentPlacement {
168 package_name: "dip_14w".to_string(),
169 part_number: "pn-hs346-dip".to_string(),
170 reference_designator: "U4".to_string(),
171 x: 2200.0,
172 y: 2500.0,
173 mounting_offset: 0.0,
174 rotation_angle: 270.0,
175 board_side: "TOP".to_string(),
176 placement_status: "ECAD".to_string(),
177 },
178 ];
179
180 let (remaining, component_placements) = parse_component_placement_section(input).unwrap();
181 assert_eq!(remaining, "");
182 assert_eq!(component_placements, expected);
183 }
184 #[test]
185 fn test_invalid_component_placement() {
186 let input = "cs13_a pn-cap C1\n4000.0 1000.0 100.0 hi hi 0.0 TOP PLACED\n";
188 let result = component_placement(input);
189 assert!(!result.is_ok());
190 }
191}