use super::common::ListToken; use super::read::ReadDsn; use super::read::{ListTokenizer, ParseError, ParseErrorContext}; use super::write::ListWriter; use super::write::WriteSes; use crate::math::PointWithRotation; use specctra_derive::ReadDsn; use specctra_derive::WriteSes; #[derive(ReadDsn, WriteSes, Debug)] pub struct Dummy {} #[derive(ReadDsn, WriteSes, Debug)] pub struct SesFile { pub session: Session, } #[derive(ReadDsn, WriteSes, Debug)] pub struct Session { #[anon] pub id: String, pub routes: Routes, } #[derive(ReadDsn, WriteSes, Debug)] pub struct Routes { pub resolution: Resolution, pub library_out: Library, pub network_out: NetworkOut, } #[derive(ReadDsn, WriteSes, Debug)] pub struct NetworkOut { #[vec("net")] pub net: Vec, } #[derive(ReadDsn, WriteSes, Debug)] pub struct NetOut { #[anon] pub name: String, #[vec("wire")] pub wire: Vec, #[vec("via")] pub via: Vec, } #[derive(ReadDsn, WriteSes, Debug)] pub struct DsnFile { pub pcb: Pcb, } #[derive(ReadDsn, WriteSes, Debug)] pub struct Pcb { #[anon] pub name: String, pub parser: Option, pub resolution: Resolution, pub unit: Option, pub structure: Structure, pub placement: Placement, pub library: Library, pub network: Network, pub wiring: Wiring, } #[derive(WriteSes, Debug)] pub struct Parser { pub string_quote: Option, pub space_in_quoted_tokens: Option, pub host_cad: Option, pub host_version: Option, } #[derive(ReadDsn, WriteSes, Debug)] pub struct Resolution { #[anon] pub unit: String, #[anon] pub value: f32, } #[derive(WriteSes, Debug)] pub struct Structure { #[vec("layer")] pub layers: Vec, pub boundary: Boundary, #[vec("plane")] pub planes: Vec, #[vec("keepout")] pub keepouts: Vec, pub via: ViaNames, #[vec("grid")] pub grids: Vec, // this is a vec of special structs because EasyEDA uses different syntax // it outputs a sequence of rules containing a clearance each // (in class rules it outputs a single rule with all clearances like KiCad) #[vec("rule")] pub rules: Vec, } // custom impl to handle layers appearing late impl ReadDsn for Structure { fn read_dsn(tokenizer: &mut ListTokenizer) -> Result { let mut value = Self { layers: tokenizer.read_named_array("layer")?, boundary: tokenizer.read_named("boundary")?, planes: tokenizer.read_named_array("plane")?, keepouts: tokenizer.read_named_array("keepout")?, via: tokenizer.read_named("via")?, grids: tokenizer.read_named_array("grid")?, rules: tokenizer.read_named_array("rule")?, }; value .layers .append(&mut tokenizer.read_named_array("layer")?); Ok(value) } } #[derive(ReadDsn, WriteSes, Debug)] pub struct Layer { #[anon] pub name: String, pub r#type: String, pub property: Option, } #[derive(ReadDsn, WriteSes, Debug)] pub struct Property { pub index: usize, } #[derive(ReadDsn, WriteSes, Debug)] pub struct Boundary { pub path: Path, } #[derive(ReadDsn, WriteSes, Debug)] pub struct Plane { #[anon] pub net: String, pub polygon: Polygon, } #[derive(ReadDsn, WriteSes, Debug)] pub struct ViaNames { #[anon_vec] pub names: Vec, } #[derive(ReadDsn, WriteSes, Debug)] pub struct Grid { #[anon] pub kind: String, #[anon] pub value: f64, } #[derive(ReadDsn, WriteSes, Debug)] pub struct StructureRule { pub width: Option, #[vec("clearance", "clear")] pub clearances: Vec, } #[derive(ReadDsn, WriteSes, Debug)] pub struct Placement { #[vec("component")] pub components: Vec, } #[derive(ReadDsn, WriteSes, Debug)] pub struct Component { #[anon] pub name: String, #[vec("place")] pub places: Vec, } #[derive(ReadDsn, WriteSes, Debug)] #[allow(non_snake_case)] pub struct Place { #[anon] pub name: String, #[anon] pub x: f64, #[anon] pub y: f64, #[anon] pub side: String, #[anon] pub rotation: f64, pub PN: Option, } impl Place { pub fn point_with_rotation(&self) -> PointWithRotation { PointWithRotation { pos: (self.x, self.y).into(), rot: self.rotation, } } } #[derive(ReadDsn, WriteSes, Debug)] pub struct Library { #[vec("image")] pub images: Vec, #[vec("padstack")] pub padstacks: Vec, } impl Library { pub fn find_padstack_by_name(&self, name: &str) -> Option<&Padstack> { self.padstacks.iter().find(|padstack| padstack.name == name) } } #[derive(ReadDsn, WriteSes, Debug)] pub struct Image { #[anon] pub name: String, #[vec("outline")] pub outlines: Vec, #[vec("pin")] pub pins: Vec, #[vec("keepout")] pub keepouts: Vec, } #[derive(ReadDsn, WriteSes, Debug)] pub struct Outline { pub path: Path, } #[derive(ReadDsn, WriteSes, Debug)] pub struct Pin { #[anon] pub name: String, pub rotate: Option, #[anon] pub id: String, #[anon] pub x: f64, #[anon] pub y: f64, } impl Pin { pub fn point_with_rotation(&self) -> PointWithRotation { PointWithRotation { pos: (self.x, self.y).into(), rot: self.rotate.unwrap_or(0.0), } } } #[derive(ReadDsn, WriteSes, Debug)] pub struct Keepout { #[anon] pub idk: String, #[anon] pub shape: Shape, } #[derive(ReadDsn, WriteSes, Debug)] pub struct Padstack { #[anon] pub name: String, #[vec("shape")] pub shapes: Vec, pub attach: Option, } // TODO: derive for enums if more than this single one is needed #[derive(Debug)] pub enum Shape { Circle(Circle), Rect(Rect), Path(Path), Polygon(Polygon), } impl ReadDsn for Shape { fn read_dsn(tokenizer: &mut ListTokenizer) -> Result { let ctx = tokenizer.context(); let name = tokenizer.consume_token()?.expect_any_start()?; let value = match name.as_str() { "circle" => Ok(Shape::Circle(tokenizer.read_value()?)), "rect" => Ok(Shape::Rect(tokenizer.read_value()?)), "path" => Ok(Shape::Path(tokenizer.read_value()?)), "polygon" => Ok(Shape::Polygon(tokenizer.read_value()?)), _ => Err(ParseError::Expected("a different keyword").add_context(ctx)), }; tokenizer.consume_token()?.expect_end()?; value } } impl WriteSes for Shape { fn write_dsn(&self, writer: &mut ListWriter) -> Result<(), std::io::Error> { match self { Self::Circle(inner) => writer.write_named("circle", inner), Self::Rect(inner) => writer.write_named("rect", inner), Self::Path(inner) => writer.write_named("path", inner), Self::Polygon(inner) => writer.write_named("polygon", inner), } } } #[derive(ReadDsn, WriteSes, Debug)] pub struct Circle { #[anon] pub layer: String, #[anon] pub diameter: f64, #[anon] pub offset: Option, } #[derive(ReadDsn, WriteSes, Debug)] pub struct Network { #[vec("net")] pub nets: Vec, #[vec("class")] pub classes: Vec, } #[derive(ReadDsn, WriteSes, Debug)] // dsn names this "net", but it's a structure unrelated to "net" in wiring or elsewhere pub struct NetPinAssignments { #[anon] pub name: String, pub pins: Option, } #[derive(ReadDsn, WriteSes, Debug)] pub struct Pins { #[anon_vec] pub names: Vec, } #[derive(ReadDsn, WriteSes, Debug)] pub struct Class { #[anon] pub name: String, #[anon_vec] pub nets: Vec, pub circuit: Circuit, pub rule: Rule, } #[derive(ReadDsn, WriteSes, Debug)] pub struct Circuit { pub use_via: String, } #[derive(ReadDsn, WriteSes, Debug)] pub struct Wiring { #[vec("wire")] pub wires: Vec, #[vec("via")] pub vias: Vec, } #[derive(ReadDsn, WriteSes, Debug)] pub struct Wire { pub path: Path, pub net: String, pub r#type: String, } #[derive(ReadDsn, WriteSes, Debug)] pub struct WireOut { pub path: Path, } //////////////////////////////////////////// // structs that appear in multiple places // //////////////////////////////////////////// // This type isn't meant to be deserialized as is (single points are // more conveniently represented as fields on the enclosing struct) // It exists to give a way to read arrays of coordinates // (and enforce that such an array actually contains a whole number of points) #[derive(Debug)] pub struct Point { pub x: f64, pub y: f64, } // Custom impl for the case described above impl ReadDsn for Vec { fn read_dsn(tokenizer: &mut ListTokenizer) -> Result { let mut array = Vec::::new(); loop { let input = tokenizer.consume_token()?; if let ListToken::Leaf { value: ref x } = input.token { let x = x .parse::() .map_err(|_| ParseError::Expected("f64").add_context(input.context))?; let y = tokenizer.read_value::()?; array.push(Point { x, y }); } else { tokenizer.return_token(input); break; } } Ok(array) } } impl ReadDsn for Option { fn read_dsn(tokenizer: &mut ListTokenizer) -> Result { let input = tokenizer.consume_token()?; if let ListToken::Leaf { value: ref x } = input.token { let x = x .parse::() .map_err(|_| ParseError::Expected("f64").add_context(input.context))?; let y = tokenizer.read_value::()?; Ok(Some(Point { x, y })) } else { tokenizer.return_token(input); Ok(None) } } } impl WriteSes for Vec { fn write_dsn(&self, writer: &mut ListWriter) -> Result<(), std::io::Error> { for elem in self { writer.write_value(&elem.x)?; writer.write_value(&elem.y)?; } Ok(()) } } impl WriteSes for Option { fn write_dsn(&self, writer: &mut ListWriter) -> Result<(), std::io::Error> { if let Some(value) = self { writer.write_value(&value.x)?; writer.write_value(&value.y)?; } Ok(()) } } #[derive(ReadDsn, WriteSes, Debug)] pub struct Polygon { #[anon] pub layer: String, #[anon] pub width: f64, #[anon] pub coords: Vec, } #[derive(ReadDsn, WriteSes, Debug)] pub struct Path { #[anon] pub layer: String, #[anon] pub width: f64, #[anon] pub coords: Vec, } #[derive(ReadDsn, WriteSes, Debug)] pub struct Rect { #[anon] pub layer: String, #[anon] pub x1: f64, #[anon] pub y1: f64, #[anon] pub x2: f64, #[anon] pub y2: f64, } #[derive(ReadDsn, WriteSes, Debug)] pub struct Via { #[anon] pub name: String, #[anon] pub x: f64, #[anon] pub y: f64, pub net: String, pub r#type: String, } #[derive(ReadDsn, WriteSes, Debug)] pub struct Rule { pub width: f32, #[vec("clearance")] pub clearances: Vec, } #[derive(ReadDsn, WriteSes, Clone, Debug)] pub struct Clearance { #[anon] pub value: f32, pub r#type: Option, }