dsn, sdl2-demo: replace hardcoded layout with basic .dsn file

This commit is contained in:
Tomasz Cichoń 2024-02-21 19:09:38 +01:00
parent 08c03a0e77
commit 3f6bad2ed6
6 changed files with 1034 additions and 351 deletions

View File

@ -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,
},
})
.unwrap();
let dot_start2 = router
.layout
.add_fixed_dot(FixedDotWeight {
component: component3_1,
circle: Circle {
pos: (100.5, 500.5).into(),
r: 8.0,
},
})
.unwrap();
let dot_start3 = router
.layout
.add_fixed_dot(FixedDotWeight {
component: component4_1,
circle: Circle {
pos: (160.5, 430.5).into(),
r: 8.0,
},
})
.unwrap();
let dot_end = router
.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();
let _ = router.layout.add_fixed_seg(
dot1_1,
dot2_1,
FixedSegWeight {
component: component2,
width: 3.0,
},
); );
let dot2_2 = router // add vias to layout and save indices of dots in the order they appear in the file
.layout let dot_indices: Vec<_> = pcb.wiring.vias
.add_fixed_dot(FixedDotWeight { .iter()
component: component2, .map(|via| {
circle: Circle { let net_id = net_ids.get(&via.net.0).unwrap();
pos: (600.5, 500.5).into(), let component = router.layout.add_component(*net_id as i64);
r: 8.0,
}, // 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 _ = router.layout.add_fixed_seg( for wire in pcb.wiring.wires.iter() {
dot2_1, let net_id = net_ids.get(&wire.net.0).unwrap();
dot2_2, let component = router.layout.add_component(*net_id as i64);
FixedSegWeight {
component: component2,
width: 3.0,
},
);
let dot3 = router // add the first coordinate in the wire path as a dot and save its index
.layout let mut prev_index = router.layout.add_fixed_dot(FixedDotWeight {
.add_fixed_dot(FixedDotWeight { component,
component: component2,
circle: Circle { circle: Circle {
pos: (400.5, 200.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 dot4 = 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: component2, component,
circle: Circle { circle: Circle {
pos: (400.5, 400.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,
}
}).unwrap();
let _ = router.layout.add_fixed_seg( // add a seg between the current and previous coords
dot3, let _ = router.layout.add_fixed_seg(
dot4, prev_index,
FixedSegWeight { index,
component: component2, FixedSegWeight {
width: 3.0, component,
}, width: wire.path.width as f64 / 100.0,
); },
).unwrap();
let dot5 = router prev_index = index;
.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(())
} }

632
src/dsn/de.rs Normal file
View File

@ -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)
}
}

2
src/dsn/mod.rs Normal file
View File

@ -0,0 +1,2 @@
pub mod de;
pub mod structure;

239
src/dsn/structure.rs Normal file
View File

@ -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>,
}

View File

@ -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;

77
tests/data/test.dsn Normal file
View File

@ -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))
)
)