diff --git a/crates/specctra-core/src/read.rs b/crates/specctra-core/src/read.rs index 6121c43..2ce333d 100644 --- a/crates/specctra-core/src/read.rs +++ b/crates/specctra-core/src/read.rs @@ -138,7 +138,7 @@ impl ListTokenizer { (self.line, self.column) } - fn add_context(&self, error: ParseError) -> ParseErrorContext { + pub(crate) fn add_context(&self, error: ParseError) -> ParseErrorContext { ParseErrorContext { error, context: (self.line, self.column), diff --git a/crates/specctra-core/src/structure.rs b/crates/specctra-core/src/structure.rs index 6f21ae5..b01c1fa 100644 --- a/crates/specctra-core/src/structure.rs +++ b/crates/specctra-core/src/structure.rs @@ -8,9 +8,10 @@ use crate::error::{ParseError, ParseErrorContext}; use crate::math::PointWithRotation; use crate::ListToken; +use core::fmt; use geo_types::{LineString, Polygon as GeoPolygon}; use specctra_derive::{ReadDsn, WriteSes}; -use std::borrow::Cow; +use std::{borrow::Cow, io}; #[derive(ReadDsn, WriteSes, Debug, Clone, PartialEq)] pub struct Dummy {} @@ -106,7 +107,7 @@ pub struct Structure { } // custom impl to handle layers appearing late -impl ReadDsn for Structure { +impl ReadDsn for Structure { fn read_dsn(tokenizer: &mut ListTokenizer) -> Result { let mut value = Self { layers: tokenizer.read_named_array(&["layer"])?, @@ -296,12 +297,153 @@ impl Pin { } } -#[derive(ReadDsn, WriteSes, Debug, Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] +pub struct Keepouts(pub Vec); + +impl ReadDsn for Keepouts { + fn read_dsn(tokenizer: &mut ListTokenizer) -> Result { + let mut ret = Vec::new(); + while let Ok(input) = tokenizer.consume_token() { + let is_keepout = input.token.is_start_of(&[ + "keepout", + "place_keepout", + "via_keepout", + "wire_keepout", + "bend_keepout", + "elongate_keepout", + ]); + tokenizer.return_token(input); + if !is_keepout { + break; + } + ret.push(Keepout::read_dsn(tokenizer)?); + } + Ok(Self(ret)) + } +} + +impl WriteSes for Keepouts { + fn write_dsn(&self, writer: &mut ListWriter) -> Result<(), io::Error> { + for i in &self.0[..] { + i.write_dsn(writer)?; + } + Ok(()) + } +} + +#[derive(Clone, Debug, PartialEq)] +pub enum KeepoutKind { + /// `keepout` + Normal, + /// `place_keepout` + Place, + /// `via_keepout` + Via, + /// `wire_keepout` + Wire, + /// `bend_keepout` + Bend, + /// `elongate_keepout` + Elongate, +} + +impl core::str::FromStr for KeepoutKind { + type Err = (); + + fn from_str(s: &str) -> Result { + Ok(match s { + "keepout" => Self::Normal, + "place_keepout" => Self::Place, + "via_keepout" => Self::Via, + "wire_keepout" => Self::Wire, + "bend_keepout" => Self::Bend, + "elongate_keepout" => Self::Elongate, + _ => return Err(()), + }) + } +} + +impl AsRef for KeepoutKind { + fn as_ref(&self) -> &str { + match self { + Self::Normal => "keepout", + Self::Place => "place_keepout", + Self::Via => "via_keepout", + Self::Wire => "wire_keepout", + Self::Bend => "bend_keepout", + Self::Elongate => "elongate_keepout", + } + } +} + +impl fmt::Display for KeepoutKind { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_ref()) + } +} + +#[derive(Clone, Debug, PartialEq)] pub struct Keepout { - #[anon] - pub idk: String, - #[anon] + pub kind: KeepoutKind, + pub id_: String, + pub sequence_number: Option, pub shape: Shape, + pub rule: Option, + // TODO: `place_rule`: `(place_rule (spacing ....))` +} + +impl ReadDsn for Keepout { + fn read_dsn(tokenizer: &mut ListTokenizer) -> Result { + let input = tokenizer.consume_token()?; + let err = ParseError::ExpectedStartOfListOneOf(&[ + "keepout", + "place_keepout", + "via_keepout", + "wire_keepout", + "bend_keepout", + "elongate_keepout", + ]); + let kind = if let ListToken::Start { name: ref kind } = input.token { + kind.parse::().map_err(|()| { + tokenizer.return_token(input); + tokenizer.add_context(err) + })? + } else { + tokenizer.return_token(input); + return Err(tokenizer.add_context(err)); + }; + + let id_ = String::read_dsn(tokenizer)?; + let sequence_number = tokenizer.read_optional(&["sequence_number"])?; + let shape = Shape::read_dsn(tokenizer)?; + let rule = tokenizer.read_optional(&["rule"])?; + // TODO: handle `place_rule` + + tokenizer.consume_token()?.expect_end()?; + Ok(Self { + kind, + id_, + sequence_number, + shape, + rule, + }) + } +} + +impl WriteSes for Keepout { + fn write_dsn(&self, writer: &mut ListWriter) -> Result<(), io::Error> { + writer.write_token(ListToken::Start { + name: self.kind.as_ref().to_string(), + })?; + + self.id_.write_dsn(writer)?; + writer.write_optional("sequence_number", &self.sequence_number)?; + self.shape.write_dsn(writer)?; + writer.write_optional("rule", &self.rule)?; + + writer.write_token(ListToken::End) + } } #[derive(ReadDsn, WriteSes, Debug, Clone, PartialEq)] @@ -420,7 +562,7 @@ impl From for geo_types::Point { } // Custom impl for the case described above -impl ReadDsn for Vec { +impl ReadDsn for Vec { fn read_dsn(tokenizer: &mut ListTokenizer) -> Result { let mut array = Vec::new(); while let Some(x) = tokenizer.read_value::>()? { @@ -430,7 +572,7 @@ impl ReadDsn for Vec { } } -impl ReadDsn for Option { +impl ReadDsn for Option { fn read_dsn(tokenizer: &mut ListTokenizer) -> Result { let input = tokenizer.consume_token()?; Ok(if let ListToken::Leaf { value: ref x } = input.token { @@ -446,8 +588,8 @@ impl ReadDsn for Option { } } -impl WriteSes for Vec { - fn write_dsn(&self, writer: &mut ListWriter) -> Result<(), std::io::Error> { +impl WriteSes for Vec { + fn write_dsn(&self, writer: &mut ListWriter) -> Result<(), io::Error> { for elem in self { writer.write_value(&elem.x)?; writer.write_value(&elem.y)?; @@ -456,8 +598,8 @@ impl WriteSes for Vec { } } -impl WriteSes for Option { - fn write_dsn(&self, writer: &mut ListWriter) -> Result<(), std::io::Error> { +impl WriteSes for Option { + fn write_dsn(&self, writer: &mut ListWriter) -> Result<(), io::Error> { if let Some(value) = self { writer.write_value(&value.x)?; writer.write_value(&value.y)?;