use nom::branch::alt;
use nom::sequence::delimited;
use crate::point::{Point, point};
use crate::primitives::ws;
use crate::{parse_section, ws_separated};
use nom::IResult;
use nom::Parser;
use nom::bytes::complete::{is_not, tag};
use nom::character::complete::not_line_ending;
use nom::multi::many1;
use nom::number::complete::float;
#[derive(Clone, Debug, PartialEq, Default, PartialOrd)]
pub struct BoardPanelOutline {
pub owner: String, pub thickness: f32,
pub outline: Vec<Point>,
}
pub fn parse_board_panel_outline(input: &str) -> IResult<&str, BoardPanelOutline> {
fn interior_contents(input: &str) -> IResult<&str, (&str, f32, Vec<Point>)> {
(ws(owner), ws(float), ws(many1(ws(point)))).parse(input)
}
let (remaining, (owner, thickness, outline)) = ws(alt((
parse_section!("BOARD_OUTLINE", interior_contents),
parse_section!("PANEL_OUTLINE", interior_contents),
)))
.parse(input)?;
Ok((
remaining,
BoardPanelOutline {
owner: owner.to_string(),
thickness,
outline,
},
))
}
#[derive(Debug, PartialEq, Clone, Default, PartialOrd)]
pub struct OtherOutline {
pub owner: String, pub id: String,
pub extrude_thickness: f32,
pub board_side: String, pub outline: Vec<Point>,
}
pub fn parse_other_outline(input: &str) -> IResult<&str, OtherOutline> {
let (remaining, (owner, id, extrude_thickness, board_side, outline)) = parse_section!(
"OTHER_OUTLINE",
ws_separated!((
owner, is_not(" "), float, not_line_ending, many1(ws(point)) ))
)
.parse(input)?;
Ok((
remaining,
OtherOutline {
owner: owner.to_string(),
id: id.to_string(),
extrude_thickness,
board_side: board_side.to_string(),
outline,
},
))
}
#[derive(Debug, PartialEq, Clone, Default, PartialOrd)]
pub struct RoutingOutline {
pub owner: String, pub routing_layers: String, pub outline: Vec<Point>,
}
pub fn parse_routing_outline(input: &str) -> IResult<&str, RoutingOutline> {
let (remaining, (owner, routing_layers, outline)) = parse_section!(
"ROUTE_OUTLINE",
ws_separated!((owner, not_line_ending, many1(ws(point))))
)
.parse(input)?;
Ok((
remaining,
RoutingOutline {
owner: owner.to_string(),
routing_layers: routing_layers.to_string(),
outline,
},
))
}
#[derive(Debug, PartialEq, Clone, Default, PartialOrd)]
pub struct PlacementOutline {
pub owner: String, pub board_side: String, pub outline_height: f32, pub outline: Vec<Point>,
}
pub fn parse_placement_outline(input: &str) -> IResult<&str, PlacementOutline> {
let (remaining, (owner, board_side, outline_height, outline)) = parse_section!(
"PLACE_OUTLINE",
ws_separated!((
owner, is_not(" "), float, many1(ws(point)) ))
)
.parse(input)?;
Ok((
remaining,
PlacementOutline {
owner: owner.to_string(),
board_side: board_side.to_string(),
outline_height,
outline,
},
))
}
#[derive(Debug, PartialEq, Clone, Default, PartialOrd)]
pub struct RoutingKeepout {
pub owner: String, pub routing_layers: String, pub outline: Vec<Point>,
}
pub fn parse_routing_keepout(input: &str) -> IResult<&str, RoutingKeepout> {
let (remaining, (owner, routing_layers, outline)) = parse_section!(
"ROUTE_KEEPOUT",
ws_separated!((owner, not_line_ending, many1(ws(point))))
)
.parse(input)?;
Ok((
remaining,
RoutingKeepout {
owner: owner.to_string(),
routing_layers: routing_layers.to_string(),
outline,
},
))
}
#[derive(Debug, PartialEq, Clone, Default, PartialOrd)]
pub struct ViaKeepout {
pub owner: String, pub outline: Vec<Point>,
}
pub fn parse_via_keepout(input: &str) -> IResult<&str, ViaKeepout> {
let (remaining, (owner, outline)) =
parse_section!("VIA_KEEPOUT", ws_separated!((owner, many1(ws(point))))).parse(input)?;
Ok((
remaining,
ViaKeepout {
owner: owner.to_string(),
outline,
},
))
}
#[derive(Debug, PartialEq, Clone, Default, PartialOrd)]
pub struct PlacementKeepout {
pub owner: String, pub board_side: String, pub keepout_height: f32, pub outline: Vec<Point>,
}
pub fn parse_placement_keepout(input: &str) -> IResult<&str, PlacementKeepout> {
let (remaining, (owner, board_side, keepout_height, outline)) = parse_section!(
"PLACE_KEEPOUT",
ws_separated!((
owner,
is_not(" "), float, many1(ws(point)) ))
)
.parse(input)?;
Ok((
remaining,
PlacementKeepout {
owner: owner.to_string(),
board_side: board_side.to_string(),
keepout_height,
outline,
},
))
}
#[derive(Debug, PartialEq, Clone, Default, PartialOrd)]
pub struct PlacementGroupArea {
pub owner: String, pub board_side: String, pub group_name: String,
pub outline: Vec<Point>,
}
pub fn parse_placement_group_area(input: &str) -> IResult<&str, PlacementGroupArea> {
let (remaining, (owner, board_side, group_name, outline)) = parse_section!(
"PLACE_REGION",
ws_separated!((
owner,
is_not(" "), not_line_ending, many1(ws(point)) ))
)
.parse(input)?;
Ok((
remaining,
PlacementGroupArea {
owner: owner.to_string(),
board_side: board_side.to_string(),
group_name: group_name.to_string(),
outline,
},
))
}
pub fn owner(input: &str) -> IResult<&str, &str> {
alt((tag("ECAD"), tag("MCAD"), tag("UNOWNED"))).parse(input)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_board_outline() {
let input = ".BOARD_OUTLINE MCAD
62.0
0 5.5 -120.0 0.0
0 36.1 -120.0 263.266
1 5127.5 56 360
.END_BOARD_OUTLINE";
let (remaining, board_outline) = parse_board_panel_outline(input).unwrap();
assert_eq!(remaining, "");
assert_eq!(board_outline.owner, "MCAD");
assert_eq!(board_outline.thickness, 62.0);
assert_eq!(board_outline.outline.len(), 3);
assert_eq!(board_outline.outline[0].loop_label, 0);
assert_eq!(board_outline.outline[0].x, 5.5);
assert_eq!(board_outline.outline[0].y, -120.0);
assert_eq!(board_outline.outline[0].angle, 0.0);
}
#[test]
fn test_parse_board_outline_2() {
let input = ".BOARD_OUTLINE ECAD
40.0
0 0.0 0.0 0.000
3 1574.0 235.7 90.087
.END_BOARD_OUTLINE";
let (remaining, board_outline) = parse_board_panel_outline(input).unwrap();
let expected_outline = BoardPanelOutline {
owner: "ECAD".to_string(),
thickness: 40.0,
outline: vec![
Point {
loop_label: 0,
x: 0.0,
y: 0.0,
angle: 0.0,
},
Point {
loop_label: 3,
x: 1574.0,
y: 235.7,
angle: 90.087,
},
],
};
assert_eq!(remaining, "");
assert_eq!(board_outline, expected_outline);
}
#[test]
fn test_parse_other_outline() {
let input = ".OTHER_OUTLINE MCAD
my_outline 62.0 TOP
0 5.5 -120.0 0.0
.END_OTHER_OUTLINE";
let (remaining, other_outline) = parse_other_outline(input).unwrap();
assert_eq!(remaining, "");
assert_eq!(other_outline.owner, "MCAD");
assert_eq!(other_outline.id, "my_outline");
assert_eq!(other_outline.extrude_thickness, 62.0);
assert_eq!(other_outline.board_side, "TOP");
assert_eq!(other_outline.outline.len(), 1);
assert_eq!(other_outline.outline[0].loop_label, 0);
assert_eq!(other_outline.outline[0].x, 5.5);
assert_eq!(other_outline.outline[0].y, -120.0);
assert_eq!(other_outline.outline[0].angle, 0.0);
}
#[test]
fn test_parse_routing_outline() {
let input = ".ROUTE_OUTLINE ECAD
ALL
0 5112.5 150.0 0.0
0 5112.5 2058.2 0.0
.END_ROUTE_OUTLINE";
let (remaining, routing_outline) = parse_routing_outline(input).unwrap();
assert_eq!(remaining, "");
assert_eq!(routing_outline.owner, "ECAD");
assert_eq!(routing_outline.routing_layers, "ALL");
assert_eq!(routing_outline.outline.len(), 2);
assert_eq!(routing_outline.outline[0].loop_label, 0);
assert_eq!(routing_outline.outline[0].x, 5112.5);
assert_eq!(routing_outline.outline[0].y, 150.0);
assert_eq!(routing_outline.outline[0].angle, 0.0);
}
#[test]
fn test_parse_placement_outline() {
let input = ".PLACE_OUTLINE MCAD
TOP 1000.0
0 -5.0 2034.9 -152.9
.END_PLACE_OUTLINE";
let (remaining, placement_outline) = parse_placement_outline(input).unwrap();
assert_eq!(remaining, "");
assert_eq!(placement_outline.owner, "MCAD");
assert_eq!(placement_outline.board_side, "TOP");
assert_eq!(placement_outline.outline_height, 1000.0);
assert_eq!(placement_outline.outline.len(), 1);
assert_eq!(placement_outline.outline[0].loop_label, 0);
assert_eq!(placement_outline.outline[0].x, -5.0);
assert_eq!(placement_outline.outline[0].y, 2034.9);
assert_eq!(placement_outline.outline[0].angle, -152.9);
}
#[test]
fn test_parse_routing_keepout() {
let input = ".ROUTE_KEEPOUT ECAD
ALL
0 2650.0 2350.0 0.0
0 3100.0 2350.0 360.0
.END_ROUTE_KEEPOUT";
let (remaining, routing_keepout) = parse_routing_keepout(input).unwrap();
assert_eq!(remaining, "");
assert_eq!(routing_keepout.owner, "ECAD");
assert_eq!(routing_keepout.routing_layers, "ALL");
assert_eq!(routing_keepout.outline.len(), 2);
assert_eq!(routing_keepout.outline[0].loop_label, 0);
assert_eq!(routing_keepout.outline[0].x, 2650.0);
assert_eq!(routing_keepout.outline[0].y, 2350.0);
assert_eq!(routing_keepout.outline[0].angle, 0.0);
}
#[test]
fn test_parse_via_keepout() {
let input = ".VIA_KEEPOUT ECAD
0 2650.0 2350.0 0.0
0 3100.0 2350.0 360.0
.END_VIA_KEEPOUT";
let (remaining, via_keepout) = parse_via_keepout(input).unwrap();
assert_eq!(remaining, "");
assert_eq!(via_keepout.owner, "ECAD");
assert_eq!(via_keepout.outline.len(), 2);
assert_eq!(via_keepout.outline[0].loop_label, 0);
assert_eq!(via_keepout.outline[0].x, 2650.0);
assert_eq!(via_keepout.outline[0].y, 2350.0);
assert_eq!(via_keepout.outline[0].angle, 0.0);
}
#[test]
fn test_parse_placement_keepout() {
let input = ".PLACE_KEEPOUT MCAD
TOP 300.0
0 3700.0 5000.0 0.0
0 3700.0 5000.0 0.0
.END_PLACE_KEEPOUT";
let (remaining, placement_keepout) = parse_placement_keepout(input).unwrap();
assert_eq!(remaining, "");
assert_eq!(placement_keepout.owner, "MCAD");
assert_eq!(placement_keepout.board_side, "TOP");
assert_eq!(placement_keepout.keepout_height, 300.0);
assert_eq!(placement_keepout.outline.len(), 2);
assert_eq!(placement_keepout.outline[0].loop_label, 0);
assert_eq!(placement_keepout.outline[0].x, 3700.0);
assert_eq!(placement_keepout.outline[0].y, 5000.0);
assert_eq!(placement_keepout.outline[0].angle, 0.0);
}
#[test]
fn test_parse_placement_group_area() {
let input = ".PLACE_REGION UNOWNED
TOP the_best_group
0 5.5 -120.0 0.0
.END_PLACE_REGION";
let (remaining, placement_group_area) = parse_placement_group_area(input).unwrap();
assert_eq!(remaining, "");
assert_eq!(placement_group_area.owner, "UNOWNED");
assert_eq!(placement_group_area.board_side, "TOP");
assert_eq!(placement_group_area.group_name, "the_best_group");
assert_eq!(placement_group_area.outline.len(), 1);
assert_eq!(placement_group_area.outline[0].loop_label, 0);
assert_eq!(placement_group_area.outline[0].x, 5.5);
assert_eq!(placement_group_area.outline[0].y, -120.0);
assert_eq!(placement_group_area.outline[0].angle, 0.0);
}
#[test]
fn test_point() {
let input = "0 5.5 -120.0 0.0";
let (remaining, point) = point(input).unwrap();
assert_eq!(remaining, "");
assert_eq!(point.loop_label, 0);
assert_eq!(point.x, 5.5);
assert_eq!(point.y, -120.0);
assert_eq!(point.angle, 0.0);
}
#[test]
fn test_owner() {
let input = "ECADMCADUNOWNED";
let (remaining, owner_str) = owner(input).unwrap();
assert_eq!(remaining, "MCADUNOWNED");
assert_eq!(owner_str, "ECAD");
let (remaining, owner_str) = owner(remaining).unwrap();
assert_eq!(remaining, "UNOWNED");
assert_eq!(owner_str, "MCAD");
let (remaining, owner_str) = owner(remaining).unwrap();
assert_eq!(remaining, "");
assert_eq!(owner_str, "UNOWNED");
}
}