dsn: add new parser/writer and .ses export prototype

This commit is contained in:
Tomasz Cichoń 2024-06-12 13:29:24 +02:00
parent 90bc90350d
commit 7d9bf1346a
11 changed files with 1255 additions and 0 deletions

View File

@ -32,6 +32,10 @@ itertools = "0.8.2"
contracts = "0.6.3" contracts = "0.6.3"
bimap = "0.6.3" bimap = "0.6.3"
log = "0.4" log = "0.4"
utf8-chars = "3.0.2"
[dependencies.dsn_derive]
path = "macro/dsn_derive"
[dependencies.geo] [dependencies.geo]
version = "0.25.1" version = "0.25.1"

View File

@ -0,0 +1,12 @@
[package]
name = "dsn_derive"
version = "0.1.0"
edition = "2021"
[lib]
proc-macro = true
[dependencies]
proc-macro2 = "1.0.79"
quote = "1.0.35"
syn = "2.0.55"

View File

@ -0,0 +1,40 @@
use proc_macro::TokenStream;
use syn::{DeriveInput, Attribute, LitStr};
mod read;
mod write;
#[proc_macro_derive(ReadDsn, attributes(opt, anon, vec, anon_vec))]
pub fn derive_read(input: TokenStream)
-> TokenStream
{
let input = syn::parse_macro_input!(input as DeriveInput);
read::impl_read(&input).into()
}
#[proc_macro_derive(WriteDsn, attributes(anon))]
pub fn derive_write(input: TokenStream)
-> TokenStream
{
let input = syn::parse_macro_input!(input as DeriveInput);
write::impl_write(&input).into()
}
fn attr_present(attrs: &Vec<Attribute>, name: &str) -> bool {
attrs
.iter()
.find(|attr| attr.path().is_ident(name))
.is_some()
}
fn attr_content(attrs: &Vec<Attribute>, name: &str) -> Option<String> {
attrs
.iter()
.find(|attr| attr.path().is_ident(name))
.and_then(|attr| Some(attr
.parse_args::<LitStr>()
.expect("string literal")
.value()
))
}

View File

@ -0,0 +1,84 @@
use proc_macro2::TokenStream;
use quote::quote;
use syn::{Data, DeriveInput, Fields, Field};
use syn::Type::Path;
use syn::ext::IdentExt;
use crate::attr_present;
use crate::attr_content;
pub fn impl_read(input: &DeriveInput) -> TokenStream {
let name = &input.ident;
let body = impl_body(&input.data);
quote! {
impl<R: std::io::BufRead> ReadDsn<R> for #name {
fn read_dsn(tokenizer: &mut ListTokenizer<R>)
-> Result<Self, ParseError>
{
#body
}
}
}
}
fn impl_body(data: &Data) -> TokenStream {
match data {
Data::Struct(data) => {
match &data.fields {
Fields::Named(fields) => {
let fields = fields.named.iter().map(|field| {
impl_field(field)
});
quote! {
Ok(Self {
#(#fields)*
})
}
}
_ => unimplemented!()
}
}
Data::Enum(_data) => {
todo!();
}
_ => unimplemented!()
}
}
fn impl_field(field: &Field) -> TokenStream {
let name = &field.ident;
let name_str = name.as_ref().expect("field name").unraw();
if attr_present(&field.attrs, "anon") {
quote! {
#name: tokenizer.read_value()?,
}
} else if let Some(dsn_name) = attr_content(&field.attrs, "vec") {
quote! {
#name: tokenizer.read_named_array(#dsn_name)?,
}
} else if attr_present(&field.attrs, "anon_vec") {
quote! {
#name: tokenizer.read_array()?,
}
} else {
if let Path(type_path) = &field.ty {
let segments = &type_path.path.segments;
if segments.len() == 1 {
let ident = &segments.first().unwrap().ident;
if ident == "Option" {
return quote! {
#name: tokenizer.read_optional(stringify!(#name_str))?,
}
}
}
}
quote! {
#name: tokenizer.read_named(stringify!(#name_str))?,
}
}
}

View File

