mirror of https://codeberg.org/topola/topola.git
dsn, sdl2-demo: replace hardcoded layout with basic .dsn file
This commit is contained in:
parent
08c03a0e77
commit
3f6bad2ed6
|
|
@ -48,6 +48,9 @@ use topola::tracer::{Trace, Tracer};
|
||||||
use topola::math::Circle;
|
use topola::math::Circle;
|
||||||
use topola::router::Router;
|
use topola::router::Router;
|
||||||
|
|
||||||
|
use topola::dsn::de::from_str;
|
||||||
|
use topola::dsn::structure::Pcb;
|
||||||
|
|
||||||
struct SimpleRules {
|
struct SimpleRules {
|
||||||
net_clearances: HashMap<(i64, i64), f64>,
|
net_clearances: HashMap<(i64, i64), f64>,
|
||||||
}
|
}
|
||||||
|
|
@ -245,331 +248,104 @@ fn main() -> Result<(), anyhow::Error> {
|
||||||
]),
|
]),
|
||||||
});
|
});
|
||||||
|
|
||||||
let component1_1 = router.layout.add_component(1);
|
let contents = std::fs::read_to_string("tests/data/test.dsn")?;
|
||||||
let component1_2 = router.layout.add_component(1);
|
let pcb = from_str::<Pcb>(&contents)?;
|
||||||
let component2 = router.layout.add_component(2);
|
//dbg!(&pcb);
|
||||||
let component3_1 = router.layout.add_component(3);
|
|
||||||
let component3_2 = router.layout.add_component(3);
|
|
||||||
let component4_1 = router.layout.add_component(4);
|
|
||||||
let component4_2 = router.layout.add_component(4);
|
|
||||||
|
|
||||||
let dot_start = router
|
// this holds the mapping of net names to numerical IDs (here for now)
|
||||||
.layout
|
let net_ids: HashMap<String, usize> = HashMap::from_iter(
|
||||||
.add_fixed_dot(FixedDotWeight {
|
pcb.network.classes[0].nets
|
||||||
component: component1_1,
|
.iter()
|
||||||
circle: Circle {
|
.enumerate()
|
||||||
pos: (100.5, 400.5).into(),
|
.map(|(id, net)| (net.clone(), id))
|
||||||
r: 8.0,
|
);
|
||||||
},
|
|
||||||
|
// add vias to layout and save indices of dots in the order they appear in the file
|
||||||
|
let dot_indices: Vec<_> = pcb.wiring.vias
|
||||||
|
.iter()
|
||||||
|
.map(|via| {
|
||||||
|
let net_id = net_ids.get(&via.net.0).unwrap();
|
||||||
|
let component = router.layout.add_component(*net_id as i64);
|
||||||
|
|
||||||
|
// no way to resolve the name or layer support yet
|
||||||
|
// pick the first layer of the first object found
|
||||||
|
let circle = &pcb.library.padstacks[0].shapes[0].0;
|
||||||
|
let circle = Circle {
|
||||||
|
pos: (
|
||||||
|
via.x as f64 / 100.0,
|
||||||
|
-via.y as f64 / 100.0,
|
||||||
|
).into(),
|
||||||
|
r: circle.radius as f64 / 100.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
router.layout.add_fixed_dot(FixedDotWeight {
|
||||||
|
component,
|
||||||
|
circle,
|
||||||
|
}).unwrap()
|
||||||
})
|
})
|
||||||
.unwrap();
|
.collect();
|
||||||
|
|
||||||
let dot_start2 = router
|
for wire in pcb.wiring.wires.iter() {
|
||||||
.layout
|
let net_id = net_ids.get(&wire.net.0).unwrap();
|
||||||
.add_fixed_dot(FixedDotWeight {
|
let component = router.layout.add_component(*net_id as i64);
|
||||||
component: component3_1,
|
|
||||||
|
// add the first coordinate in the wire path as a dot and save its index
|
||||||
|
let mut prev_index = router.layout.add_fixed_dot(FixedDotWeight {
|
||||||
|
component,
|
||||||
circle: Circle {
|
circle: Circle {
|
||||||
pos: (100.5, 500.5).into(),
|
pos: (
|
||||||
r: 8.0,
|
wire.path.coords[0].x as f64 / 100.0,
|
||||||
},
|
-wire.path.coords[0].y as f64 / 100.0,
|
||||||
})
|
).into(),
|
||||||
.unwrap();
|
r: wire.path.width as f64 / 100.0,
|
||||||
|
}
|
||||||
|
}).unwrap();
|
||||||
|
|
||||||
let dot_start3 = router
|
// iterate through path coords starting from the second
|
||||||
.layout
|
for coord in wire.path.coords.iter().skip(1) {
|
||||||
.add_fixed_dot(FixedDotWeight {
|
let index = router.layout.add_fixed_dot(FixedDotWeight {
|
||||||
component: component4_1,
|
component,
|
||||||
circle: Circle {
|
circle: Circle {
|
||||||
pos: (160.5, 430.5).into(),
|
pos: (
|
||||||
r: 8.0,
|
coord.x as f64 / 100.0,
|
||||||
},
|
-coord.y as f64 / 100.0,
|
||||||
})
|
).into(),
|
||||||
.unwrap();
|
r: wire.path.width as f64 / 100.0,
|
||||||
|
}
|
||||||
let dot_end = router
|
}).unwrap();
|
||||||
.layout
|
|
||||||
.add_fixed_dot(FixedDotWeight {
|
|
||||||
component: component1_2,
|
|
||||||
circle: Circle {
|
|
||||||
pos: (470.5, 350.5).into(),
|
|
||||||
r: 8.0,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let dot_end2 = router
|
|
||||||
.layout
|
|
||||||
.add_fixed_dot(FixedDotWeight {
|
|
||||||
component: component3_2,
|
|
||||||
circle: Circle {
|
|
||||||
pos: (500.5, 150.5).into(),
|
|
||||||
r: 8.0,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let dot_end3 = router
|
|
||||||
.layout
|
|
||||||
.add_fixed_dot(FixedDotWeight {
|
|
||||||
component: component4_2,
|
|
||||||
circle: Circle {
|
|
||||||
pos: (350.5, 200.5).into(),
|
|
||||||
r: 8.0,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let dot1_1 = router
|
|
||||||
.layout
|
|
||||||
.add_fixed_dot(FixedDotWeight {
|
|
||||||
component: component2,
|
|
||||||
circle: Circle {
|
|
||||||
pos: (200.5, 200.5).into(),
|
|
||||||
r: 8.0,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let dot2_1 = router
|
|
||||||
.layout
|
|
||||||
.add_fixed_dot(FixedDotWeight {
|
|
||||||
component: component2,
|
|
||||||
circle: Circle {
|
|
||||||
pos: (200.5, 500.5).into(),
|
|
||||||
r: 8.0,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
|
// add a seg between the current and previous coords
|
||||||
let _ = router.layout.add_fixed_seg(
|
let _ = router.layout.add_fixed_seg(
|
||||||
dot1_1,
|
prev_index,
|
||||||
dot2_1,
|
index,
|
||||||
FixedSegWeight {
|
FixedSegWeight {
|
||||||
component: component2,
|
component,
|
||||||
width: 3.0,
|
width: wire.path.width as f64 / 100.0,
|
||||||
},
|
},
|
||||||
);
|
).unwrap();
|
||||||
|
|
||||||
let dot2_2 = router
|
prev_index = index;
|
||||||
.layout
|
}
|
||||||
.add_fixed_dot(FixedDotWeight {
|
}
|
||||||
component: component2,
|
|
||||||
circle: Circle {
|
|
||||||
pos: (600.5, 500.5).into(),
|
|
||||||
r: 8.0,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let _ = router.layout.add_fixed_seg(
|
|
||||||
dot2_1,
|
|
||||||
dot2_2,
|
|
||||||
FixedSegWeight {
|
|
||||||
component: component2,
|
|
||||||
width: 3.0,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
let dot3 = router
|
|
||||||
.layout
|
|
||||||
.add_fixed_dot(FixedDotWeight {
|
|
||||||
component: component2,
|
|
||||||
circle: Circle {
|
|
||||||
pos: (400.5, 200.5).into(),
|
|
||||||
r: 8.0,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let dot4 = router
|
|
||||||
.layout
|
|
||||||
.add_fixed_dot(FixedDotWeight {
|
|
||||||
component: component2,
|
|
||||||
circle: Circle {
|
|
||||||
pos: (400.5, 400.5).into(),
|
|
||||||
r: 8.0,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let _ = router.layout.add_fixed_seg(
|
|
||||||
dot3,
|
|
||||||
dot4,
|
|
||||||
FixedSegWeight {
|
|
||||||
component: component2,
|
|
||||||
width: 3.0,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
let dot5 = router
|
|
||||||
.layout
|
|
||||||
.add_fixed_dot(FixedDotWeight {
|
|
||||||
component: component2,
|
|
||||||
circle: Circle {
|
|
||||||
pos: (530.5, 400.5).into(),
|
|
||||||
r: 8.0,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let _ = router.layout.add_fixed_seg(
|
|
||||||
dot4,
|
|
||||||
dot5,
|
|
||||||
FixedSegWeight {
|
|
||||||
component: component2,
|
|
||||||
width: 3.0,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
let dot1_2 = router
|
|
||||||
.layout
|
|
||||||
.add_fixed_dot(FixedDotWeight {
|
|
||||||
component: component2,
|
|
||||||
circle: Circle {
|
|
||||||
pos: (600.5, 200.5).into(),
|
|
||||||
r: 8.0,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let _ = router.layout.add_fixed_seg(
|
|
||||||
dot3,
|
|
||||||
dot1_2,
|
|
||||||
FixedSegWeight {
|
|
||||||
component: component2,
|
|
||||||
width: 3.0,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
let _ = router.layout.add_fixed_seg(
|
|
||||||
dot1_2,
|
|
||||||
dot2_2,
|
|
||||||
FixedSegWeight {
|
|
||||||
component: component2,
|
|
||||||
width: 3.0,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
let dot6 = router
|
|
||||||
.layout
|
|
||||||
.add_fixed_dot(FixedDotWeight {
|
|
||||||
component: component2,
|
|
||||||
circle: Circle {
|
|
||||||
pos: (530.5, 300.5).into(),
|
|
||||||
r: 8.0,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let _ = router.layout.add_fixed_seg(
|
|
||||||
dot5,
|
|
||||||
dot6,
|
|
||||||
FixedSegWeight {
|
|
||||||
component: component2,
|
|
||||||
width: 3.0,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
/*let dot7 = router
|
|
||||||
.layout
|
|
||||||
.add_dot(FixedDotWeight {
|
|
||||||
net: 2,
|
|
||||||
circle: Circle {
|
|
||||||
pos: (400.5, 440.5).into(),
|
|
||||||
r: 8.0,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let _ = router.layout.add_seg(
|
|
||||||
dot4,
|
|
||||||
dot7,
|
|
||||||
FixedSegWeight {
|
|
||||||
net: 20,
|
|
||||||
width: 3.0,
|
|
||||||
},
|
|
||||||
);*/
|
|
||||||
|
|
||||||
/*render_times(
|
|
||||||
&mut event_pump,
|
|
||||||
&mut canvas,
|
|
||||||
RouterOrLayout::Layout(&router.layout),
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
&[],
|
|
||||||
-1,
|
|
||||||
);
|
|
||||||
|
|
||||||
render_times(
|
render_times(
|
||||||
&mut event_pump,
|
&mut event_pump,
|
||||||
&mut canvas,
|
&window,
|
||||||
RouterOrLayout::Router(&mut router),
|
&mut renderer,
|
||||||
Some(dot_start),
|
&font_context,
|
||||||
Some(dot_end),
|
RouterOrLayout::Layout(&router.layout),
|
||||||
|
None,
|
||||||
None,
|
None,
|
||||||
&[],
|
&[],
|
||||||
|
&[],
|
||||||
|
&[],
|
||||||
-1,
|
-1,
|
||||||
);*/
|
);
|
||||||
|
|
||||||
|
// these are both on net 1 in the test file
|
||||||
let _ = router.route_band(
|
let _ = router.route_band(
|
||||||
dot_start,
|
dot_indices[1],
|
||||||
dot_end,
|
dot_indices[2],
|
||||||
3.0,
|
|
||||||
&mut EmptyRouterObserver,
|
|
||||||
//&mut DebugRouterObserver::new(&mut event_pump, &window, &mut renderer, &font_context),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
render_times(
|
|
||||||
&mut event_pump,
|
|
||||||
&window,
|
|
||||||
&mut renderer,
|
|
||||||
&font_context,
|
|
||||||
RouterOrLayout::Layout(&router.layout),
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
&[],
|
|
||||||
&[],
|
|
||||||
&[],
|
|
||||||
-1,
|
|
||||||
);
|
|
||||||
|
|
||||||
/*render_times(
|
|
||||||
&mut event_pump,
|
|
||||||
&mut canvas,
|
|
||||||
RouterOrLayout::Router(&mut router),
|
|
||||||
Some(dot_start2),
|
|
||||||
Some(dot_end),
|
|
||||||
None,
|
|
||||||
&[],
|
|
||||||
-1,
|
|
||||||
);*/
|
|
||||||
|
|
||||||
let band2 = router.route_band(
|
|
||||||
dot_start2,
|
|
||||||
dot_end2,
|
|
||||||
3.0,
|
|
||||||
&mut EmptyRouterObserver,
|
|
||||||
//&mut DebugRouterObserver::new(&mut event_pump, &window, &mut renderer, &font_context),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
render_times(
|
|
||||||
&mut event_pump,
|
|
||||||
&window,
|
|
||||||
&mut renderer,
|
|
||||||
&font_context,
|
|
||||||
RouterOrLayout::Layout(&router.layout),
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
&[],
|
|
||||||
&[],
|
|
||||||
&[],
|
|
||||||
-1,
|
|
||||||
);
|
|
||||||
|
|
||||||
let band3 = router.route_band(
|
|
||||||
dot_start3,
|
|
||||||
dot_end3,
|
|
||||||
3.0,
|
3.0,
|
||||||
//&mut EmptyRouterObserver,
|
//&mut EmptyRouterObserver,
|
||||||
&mut DebugRouterObserver::new(&mut event_pump, &window, &mut renderer, &font_context),
|
&mut DebugRouterObserver::new(&mut event_pump, &window, &mut renderer, &font_context),
|
||||||
|
|
@ -589,50 +365,6 @@ fn main() -> Result<(), anyhow::Error> {
|
||||||
-1,
|
-1,
|
||||||
);
|
);
|
||||||
|
|
||||||
router.layout.remove_band(band3);
|
|
||||||
|
|
||||||
render_times(
|
|
||||||
&mut event_pump,
|
|
||||||
&window,
|
|
||||||
&mut renderer,
|
|
||||||
&font_context,
|
|
||||||
RouterOrLayout::Layout(&router.layout),
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
&[],
|
|
||||||
&[],
|
|
||||||
&[],
|
|
||||||
-1,
|
|
||||||
);
|
|
||||||
|
|
||||||
render_times(
|
|
||||||
&mut event_pump,
|
|
||||||
&window,
|
|
||||||
&mut renderer,
|
|
||||||
&font_context,
|
|
||||||
RouterOrLayout::Router(&mut router),
|
|
||||||
Some(band2),
|
|
||||||
None,
|
|
||||||
&[],
|
|
||||||
&[],
|
|
||||||
&[],
|
|
||||||
-1,
|
|
||||||
);
|
|
||||||
|
|
||||||
render_times(
|
|
||||||
&mut event_pump,
|
|
||||||
&window,
|
|
||||||
&mut renderer,
|
|
||||||
&font_context,
|
|
||||||
RouterOrLayout::Layout(&router.layout),
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
&[],
|
|
||||||
&[],
|
|
||||||
&[],
|
|
||||||
-1,
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,632 @@
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde::de::{self, Visitor, SeqAccess, DeserializeSeed};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
#[error("{0}")]
|
||||||
|
Message(String),
|
||||||
|
#[error("unexpected EOF")]
|
||||||
|
Eof,
|
||||||
|
#[error("expected boolean value")]
|
||||||
|
ExpectedBool,
|
||||||
|
#[error("expected quoted string")]
|
||||||
|
ExpectedQuoted,
|
||||||
|
#[error("spaces in quoted strings weren't declared")]
|
||||||
|
SpaceInQuoted,
|
||||||
|
#[error("expected unquoted string")]
|
||||||
|
ExpectedUnquoted,
|
||||||
|
#[error("expected opening parenthesis")]
|
||||||
|
ExpectedOpeningParen,
|
||||||
|
#[error("expected closing parenthesis")]
|
||||||
|
ExpectedClosingParen,
|
||||||
|
#[error("wrong struct, expected {0}")]
|
||||||
|
ExpectedStruct(&'static str),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl de::Error for Error {
|
||||||
|
fn custom<T: std::fmt::Display>(msg: T) -> Self {
|
||||||
|
Error::Message(msg.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Deserializer<'de>
|
||||||
|
{
|
||||||
|
input: &'de str,
|
||||||
|
line: usize,
|
||||||
|
column: usize,
|
||||||
|
|
||||||
|
string_quote: Option<char>,
|
||||||
|
space_in_quoted_tokens: bool,
|
||||||
|
reconfig_incoming: Option<ReconfigIncoming>,
|
||||||
|
|
||||||
|
next_option_empty_hint: bool,
|
||||||
|
last_deserialized_type: Option<&'static str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug, Copy, Clone)]
|
||||||
|
enum ReconfigIncoming {
|
||||||
|
StringQuote,
|
||||||
|
SpaceAllowed,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserializer<'de> {
|
||||||
|
fn from_str(input: &'de str) -> Self {
|
||||||
|
Self {
|
||||||
|
input,
|
||||||
|
line: 0,
|
||||||
|
column: 0,
|
||||||
|
|
||||||
|
string_quote: None,
|
||||||
|
space_in_quoted_tokens: false,
|
||||||
|
reconfig_incoming: None,
|
||||||
|
|
||||||
|
next_option_empty_hint: false,
|
||||||
|
last_deserialized_type: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_name_lookahead(&self) -> Option<String> {
|
||||||
|
let mut iter = self.input.chars();
|
||||||
|
if iter.next() != Some('(') {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Some(iter.take_while(|c| c != &' ' && c != &'\r' && c != &'\n').collect::<String>())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn peek(&mut self) -> Result<char> {
|
||||||
|
self.input.chars().next().ok_or(Error::Eof)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next(&mut self) -> Result<char> {
|
||||||
|
let chr = self.peek()?;
|
||||||
|
self.input = &self.input[1..];
|
||||||
|
if chr == '\n' {
|
||||||
|
self.line += 1;
|
||||||
|
self.column = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self.column += 1;
|
||||||
|
}
|
||||||
|
Ok(chr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn skip_ws(&mut self) {
|
||||||
|
while let Ok(chr) = self.peek() {
|
||||||
|
if chr != ' ' && chr != '\r' && chr != '\n' {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self.next().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_bool(&mut self) -> Result<bool> {
|
||||||
|
match &self.parse_identifier() {
|
||||||
|
Ok(string) => match string.as_str() {
|
||||||
|
"on" => Ok(true),
|
||||||
|
"off" => Ok(false),
|
||||||
|
_ => Err(Error::ExpectedBool),
|
||||||
|
}
|
||||||
|
Err(_) => Err(Error::ExpectedBool),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_identifier(&mut self) -> Result<String> {
|
||||||
|
self.parse_unquoted()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_string(&mut self) -> Result<String> {
|
||||||
|
let chr = self.peek()?;
|
||||||
|
if self.string_quote == Some(chr) {
|
||||||
|
self.parse_quoted()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self.parse_unquoted()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_unquoted(&mut self) -> Result<String> {
|
||||||
|
let mut string = String::new();
|
||||||
|
loop {
|
||||||
|
let chr = self.peek()?;
|
||||||
|
if chr != ' ' && chr != '\r' && chr != '\n' && chr != '(' && chr != ')' {
|
||||||
|
string.push(self.next()?); // can't fail because of earlier peek
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if string.len() > 0 {
|
||||||
|
return Ok(string);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
dbg!(self.line, self.column);
|
||||||
|
return Err(Error::ExpectedUnquoted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// method only called if parse_string sees a valid string quote
|
||||||
|
fn parse_quoted(&mut self) -> Result<String> {
|
||||||
|
assert!(self.next().unwrap() == self.string_quote.unwrap());
|
||||||
|
|
||||||
|
let mut string = String::new();
|
||||||
|
loop {
|
||||||
|
let chr = self.peek()?;
|
||||||
|
|
||||||
|
// XXX this is silly
|
||||||
|
// not declaring that spaces are allowed in qyoted strings downgrades the format
|
||||||
|
// but there's no reason we shouldn't try to parse the file anyway, no ambiguity arises
|
||||||
|
// maybe this should log a warning and proceed?
|
||||||
|
if self.space_in_quoted_tokens != true && chr == ' ' {
|
||||||
|
return Err(Error::SpaceInQuoted);
|
||||||
|
}
|
||||||
|
|
||||||
|
if Some(chr) == self.string_quote {
|
||||||
|
self.next().unwrap();
|
||||||
|
return Ok(string);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
string.push(self.next()?); // can't fail because of earlier peek
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_str<'a, T>(input: &'a str) -> Result<T>
|
||||||
|
where
|
||||||
|
T: Deserialize<'a>,
|
||||||
|
{
|
||||||
|
let mut deserializer = Deserializer::from_str(input);
|
||||||
|
let t = T::deserialize(&mut deserializer)?;
|
||||||
|
if !deserializer.input.is_empty() {
|
||||||
|
println!("remaining input");
|
||||||
|
dbg!(deserializer.input);
|
||||||
|
}
|
||||||
|
Ok(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn deserialize_any<V>(self, _visitor: V) -> Result<V::Value>
|
||||||
|
where V:
|
||||||
|
Visitor<'de>,
|
||||||
|
{
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value>
|
||||||
|
where V:
|
||||||
|
Visitor<'de>,
|
||||||
|
{
|
||||||
|
let value = self.parse_bool()?;
|
||||||
|
self.skip_ws();
|
||||||
|
|
||||||
|
// if the struct deserializer set a variable saying the incoming value should reconfigure a specific variable in the parser
|
||||||
|
// we do so and clear the flag
|
||||||
|
if self.reconfig_incoming == Some(ReconfigIncoming::SpaceAllowed) {
|
||||||
|
self.space_in_quoted_tokens = value;
|
||||||
|
self.reconfig_incoming = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.last_deserialized_type = Some("");
|
||||||
|
visitor.visit_bool(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_i8<V>(self, _visitor: V) -> Result<V::Value>
|
||||||
|
where V: Visitor<'de>,
|
||||||
|
{
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
fn deserialize_i16<V>(self, _visitor: V) -> Result<V::Value>
|
||||||
|
where V: Visitor<'de>,
|
||||||
|
{
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value>
|
||||||
|
where V: Visitor<'de>,
|
||||||
|
{
|
||||||
|
let value = self.parse_unquoted()?;
|
||||||
|
self.skip_ws();
|
||||||
|
|
||||||
|
self.last_deserialized_type = Some("");
|
||||||
|
visitor.visit_i32(value.parse().unwrap())
|
||||||
|
}
|
||||||
|
fn deserialize_i64<V>(self, _visitor: V) -> Result<V::Value>
|
||||||
|
where V: Visitor<'de>,
|
||||||
|
{
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
fn deserialize_u8<V>(self, _visitor: V) -> Result<V::Value>
|
||||||
|
where V: Visitor<'de>,
|
||||||
|
{
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
fn deserialize_u16<V>(self, _visitor: V) -> Result<V::Value>
|
||||||
|
where V: Visitor<'de>,
|
||||||
|
{
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value>
|
||||||
|
where V: Visitor<'de>,
|
||||||
|
{
|
||||||
|
let value = self.parse_unquoted()?;
|
||||||
|
self.skip_ws();
|
||||||
|
|
||||||
|
self.last_deserialized_type = Some("");
|
||||||
|
visitor.visit_u32(value.parse().unwrap())
|
||||||
|
}
|
||||||
|
fn deserialize_u64<V>(self, _visitor: V) -> Result<V::Value>
|
||||||
|
where V: Visitor<'de>,
|
||||||
|
{
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value>
|
||||||
|
where V: Visitor<'de>,
|
||||||
|
{
|
||||||
|
let value = self.parse_unquoted()?;
|
||||||
|
self.skip_ws();
|
||||||
|
|
||||||
|
self.last_deserialized_type = Some("");
|
||||||
|
visitor.visit_f32(value.parse().unwrap())
|
||||||
|
}
|
||||||
|
fn deserialize_f64<V>(self, _visitor: V) -> Result<V::Value>
|
||||||
|
where V: Visitor<'de>,
|
||||||
|
{
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
fn deserialize_char<V>(self, visitor: V) -> Result<V::Value>
|
||||||
|
where V: Visitor<'de>,
|
||||||
|
{
|
||||||
|
let chr = self.next()?;
|
||||||
|
self.skip_ws();
|
||||||
|
|
||||||
|
// if the struct deserializer set a variable saying the incoming value should reconfigure a specific variable in the parser
|
||||||
|
// we do so and clear the flag
|
||||||
|
if self.reconfig_incoming == Some(ReconfigIncoming::StringQuote) {
|
||||||
|
self.string_quote = Some(chr);
|
||||||
|
self.reconfig_incoming = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.last_deserialized_type = Some("");
|
||||||
|
visitor.visit_char(chr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_str<V>(self, _visitor: V) -> Result<V::Value>
|
||||||
|
where V: Visitor<'de>,
|
||||||
|
{
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_string<V>(self, visitor: V) -> Result<V::Value>
|
||||||
|
where V:
|
||||||
|
Visitor<'de>,
|
||||||
|
{
|
||||||
|
let string = self.parse_string()?;
|
||||||
|
self.skip_ws();
|
||||||
|
|
||||||
|
self.last_deserialized_type = Some("");
|
||||||
|
visitor.visit_string(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_bytes<V>(self, _visitor: V) -> Result<V::Value>
|
||||||
|
where V: Visitor<'de>,
|
||||||
|
{
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_byte_buf<V>(
|
||||||
|
self,
|
||||||
|
_visitor: V
|
||||||
|
) -> Result<V::Value>
|
||||||
|
where V: Visitor<'de>,
|
||||||
|
{
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value>
|
||||||
|
where V: Visitor<'de>,
|
||||||
|
{
|
||||||
|
if self.next_option_empty_hint {
|
||||||
|
visitor.visit_none()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
visitor.visit_some(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_unit<V>(self, _visitor: V) -> Result<V::Value>
|
||||||
|
where V: Visitor<'de>,
|
||||||
|
{
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_unit_struct<V>(
|
||||||
|
self,
|
||||||
|
name: &'static str,
|
||||||
|
visitor: V
|
||||||
|
) -> Result<V::Value>
|
||||||
|
where V: Visitor<'de>,
|
||||||
|
{
|
||||||
|
if self.next()? != '(' {
|
||||||
|
return Err(Error::ExpectedOpeningParen);
|
||||||
|
}
|
||||||
|
self.skip_ws();
|
||||||
|
|
||||||
|
if &self.parse_identifier()? != name {
|
||||||
|
return Err(Error::ExpectedStruct(name));
|
||||||
|
}
|
||||||
|
self.skip_ws();
|
||||||
|
|
||||||
|
if self.next()? != ')' {
|
||||||
|
return Err(Error::ExpectedClosingParen);
|
||||||
|
}
|
||||||
|
self.skip_ws();
|
||||||
|
|
||||||
|
self.last_deserialized_type = Some(name);
|
||||||
|
|
||||||
|
visitor.visit_unit()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_newtype_struct<V>(
|
||||||
|
self,
|
||||||
|
name: &'static str,
|
||||||
|
visitor: V
|
||||||
|
) -> Result<V::Value>
|
||||||
|
where V: Visitor<'de>,
|
||||||
|
{
|
||||||
|
if self.next()? != '(' {
|
||||||
|
return Err(Error::ExpectedOpeningParen);
|
||||||
|
}
|
||||||
|
self.skip_ws();
|
||||||
|
|
||||||
|
if &self.parse_identifier()? != name {
|
||||||
|
return Err(Error::ExpectedStruct(name));
|
||||||
|
}
|
||||||
|
self.skip_ws();
|
||||||
|
|
||||||
|
// if what we're deserializing is a directive to update parser configuration
|
||||||
|
// set a variable so the deserializer for the following value can update the relevant config
|
||||||
|
// (the variable is reset to None by the bool/char deserializer when it updates the config)
|
||||||
|
self.reconfig_incoming = match name {
|
||||||
|
"string_quote" => Some(ReconfigIncoming::StringQuote),
|
||||||
|
"space_in_quoted_tokens" => Some(ReconfigIncoming::SpaceAllowed),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let value = visitor.visit_seq(NewtypeStructFields::new(self))?;
|
||||||
|
|
||||||
|
if self.next()? != ')' {
|
||||||
|
return Err(Error::ExpectedClosingParen);
|
||||||
|
}
|
||||||
|
self.skip_ws();
|
||||||
|
|
||||||
|
self.last_deserialized_type = Some(name);
|
||||||
|
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value>
|
||||||
|
where V: Visitor<'de>,
|
||||||
|
{
|
||||||
|
self.last_deserialized_type = None;
|
||||||
|
visitor.visit_seq(ArrayIndices::new(self))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_tuple<V>(
|
||||||
|
self,
|
||||||
|
_len: usize,
|
||||||
|
_visitor: V
|
||||||
|
) -> Result<V::Value>
|
||||||
|
where V: Visitor<'de>,
|
||||||
|
{
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_tuple_struct<V>(
|
||||||
|
self,
|
||||||
|
_name: &'static str,
|
||||||
|
_len: usize,
|
||||||
|
_visitor: V
|
||||||
|
) -> Result<V::Value>
|
||||||
|
where V: Visitor<'de>,
|
||||||
|
{
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_map<V>(self, _visitor: V) -> Result<V::Value>
|
||||||
|
where V: Visitor<'de>,
|
||||||
|
{
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_struct<V>(
|
||||||
|
self,
|
||||||
|
name: &'static str,
|
||||||
|
fields: &'static [&'static str],
|
||||||
|
visitor: V
|
||||||
|
) -> Result<V::Value>
|
||||||
|
where V: Visitor<'de>,
|
||||||
|
{
|
||||||
|
if self.next()? != '(' {
|
||||||
|
return Err(Error::ExpectedOpeningParen);
|
||||||
|
}
|
||||||
|
self.skip_ws();
|
||||||
|
|
||||||
|
if &self.parse_identifier()? != name {
|
||||||
|
return Err(Error::ExpectedStruct(name));
|
||||||
|
}
|
||||||
|
self.skip_ws();
|
||||||
|
|
||||||
|
let value = visitor.visit_seq(StructFields::new(self, fields))?;
|
||||||
|
|
||||||
|
if self.next()? != ')' {
|
||||||
|
return Err(Error::ExpectedClosingParen);
|
||||||
|
}
|
||||||
|
self.skip_ws();
|
||||||
|
|
||||||
|
// a hint for the array deserializer
|
||||||
|
self.last_deserialized_type = Some(name);
|
||||||
|
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_enum<V>(
|
||||||
|
self,
|
||||||
|
_name: &'static str,
|
||||||
|
_variants: &'static [&'static str],
|
||||||
|
_visitor: V
|
||||||
|
) -> Result<V::Value>
|
||||||
|
where V: Visitor<'de>,
|
||||||
|
{
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_identifier<V>(
|
||||||
|
self,
|
||||||
|
visitor: V
|
||||||
|
) -> Result<V::Value>
|
||||||
|
where V: Visitor<'de>
|
||||||
|
{
|
||||||
|
visitor.visit_string(self.parse_string()?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_ignored_any<V>(
|
||||||
|
self,
|
||||||
|
_visitor: V
|
||||||
|
) -> Result<V::Value>
|
||||||
|
where V: Visitor<'de>
|
||||||
|
{
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NewtypeStructFields<'a, 'de: 'a> {
|
||||||
|
de: &'a mut Deserializer<'de>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'de> NewtypeStructFields<'a, 'de> {
|
||||||
|
fn new(de: &'a mut Deserializer<'de>) -> Self {
|
||||||
|
Self {
|
||||||
|
de,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de, 'a> SeqAccess<'de> for NewtypeStructFields<'a, 'de> {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn next_element_seed<S>(&mut self, seed: S) -> Result<Option<S::Value>>
|
||||||
|
where S: DeserializeSeed<'de>,
|
||||||
|
{
|
||||||
|
if self.de.peek()? == ')' {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
seed.deserialize(&mut *self.de).map(Some)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ArrayIndices<'a, 'de: 'a> {
|
||||||
|
de: &'a mut Deserializer<'de>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'de> ArrayIndices<'a, 'de> {
|
||||||
|
fn new(de: &'a mut Deserializer<'de>) -> Self {
|
||||||
|
Self {
|
||||||
|
de,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de, 'a> SeqAccess<'de> for ArrayIndices<'a, 'de> {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn next_element_seed<S>(&mut self, seed: S) -> Result<Option<S::Value>>
|
||||||
|
where S: DeserializeSeed<'de>,
|
||||||
|
{
|
||||||
|
if self.de.peek()? == ')' {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(prev) = self.de.last_deserialized_type {
|
||||||
|
if let Some(lookahead) = self.de.next_name_lookahead() {
|
||||||
|
if prev != lookahead {
|
||||||
|
// the next struct is of different type from the array contents
|
||||||
|
// that means the array implicitly ended
|
||||||
|
// and we're looking at a field following the array instead
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
seed.deserialize(&mut *self.de).map(Some)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct StructFields<'a, 'de: 'a> {
|
||||||
|
de: &'a mut Deserializer<'de>,
|
||||||
|
current_field: usize,
|
||||||
|
fields: &'static [&'static str],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'de> StructFields<'a, 'de> {
|
||||||
|
fn new(de: &'a mut Deserializer<'de>, fields: &'static [&'static str]) -> Self {
|
||||||
|
Self {
|
||||||
|
de,
|
||||||
|
current_field: 0,
|
||||||
|
fields,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de, 'a> SeqAccess<'de> for StructFields<'a, 'de> {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn next_element_seed<S>(&mut self, seed: S) -> Result<Option<S::Value>>
|
||||||
|
where S: DeserializeSeed<'de>,
|
||||||
|
{
|
||||||
|
if self.de.peek()? == ')' {
|
||||||
|
if self.current_field < self.fields.len() {
|
||||||
|
// We're short a field (or multiple),
|
||||||
|
// but the trailing field(s) might be optional and implicitly absent.
|
||||||
|
// In that case we prepare a hint for deserialize_option to emit None:
|
||||||
|
self.de.next_option_empty_hint = true;
|
||||||
|
// and we tell serde to deserialize a field that may or may not be there:
|
||||||
|
self.current_field += 1;
|
||||||
|
return seed.deserialize(&mut *self.de).map(Some);
|
||||||
|
// If it was a non-optional that was missing for real,
|
||||||
|
// then even though our bet here was wrong (and we just lied to serde)
|
||||||
|
// the deserializer we handed off to will see the same closing paren
|
||||||
|
// (that we reacted to just now) and still return a sensible error.
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO explain this part of empty option detection
|
||||||
|
if let Some(lookahead) = self.de.next_name_lookahead() {
|
||||||
|
if lookahead != self.fields[self.current_field] {
|
||||||
|
self.de.next_option_empty_hint = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self.de.next_option_empty_hint = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self.de.next_option_empty_hint = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.current_field += 1;
|
||||||
|
seed.deserialize(&mut *self.de).map(Some)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod de;
|
||||||
|
pub mod structure;
|
||||||
|
|
@ -0,0 +1,239 @@
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[serde(rename = "pcb")]
|
||||||
|
pub struct Pcb {
|
||||||
|
pub name: String,
|
||||||
|
pub parser: Parser,
|
||||||
|
pub resolution: Resolution,
|
||||||
|
pub unit: Option<Unit>,
|
||||||
|
pub structure: Structure,
|
||||||
|
pub placement: Placement,
|
||||||
|
pub library: Library,
|
||||||
|
pub network: Network,
|
||||||
|
pub wiring: Wiring,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[serde(rename = "parser")]
|
||||||
|
pub struct Parser {
|
||||||
|
pub string_quote: Option<StringQuote>,
|
||||||
|
pub space_in_quoted_tokens: SpaceAllowed,
|
||||||
|
pub host_cad: Option<HostCad>,
|
||||||
|
pub host_version: Option<HostVersion>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
|
||||||
|
#[serde(rename = "string_quote")]
|
||||||
|
pub struct StringQuote(pub char);
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
|
||||||
|
#[serde(rename = "space_in_quoted_tokens")]
|
||||||
|
pub struct SpaceAllowed(pub bool);
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[serde(rename = "host_cad")]
|
||||||
|
pub struct HostCad(pub String);
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[serde(rename = "host_version")]
|
||||||
|
pub struct HostVersion(pub String);
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[serde(rename = "resolution")]
|
||||||
|
pub struct Resolution {
|
||||||
|
pub unit: String,
|
||||||
|
pub value: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[serde(rename = "unit")]
|
||||||
|
pub struct Unit(pub String);
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[serde(rename = "structure")]
|
||||||
|
pub struct Structure {
|
||||||
|
pub layers: Vec<Layer>,
|
||||||
|
pub boundary: Boundary,
|
||||||
|
pub vias: Vias,
|
||||||
|
pub rule: Rule,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[serde(rename = "layer")]
|
||||||
|
pub struct Layer {
|
||||||
|
pub name: String,
|
||||||
|
pub r#type: Type,
|
||||||
|
pub property: Property,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[serde(rename = "property")]
|
||||||
|
pub struct Property(Index);
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[serde(rename = "index")]
|
||||||
|
pub struct Index(pub u32);
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[serde(rename = "boundary")]
|
||||||
|
pub struct Boundary(pub Path);
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[serde(rename = "via")]
|
||||||
|
pub struct Vias {
|
||||||
|
vias: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[serde(rename = "placement")]
|
||||||
|
pub struct Placement;
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[serde(rename = "library")]
|
||||||
|
pub struct Library {
|
||||||
|
pub padstacks: Vec<Padstack>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[serde(rename = "padstack")]
|
||||||
|
pub struct Padstack {
|
||||||
|
pub name: String,
|
||||||
|
pub shapes: Vec<Shape>,
|
||||||
|
pub attach: Attach,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[serde(rename = "shape")]
|
||||||
|
pub struct Shape(pub Circle);
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[serde(rename = "circle")]
|
||||||
|
pub struct Circle {
|
||||||
|
pub layer: String,
|
||||||
|
pub radius: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[serde(rename = "attach")]
|
||||||
|
pub struct Attach(pub bool);
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[serde(rename = "network")]
|
||||||
|
pub struct Network {
|
||||||
|
pub classes: Vec<Class>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[serde(rename = "class")]
|
||||||
|
pub struct Class {
|
||||||
|
pub name: String,
|
||||||
|
pub nets: Vec<String>,
|
||||||
|
pub circuit: Circuit,
|
||||||
|
pub rule: Rule,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[serde(rename = "circuit")]
|
||||||
|
pub struct Circuit(pub UseVia);
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[serde(rename = "use_via")]
|
||||||
|
pub struct UseVia {
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[serde(rename = "wiring")]
|
||||||
|
pub struct Wiring {
|
||||||
|
pub wires: Vec<Wire>,
|
||||||
|
pub vias: Vec<Via>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[serde(rename = "wire")]
|
||||||
|
pub struct Wire {
|
||||||
|
pub path: Path,
|
||||||
|
pub net: Net,
|
||||||
|
pub r#type: Type,
|
||||||
|
}
|
||||||
|
|
||||||
|
// structs that appear in multiple places
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub struct Type(pub String);
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[serde(rename = "unit")]
|
||||||
|
pub struct Point {
|
||||||
|
pub x: i32,
|
||||||
|
pub y: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[serde(from = "FlatPath")]
|
||||||
|
pub struct Path {
|
||||||
|
pub layer: String,
|
||||||
|
pub width: u32,
|
||||||
|
pub coords: Vec<Point>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[serde(rename = "path")]
|
||||||
|
struct FlatPath {
|
||||||
|
pub layer: String,
|
||||||
|
pub width: u32,
|
||||||
|
pub coords: Vec<i32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<FlatPath> for Path {
|
||||||
|
fn from(flat: FlatPath) -> Path {
|
||||||
|
Path {
|
||||||
|
layer: flat.layer,
|
||||||
|
width: flat.width,
|
||||||
|
coords: flat.coords.chunks(2)
|
||||||
|
.map(|pair| Point {
|
||||||
|
x: pair[0],
|
||||||
|
// it's possible to return an error instead of panicking if this From were TryFrom,
|
||||||
|
// but I don't think serde will let us grab and inspect it elsewhere
|
||||||
|
// so annotating this with line/column information later might be difficult?
|
||||||
|
y: *pair.get(1).expect("unpaired coordinate in path"),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[serde(rename = "via")]
|
||||||
|
pub struct Via {
|
||||||
|
pub name: String,
|
||||||
|
pub x: i32,
|
||||||
|
pub y: i32,
|
||||||
|
pub net: Net,
|
||||||
|
pub r#type: Type,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[serde(rename = "rule")]
|
||||||
|
pub struct Rule {
|
||||||
|
pub width: Width,
|
||||||
|
pub clearances: Vec<Clearance>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[serde(rename = "net")]
|
||||||
|
pub struct Net(pub String);
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[serde(rename = "width")]
|
||||||
|
pub struct Width(pub f32);
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[serde(rename = "clearance")]
|
||||||
|
pub struct Clearance {
|
||||||
|
pub value: f32,
|
||||||
|
pub r#type: Option<Type>,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -11,3 +11,4 @@ pub mod router;
|
||||||
pub mod tracer;
|
pub mod tracer;
|
||||||
pub mod triangulation;
|
pub mod triangulation;
|
||||||
pub mod wraparoundable;
|
pub mod wraparoundable;
|
||||||
|
pub mod dsn;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
(pcb test.dsn
|
||||||
|
(parser
|
||||||
|
(string_quote ")
|
||||||
|
(space_in_quoted_tokens on)
|
||||||
|
(host_cad "KiCad's Pcbnew")
|
||||||
|
(host_version "7.0.9")
|
||||||
|
)
|
||||||
|
(resolution um 10)
|
||||||
|
(unit um)
|
||||||
|
(structure
|
||||||
|
(layer F.Cu
|
||||||
|
(type signal)
|
||||||
|
(property
|
||||||
|
(index 0)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(layer B.Cu
|
||||||
|
(type signal)
|
||||||
|
(property
|
||||||
|
(index 1)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(boundary
|
||||||
|
(path pcb 0 60400 -50400 9600 -50400 9600 -14600 60400 -14600
|
||||||
|
60400 -50400)
|
||||||
|
)
|
||||||
|
(via "Via[0-1]_800:400_um")
|
||||||
|
(rule
|
||||||
|
(width 300)
|
||||||
|
(clearance 200.1)
|
||||||
|
(clearance 200.1 (type default_smd))
|
||||||
|
(clearance 50 (type smd_smd))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(placement
|
||||||
|
)
|
||||||
|
(library
|
||||||
|
(padstack "Via[0-1]_800:400_um"
|
||||||
|
(shape (circle F.Cu 800))
|
||||||
|
(shape (circle B.Cu 800))
|
||||||
|
(attach off)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(network
|
||||||
|
(class kicad_default "" 1 2 3 5
|
||||||
|
(circuit
|
||||||
|
(use_via Via[0-1]_800:400_um)
|
||||||
|
)
|
||||||
|
(rule
|
||||||
|
(width 300)
|
||||||
|
(clearance 200.1)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(wiring
|
||||||
|
(wire (path F.Cu 300 20000 -50000 60000 -50000)(net 5)(type route))
|
||||||
|
(wire (path F.Cu 300 60000 -20000 40000 -20000 40000 -40000)(net 5)(type route))
|
||||||
|
(wire (path F.Cu 300 50000 -40000 50000 -30000)(net 5)(type route))
|
||||||
|
(wire (path F.Cu 300 60000 -50000 60000 -20000)(net 5)(type route))
|
||||||
|
(wire (path F.Cu 300 20000 -20000 20000 -50000)(net 5)(type route))
|
||||||
|
(wire (path F.Cu 300 40000 -40000 50000 -40000)(net 5)(type route))
|
||||||
|
(via "Via[0-1]_800:400_um" 47000 -35000 (net 2)(type route))
|
||||||
|
(via "Via[0-1]_800:400_um" 10000 -40000 (net 1)(type route))
|
||||||
|
(via "Via[0-1]_800:400_um" 50000 -15000 (net 1)(type route))
|
||||||
|
(via "Via[0-1]_800:400_um" 10000 -50000 (net 2)(type route))
|
||||||
|
(via "Via[0-1]_800:400_um" 13000 -47000 (net 3)(type route))
|
||||||
|
(via "Via[0-1]_800:400_um" 35000 -20000 (net 3)(type route))
|
||||||
|
(via "Via[0-1]_800:400_um" 20000 -50000 (net 5)(type route))
|
||||||
|
(via "Via[0-1]_800:400_um" 20000 -20000 (net 5)(type route))
|
||||||
|
(via "Via[0-1]_800:400_um" 40000 -20000 (net 5)(type route))
|
||||||
|
(via "Via[0-1]_800:400_um" 60000 -50000 (net 5)(type route))
|
||||||
|
(via "Via[0-1]_800:400_um" 50000 -30000 (net 5)(type route))
|
||||||
|
(via "Via[0-1]_800:400_um" 40000 -40000 (net 5)(type route))
|
||||||
|
(via "Via[0-1]_800:400_um" 60000 -20000 (net 5)(type route))
|
||||||
|
(via "Via[0-1]_800:400_um" 50000 -40000 (net 5)(type route))
|
||||||
|
)
|
||||||
|
)
|
||||||
Loading…
Reference in New Issue