@ -0,0 +1,84 @@
use proc_macro2::TokenStream;
use quote::quote;
use syn::{Data, DeriveInput, Fields, Field};
use syn::Type::Path;
use syn::ext::IdentExt;
use crate::attr_present;
use crate::attr_content;
pub fn impl_write(input: &DeriveInput) -> TokenStream {
let name = &input.ident;
let body = impl_body(&input.data);
quote! {
impl<W: std::io::Write> WriteDsn<W> for #name {
fn write_dsn(&self, writer: &mut ListWriter<W>)
-> std::io::Result<()>
{
#body
}
}
}
}
fn impl_body(data: &Data) -> TokenStream {
match data {
Data::Struct(data) => {
match &data.fields {
Fields::Named(fields) => {
let fields = fields.named.iter().map(|field| {
impl_field(field)
});
quote! {
#(#fields)*
Ok(())
}
}
_ => unimplemented!()
}
}
_ => unimplemented!()
}
}
fn impl_field(field: &Field) -> TokenStream {
let name = &field.ident;
let name_str = name.as_ref().expect("field name").unraw();
if attr_present(&field.attrs, "anon") {
quote! {
writer.write_value(&self.#name)?;
}
} else if let Some(dsn_name) = attr_content(&field.attrs, "vec") {
quote! {
writer.write_named_array(#dsn_name, &self.#name)?;
}
} else if attr_present(&field.attrs, "anon_vec") {
quote! {
writer.write_array(&self.#name)?;
}
} else {
if let Path(type_path) = &field.ty {
let segments = &type_path.path.segments;
if segments.len() == 1 {
let ident = &segments.first().unwrap().ident;
if ident == "Option" {
return quote! {
writer.write_optional(stringify!(#name_str), &self.#name)?;
}
}
}
}
quote! {
writer.write_named(stringify!(#name_str), &self.#name)?;
}
}
}

58
src/dsn/common.rs Normal file
View File

@ -0,0 +1,58 @@
use dsn_derive::ReadDsn;
use super::structure2::*;
use super::read::{ListTokenizer, ReadDsn, ParseError};
use super::write::{ListWriter, WriteDsn};
pub enum ListToken {
Start { name: String },
Leaf { value: String },
End,
}
impl ListToken {
pub fn expect_start(self, name: &'static str) -> Result<(), ParseError> {
if let Self::Start { name: actual_name } = self {
if name == actual_name {
Ok(())
} else {
Err(ParseError::ExpectedStartOfList(name))
}
} else {
Err(ParseError::ExpectedStartOfList(name))
}
}
pub fn expect_any_start(self) -> Result<String, ParseError> {
if let Self::Start { name } = self {
Ok(name)
} else {
Err(ParseError::ExpectedStartOfList(""))
}
}
pub fn expect_leaf(self) -> Result<String, ParseError> {
if let Self::Leaf { value } = self {
Ok(value)
} else {
Err(ParseError::Expected("leaf value"))
}
}
pub fn expect_end(self) -> Result<(), ParseError> {
if let Self::End = self {
Ok(())
} else {
Err(ParseError::Expected("end of list"))
}
}
pub fn len(&self) -> usize {
match &self {
Self::Start { name } => 1 + name.len(),
Self::Leaf { value } => value.len(),
Self::End => 1,
}
}
}

View File

@ -32,7 +32,83 @@ pub struct DsnDesign {
impl DsnDesign { impl DsnDesign {
pub fn load_from_file(filename: &str) -> Result<Self, LoadingError> { pub fn load_from_file(filename: &str) -> Result<Self, LoadingError> {
let file = std::fs::File::open(filename)?;
let reader = std::io::BufReader::new(file);
let mut list_reader = super::read::ListTokenizer::new(reader);
let mut dsn = list_reader.read_value::<super::structure2::DsnFile>();
// TODO: make_board() still uses the old version of structure.rs
// so we can't pass the data to topola for real
if let Ok(dsn) = dsn {
use super::structure2::*;
// (this entire if let block does not belong here)
let ses_name = filename.replace(".dsn", ".ses");
let file2 = std::fs::File::create(ses_name).unwrap();
let writer = std::io::BufWriter::new(file2);
let mut list_writer = super::write::ListWriter::new(writer);
let mut net_outs = HashMap::<String, NetOut>::new();
for mut wire in dsn.pcb.wiring.wires {
// move wires to double check that importing the resulting file into KiCad does something
for point in &mut wire.path.coords {
point.x += 1000.0;
}
if let Some(net) = net_outs.get_mut(&wire.net) {
net.wire.push(wire);
} else {
net_outs.insert(wire.net.clone(), NetOut {
name: wire.net.clone(),
wire: vec!(wire),
via: Vec::new()
});
}
}
for via in dsn.pcb.wiring.vias {
if let Some(net) = net_outs.get_mut(&via.net) {
net.via.push(via);
} else {
net_outs.insert(via.net.clone(), NetOut {
name: via.net.clone(),
wire: Vec::new(),
via: vec!(via)
});
}
}
// build a basic .ses file from what was loaded
let ses = SesFile {
session: Session {
id: "ID".to_string(),
routes: Routes {
resolution: Resolution {
unit: "um".into(),
// TODO: why does resolution need to be adjusted from what was imported?
value: 1.0
},
library_out: Library {
images: Vec::new(),
padstacks: dsn.pcb.library.padstacks,
},
network_out: NetworkOut {
net: net_outs.into_values().collect(),
},
}
}
};
println!("{:?}", list_writer.write_value(&ses));
} else {
dbg!(dsn);
}
let contents = std::fs::read_to_string(filename)?; let contents = std::fs::read_to_string(filename)?;
Self::load_from_string(contents) Self::load_from_string(contents)
} }

View File

@ -2,3 +2,7 @@ mod de;
pub mod design; pub mod design;
pub mod mesadata; pub mod mesadata;
mod structure; mod structure;
mod common;
mod structure2;
mod read;
mod write;

312
src/dsn/read.rs Normal file
View File

@ -0,0 +1,312 @@
use thiserror::Error;
use utf8_chars::BufReadCharsExt;
use super::common::ListToken;
use super::structure2::Parser;
#[derive(Error, Debug)]
pub enum ParseError {
#[error("unexpected end of file")]
Eof,
#[error(transparent)]
Io(#[from] std::io::Error),
#[error("expected {0}")]
Expected(&'static str),
#[error("expected ({0}")]
ExpectedStartOfList(&'static str),
}
pub trait ReadDsn<R: std::io::BufRead>: Sized {
fn read_dsn(tokenizer: &mut ListTokenizer<R>) -> Result<Self, ParseError>;
}
// custom impl feeding the read values back into the tokenizer
impl<R: std::io::BufRead> ReadDsn<R> for Parser {
fn read_dsn(tokenizer: &mut ListTokenizer<R>) -> Result<Self, ParseError> {
Ok(Self {
string_quote: tokenizer
.read_optional("string_quote")?
.inspect(|v| tokenizer.quote_char = Some(*v)),
space_in_quoted_tokens: tokenizer
.read_optional("space_in_quoted_tokens")?
.inspect(|v| tokenizer.space_in_quoted = *v),
host_cad: tokenizer.read_optional("host_cad")?,
host_version: tokenizer.read_optional("host_version")?,
})
}
}
impl<R: std::io::BufRead> ReadDsn<R> for String {
fn read_dsn(tokenizer: &mut ListTokenizer<R>) -> Result<Self, ParseError> {
Ok(tokenizer.consume_token()?.expect_leaf()?)
}
}
impl<R: std::io::BufRead> ReadDsn<R> for char {
fn read_dsn(tokenizer: &mut ListTokenizer<R>) -> Result<Self, ParseError> {
let string = tokenizer.consume_token()?.expect_leaf()?;
if string.chars().count() == 1 {
Ok(string.chars().next().unwrap())
} else {
Err(ParseError::Expected("a single character"))
}
}
}
impl<R: std::io::BufRead> ReadDsn<R> for bool {
fn read_dsn(tokenizer: &mut ListTokenizer<R>) -> Result<Self, ParseError> {
match tokenizer.consume_token()?.expect_leaf()?.as_str() {
"on" => Ok(true),
"off" => Ok(false),
_ => Err(ParseError::Expected("boolean")),
}
}
}
impl<R: std::io::BufRead> ReadDsn<R> for i32 {
fn read_dsn(tokenizer: &mut ListTokenizer<R>) -> Result<Self, ParseError> {
Ok(tokenizer
.consume_token()?
.expect_leaf()?
.parse()
.map_err(|_| ParseError::Expected("i32"))?)
}
}
impl<R: std::io::BufRead> ReadDsn<R> for u32 {
fn read_dsn(tokenizer: &mut ListTokenizer<R>) -> Result<Self, ParseError> {
Ok(tokenizer
.consume_token()?
.expect_leaf()?
.parse()
.map_err(|_| ParseError::Expected("u32"))?)
}
}
impl<R: std::io::BufRead> ReadDsn<R> for usize {
fn read_dsn(tokenizer: &mut ListTokenizer<R>) -> Result<Self, ParseError> {
Ok(tokenizer
.consume_token()?
.expect_leaf()?
.parse()
.map_err(|_| ParseError::Expected("usize"))?)
}
}
impl<R: std::io::BufRead> ReadDsn<R> for f32 {
fn read_dsn(tokenizer: &mut ListTokenizer<R>) -> Result<Self, ParseError> {
Ok(tokenizer
.consume_token()?
.expect_leaf()?
.parse()
.map_err(|_| ParseError::Expected("f32"))?)
}
}
pub struct ListTokenizer<R: std::io::BufRead> {
reader: R,
peeked_char: Option<char>,
cached_token: Option<ListToken>,
space_in_quoted: bool,
quote_char: Option<char>,
line: usize,
column: usize,
}
impl<R: std::io::BufRead> ListTokenizer<R> {
pub fn new(reader: R) -> Self {
Self {
reader: reader,
peeked_char: None,
cached_token: None,
space_in_quoted: false,
quote_char: None,
line: 1,
column: 0,
}
}
fn next_char(&mut self) -> Result<char, ParseError> {
let return_chr = if let Some(chr) = self.peeked_char {
self.peeked_char = None;
chr
} else {
self.reader.chars().next().ok_or(ParseError::Eof)??
};
if return_chr == '\n' {
self.line += 1;
self.column = 0;
} else {
self.column += 1;
}
Ok(return_chr)
}
fn peek_char(&mut self) -> Result<char, ParseError> {
if let Some(chr) = self.peeked_char {
Ok(chr)
} else {
let chr = self.reader.chars().next().ok_or(ParseError::Eof)??;
self.peeked_char = Some(chr);
Ok(chr)
}
}
fn skip_whitespace(&mut self) -> Result<(), ParseError> {
loop {
let chr = self.peek_char()?;
if chr == ' ' || chr == '\r' || chr == '\n' {
self.next_char().unwrap();
} else {
return Ok(());
}
}
}
fn read_string(&mut self) -> Result<String, ParseError> {
if let Some(chr) = self.quote_char {
if chr == self.peek_char()? {
return self.read_quoted();
}
}
self.read_unquoted()
}
fn read_unquoted(&mut self) -> Result<String, ParseError> {
let mut string = String::new();
loop {
let chr = self.peek_char()?;
if chr == ' ' || chr == '(' || chr == ')' || chr == '\r' || chr == '\n' {
break;
}
string.push(self.next_char().unwrap());
}
if string.is_empty() {
Err(ParseError::Expected("string (unquoted)"))
} else {
Ok(string)
}
}
fn read_quoted(&mut self) -> Result<String, ParseError> {
let mut string = String::new();
if self.next_char().unwrap() != self.quote_char.unwrap() {
panic!();
}
loop {
let chr = self.peek_char()?;
if !self.space_in_quoted && chr == ' ' {
panic!("found a space inside a quoted string, but file didn't declare this possibility");
}
if chr == self.quote_char.unwrap() {
self.next_char().unwrap();
break;
}
string.push(self.next_char().unwrap());
}
Ok(string)
}
// the following two methods effectively allow 1 token of lookahead
// returns next token, either a cached one returned earlier or a newly read one
pub fn consume_token(&mut self) -> Result<ListToken, ParseError> {
// move out of cache if not empty, otherwise consume input
// always leaves cache empty
if let Some(token) = self.cached_token.take() {
Ok(token)
} else {
let token = self.read_token()?;
Ok(token)
}
}
// puts a token back into cache, to be consumed by something else
pub fn return_token(&mut self, token: ListToken) {
self.cached_token = Some(token);
}
fn read_token(&mut self) -> Result<ListToken, ParseError> {
self.skip_whitespace()?;
let chr = self.peek_char()?;
Ok(if chr == '(' {
self.next_char().unwrap();
self.skip_whitespace()?;
ListToken::Start { name: self.read_string()? }
} else if chr == ')' {
self.next_char().unwrap();
ListToken::End
} else {
ListToken::Leaf { value: self.read_string()? }
})
}
pub fn read_value<T: ReadDsn<R>>(&mut self) -> Result<T, ParseError> {
T::read_dsn(self)
}
pub fn read_named<T: ReadDsn<R>>(&mut self, name: &'static str) -> Result<T, ParseError> {
self.consume_token()?.expect_start(name)?;
let value = self.read_value::<T>()?;
self.consume_token()?.expect_end()?;
Ok(value)
}
pub fn read_optional<T: ReadDsn<R>>(&mut self, name: &'static str) -> Result<Option<T>, ParseError> {
let token = self.consume_token()?;
if let ListToken::Start { name: ref actual_name } = token {
if actual_name == name {
let value = self.read_value::<T>()?;
self.consume_token()?.expect_end()?;
Ok(Some(value))
} else {
self.return_token(token);
Ok(None)
}
} else {
self.return_token(token);
Ok(None)
}
}
pub fn read_array<T: ReadDsn<R>>(&mut self) -> Result<Vec<T>, ParseError> {
let mut array = Vec::<T>::new();
loop {
let token = self.consume_token()?;
if let ListToken::Leaf { .. } = token {
self.return_token(token);
array.push(self.read_value::<T>()?);
} else {
self.return_token(token);
break
}
}
Ok(array)
}
pub fn read_named_array<T: ReadDsn<R>>(&mut self, name: &'static str) -> Result<Vec<T>, ParseError> {
let mut array = Vec::<T>::new();
loop {
let token = self.consume_token()?;
if let ListToken::Start { name: ref actual_name } = token {
if actual_name == name {
let value = self.read_value::<T>()?;
self.consume_token()?.expect_end()?;
array.push(value);
} else {
self.return_token(token);
break
}
} else {
self.return_token(token);
break
}
}
Ok(array)
}
}

399
src/dsn/structure2.rs Normal file
View File

@ -0,0 +1,399 @@
use super::read::ReadDsn;
use super::write::WriteDsn;
use dsn_derive::ReadDsn;
use dsn_derive::WriteDsn;
use super::common::ListToken;
use super::read::{ListTokenizer, ParseError};
use super::write::ListWriter;
#[derive(ReadDsn, WriteDsn, Debug)]
pub struct Dummy {}
#[derive(ReadDsn, WriteDsn, Debug)]
pub struct SesFile {
pub session: Session,
}
#[derive(ReadDsn, WriteDsn, Debug)]
pub struct Session {
#[anon] pub id: String,
pub routes: Routes,
}
#[derive(ReadDsn, WriteDsn, Debug)]
pub struct Routes {
pub resolution: Resolution,
pub library_out: Library,
pub network_out: NetworkOut,
}
#[derive(ReadDsn, WriteDsn, Debug)]
pub struct NetworkOut {
#[vec("net")]
pub net: Vec<NetOut>,
}
#[derive(ReadDsn, WriteDsn, Debug)]
pub struct NetOut {
#[anon] pub name: String,
#[vec("wire")]
pub wire: Vec<Wire>,
#[vec("via")]
pub via: Vec<Via>,
}
#[derive(ReadDsn, WriteDsn, Debug)]
pub struct DsnFile {
pub pcb: Pcb,
}
#[derive(ReadDsn, WriteDsn, Debug)]
pub struct Pcb {
#[anon] pub name: String,
pub parser: Parser,
pub resolution: Resolution,
pub unit: String,
pub structure: Structure,
pub placement: Placement,
pub library: Library,
pub network: Network,
pub wiring: Wiring,
}
#[derive(WriteDsn, Debug)]
pub struct Parser {
pub string_quote: Option<char>,
pub space_in_quoted_tokens: Option<bool>,
pub host_cad: Option<String>,
pub host_version: Option<String>,
}
#[derive(ReadDsn, WriteDsn, Debug)]
pub struct Resolution {
#[anon] pub unit: String,
#[anon] pub value: f32,
}
#[derive(ReadDsn, WriteDsn, Debug)]
pub struct Structure {
#[vec("layer")]
pub layers: Vec<Layer>,
pub boundary: Boundary,
#[vec("plane")]
pub planes: Vec<Plane>,
pub via: ViaNames,
pub rule: Rule,
}
#[derive(ReadDsn, WriteDsn, Debug)]
pub struct Layer {
#[anon] pub name: String,
pub r#type: String,
pub property: Property,
}
#[derive(ReadDsn, WriteDsn, Debug)]
pub struct Property {
pub index: usize,
}
#[derive(ReadDsn, WriteDsn, Debug)]
pub struct Boundary {
pub path: Path,
}
#[derive(ReadDsn, WriteDsn, Debug)]
pub struct Plane {
#[anon] pub net: String,
pub polygon: Polygon,
}
#[derive(ReadDsn, WriteDsn, Debug)]
pub struct ViaNames {
#[anon_vec]
pub names: Vec<String>,
}
#[derive(ReadDsn, WriteDsn, Debug)]
pub struct Placement {
#[vec("component")]
pub components: Vec<Component>,
}
#[derive(ReadDsn, WriteDsn, Debug)]
pub struct Component {
#[anon] pub name: String,
#[vec("place")]
pub places: Vec<Place>,
}
#[derive(ReadDsn, WriteDsn, Debug)]
#[allow(non_snake_case)]
pub struct Place {
#[anon] pub name: String,
#[anon] pub x: f32,
#[anon] pub y: f32,
#[anon] pub side: String,
#[anon] pub rotation: f32,
pub PN: Option<String>,
}
#[derive(ReadDsn, WriteDsn, Debug)]
pub struct Library {
#[vec("image")]
pub images: Vec<Image>,
#[vec("padstack")]
pub padstacks: Vec<Padstack>,
}
#[derive(ReadDsn, WriteDsn, Debug)]
pub struct Image {
#[anon] pub name: String,
#[vec("outline")]
pub outlines: Vec<Outline>,
#[vec("pin")]
pub pins: Vec<Pin>,
#[vec("keepout")]
pub keepouts: Vec<Keepout>,
}
#[derive(ReadDsn, WriteDsn, Debug)]
pub struct Outline {
pub path: Path,
}
#[derive(ReadDsn, WriteDsn, Debug)]
pub struct Pin {
#[anon] pub name: String,
pub rotate: Option<f32>,
#[anon] pub id: String,
#[anon] pub x: f32,
#[anon] pub y: f32,
}
#[derive(ReadDsn, WriteDsn, Debug)]
pub struct Rotate {
pub angle: f32,
}
#[derive(ReadDsn, WriteDsn, Debug)]
pub struct Keepout {
#[anon] pub idk: String,
#[anon] pub shape: Shape,
}
#[derive(ReadDsn, WriteDsn, Debug)]
pub struct Padstack {
#[anon] pub name: String,
#[vec("shape")]
pub shapes: Vec<Shape>,
pub attach: bool,
}
// 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<R: std::io::BufRead> ReadDsn<R> for Shape {
fn read_dsn(tokenizer: &mut ListTokenizer<R>) -> Result<Self, ParseError> {
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")),
};
tokenizer.consume_token()?.expect_end()?;
value
}
}
impl<W: std::io::Write> WriteDsn<W> for Shape {
fn write_dsn(&self, writer: &mut ListWriter<W>) -> 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, WriteDsn, Debug)]
pub struct Circle {
#[anon] pub layer: String,
#[anon] pub diameter: u32,
#[anon] pub offset: Option<Point>,
}
#[derive(ReadDsn, WriteDsn, Debug)]
pub struct Network {
#[vec("net")]
pub nets: Vec<NetPinAssignments>,
#[vec("class")]
pub classes: Vec<Class>,
}
#[derive(ReadDsn, WriteDsn, 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: Pins,
}
#[derive(ReadDsn, WriteDsn, Debug)]
pub struct Pins {
#[anon_vec]
pub names: Vec<String>,
}
#[derive(ReadDsn, WriteDsn, Debug)]
pub struct Class {
#[anon]
pub name: String,
#[anon_vec]
pub nets: Vec<String>,
pub circuit: Circuit,
pub rule: Rule,
}
#[derive(ReadDsn, WriteDsn, Debug)]
pub struct Circuit {
pub use_via: String,
}
#[derive(ReadDsn, WriteDsn, Debug)]
pub struct Wiring {
#[vec("wire")]
pub wires: Vec<Wire>,
#[vec("via")]
pub vias: Vec<Via>,
}
#[derive(ReadDsn, WriteDsn, Debug)]
pub struct Wire {
pub path: Path,
pub net: String,
pub r#type: String,
}
////////////////////////////////////////////
// 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: f32,
pub y: f32,
}
// Custom impl for the case described above
impl<R: std::io::BufRead> ReadDsn<R> for Vec<Point> {
fn read_dsn(tokenizer: &mut ListTokenizer<R>) -> Result<Self, ParseError> {
let mut array = Vec::<Point>::new();
loop {
let token = tokenizer.consume_token()?;
if let ListToken::Leaf { value: ref x } = token {
let x = x.parse::<f32>().unwrap();
let y = tokenizer.read_value::<f32>()?;
array.push(Point { x, y });
} else {
tokenizer.return_token(token);
break
}
}
Ok(array)
}
}
impl<R: std::io::BufRead> ReadDsn<R> for Option<Point> {
fn read_dsn(tokenizer: &mut ListTokenizer<R>) -> Result<Self, ParseError> {
let token = tokenizer.consume_token()?;
if let ListToken::Leaf { value: ref x } = token {
let x = x.parse::<f32>().unwrap();
let y = tokenizer.read_value::<f32>()?;
Ok(Some(Point { x, y }))
} else {
tokenizer.return_token(token);
Ok(None)
}
}
}
impl<W: std::io::Write> WriteDsn<W> for Vec<Point> {
fn write_dsn(&self, writer: &mut ListWriter<W>) -> Result<(), std::io::Error> {
for elem in self {
writer.write_value(&elem.x)?;
writer.write_value(&elem.y)?;
}
Ok(())
}
}
impl<W: std::io::Write> WriteDsn<W> for Option<Point> {
fn write_dsn(&self, writer: &mut ListWriter<W>) -> Result<(), std::io::Error> {
if let Some(value) = self {
writer.write_value(&value.x)?;
writer.write_value(&value.y)?;
}
Ok(())
}
}
#[derive(ReadDsn, WriteDsn, Debug)]
pub struct Polygon {
#[anon] pub layer: String,
#[anon] pub width: f32,
#[anon] pub coords: Vec<Point>,
}
#[derive(ReadDsn, WriteDsn, Debug)]
pub struct Path {
#[anon] pub layer: String,
#[anon] pub width: f32,
#[anon] pub coords: Vec<Point>,
}
#[derive(ReadDsn, WriteDsn, Debug)]
pub struct Rect {
#[anon] pub layer: String,
#[anon] pub x1: f32,
#[anon] pub y1: f32,
#[anon] pub x2: f32,
#[anon] pub y2: f32,
}
#[derive(ReadDsn, WriteDsn, Debug)]
pub struct Via {
#[anon]
pub name: String,
#[anon]
pub x: i32,
#[anon]
pub y: i32,
pub net: String,
pub r#type: String,
}
#[derive(ReadDsn, WriteDsn, Debug)]
pub struct Rule {
pub width: f32,
#[vec("clearance")]
pub clearances: Vec<Clearance>,
}
#[derive(ReadDsn, WriteDsn, Debug)]
pub struct Clearance {
#[anon] pub value: f32,
pub r#type: Option<String>,
}

182
src/dsn/write.rs Normal file
View File

@ -0,0 +1,182 @@
use super::common::ListToken;
use std::io;
pub trait WriteDsn<W: io::Write> {
fn write_dsn(&self, writer: &mut ListWriter<W>) -> Result<(), io::Error>;
}
impl<W: io::Write> WriteDsn<W> for char {
fn write_dsn(&self, writer: &mut ListWriter<W>) -> Result<(), io::Error> {
writer.write_leaf(self.to_string())
}
}
impl<W: io::Write> WriteDsn<W> for String {
fn write_dsn(&self, writer: &mut ListWriter<W>) -> Result<(), io::Error> {
let string = if self.len() == 0 {
"\"\"".to_string()
} else if self.contains(" ")
|| self.contains("(")
|| self.contains(")")
|| self.contains("\n")
{
format!("\"{}\"", self)
} else {
self.to_string()
};
writer.write_leaf(string)
}
}
impl<W: io::Write> WriteDsn<W> for bool {
fn write_dsn(&self, writer: &mut ListWriter<W>) -> Result<(), io::Error> {
writer.write_leaf(match self {
true => "on".to_string(),
false => "off".to_string(),
})
}
}
impl<W: io::Write> WriteDsn<W> for i32 {
fn write_dsn(&self, writer: &mut ListWriter<W>) -> Result<(), io::Error> {
writer.write_leaf(self.to_string())
}
}
impl<W: io::Write> WriteDsn<W> for u32 {
fn write_dsn(&self, writer: &mut ListWriter<W>) -> Result<(), io::Error> {
writer.write_leaf(self.to_string())
}
}
impl<W: io::Write> WriteDsn<W> for usize {
fn write_dsn(&self, writer: &mut ListWriter<W>) -> Result<(), io::Error> {
writer.write_leaf(self.to_string())
}
}
impl<W: io::Write> WriteDsn<W> for f32 {
fn write_dsn(&self, writer: &mut ListWriter<W>) -> Result<(), io::Error> {
writer.write_leaf(self.to_string())
}
}
pub struct ListWriter<W: io::Write> {
writable: W,
indent_level: usize,
multiline_level: usize,
pub line_len: usize,
}
impl<W: io::Write> ListWriter<W> {
pub fn new(writable: W) -> Self {
Self {
writable,
indent_level: 0,
multiline_level: 0,
line_len: 0,
}
}
pub fn write_token(&mut self, token: ListToken) -> Result<(), io::Error> {
let len = token.len();
match token {
ListToken::Start { name } => {
write!(self.writable,
"\n{}({}",
" ".repeat(self.indent_level),
name
)?;
self.multiline_level = self.indent_level;
self.line_len = 2 * self.indent_level + len;
self.indent_level += 1;
Ok(())
}
ListToken::Leaf { value } => {
self.line_len += 1 + len;
write!(self.writable, " {}", value)
}
ListToken::End => {
if self.indent_level <= self.multiline_level {
self.indent_level -= 1;
self.line_len = 2 * self.indent_level + len;
write!(self.writable,
"\n{})",
" ".repeat(self.indent_level)
)
} else {
self.indent_level -= 1;
self.line_len += len;
write!(self.writable, ")")
}
}
}
}
pub fn write_leaf(&mut self, value: String) -> Result<(), io::Error> {
self.write_token(ListToken::Leaf { value })
}
pub fn write_value<T: WriteDsn<W>>(
&mut self,
value: &T,
) -> Result<(), io::Error> {
value.write_dsn(self)
}
pub fn write_named<T: WriteDsn<W>>(
&mut self,
name: &'static str,
value: &T,
)
-> Result<(), io::Error>
{
self.write_token(ListToken::Start { name: name.to_string() } )?;
self.write_value(value)?;
self.write_token(ListToken::End)?;
Ok(())
}
pub fn write_optional<T: WriteDsn<W>>(
&mut self,
name: &'static str,
optional: &Option<T>,
)
-> Result<(), io::Error>
{
if let Some(value) = optional {
self.write_named(name, value)?;
}
Ok(())
}
pub fn write_array<T: WriteDsn<W>>(
&mut self,
array: &Vec<T>,
)
-> Result<(), io::Error>
{
for elem in array {
self.write_value(elem)?;
}
Ok(())
}
pub fn write_named_array<T: WriteDsn<W>>(
&mut self,
name: &'static str,
array: &Vec<T>,
)
-> Result<(), io::Error>
{
for elem in array {
self.write_named(name, elem)?;
}
Ok(())
}
}