gds21/
lib.rs

1//!
2//! # Gds21 Integrated Circuit Layout Parser & Writer
3//!
4//! GDSII is the IC industry's de facto standard for storing and sharing layout data.
5//! Gds21 is a library for reading and creating GDSII data, similar to and largely inspired by libraries such as [gdstk](https://github.com/heitzmann/gdstk) and its predecessor [gdspy](https://github.com/heitzmann/gdspy).
6//! Gds21 differs in being designed primarily as an interface layer to GDSII for the larger [Layout21](https://github.com/dan-fritchman/Layout21) library.
7//! Reading and generating GDSII-format data are primary goals;
8//! offering ease-of-use functionality for more elaborate manipulations of GDS data is not.
9//! (Although these manipulations can be performed on Gds21's data structures).
10//! Gds21 accordingly stores layout data on GDSII's terms, using GDSII's idioms, naming conventions, and datatypes.
11//!
12//! Layout data is represented in three primary forms:
13//!
14//! * A short tree with three layers:
15//!   * The root is a [`GdsLibrary`], which primarily consists of a set of cells ([`GdsStruct`]s), and secondarily a set of metadata.
16//!     Each [`GdsLibrary`] is a universe unto itself, in that it has no mechanisms for comprehending layout cells or data defined outside itself.
17//!     On-disk each [`GdsLibrary`] is typically paired one-to-one with a `.gds` file.
18//!   * Libraries consist of cell definitions AKA [`GdsStruct`]s, which define each layout cell (or module, or "struct" in GDSII terms).
19//!   * Cells consist of [`GdsElement`]s, an enumeration which includes individual polygons ([`GdsBoundary`]),
20//!     instances of other layout cells ([`GdsStructRef`]), text ([`GdsTextElem`]), and a few other geometric elements.
21//! * For storage on disk, the [`GdsLibrary`] tree is flattened to a series of [`GdsRecord`]s.
22//!   These records indicate the beginning, end, and content of each tree-node.
23//!   Detailed descriptions of these records comprise the majority of the GDSII spec.
24//! * Records are stored on-disk in binary form as detailed in the GDSII spec.
25//!   Each includes a record-type header, datatype, length field, and optional additional content.
26//!   These raw-bytes are never stored by Gds21, only generated and consumed on their way into and out of [`Read`] and [`Write`] objects (typically [`File`]s).
27//!
28//!
29//! ## Usage
30//!
31//! Loading a [`GdsLibrary`] from disk:
32//!
33//! ```skip
34//! let lib = GdsLibrary::load("sample.gds")?;
35//! ```
36//!
37//! Creating a new and empty [`GdsLibrary`], and adding a [`GdsStruct`] cell-definition:
38//!
39//! ```
40//! use gds21::{GdsLibrary, GdsStruct};
41//! let mut lib = GdsLibrary::new("mylib");
42//! lib.structs.push(GdsStruct::new("mycell"));
43//! ```
44//!
45//! Saving a [`GdsLibrary`] to disk:
46//!
47//! ```skip
48//! lib.save("mylib.gds");
49//! ```
50//!
51//! ## Serialization
52//!
53//! Each element in Gds21's [`GdsLibrary`] tree is [`serde`]-serializable.
54//! GDSII data can be straightforwardly serialized in any serde-supported format.
55//! Examples:
56//!
57//! ```text
58//! let lib = gds21::GdsLibrary::new("mylib");
59//! let json = serde_json::to_string(&lib);
60//! let yaml = serde_yaml::to_string(&lib);
61//! let toml = toml::to_string(&lib);
62//! ```
63//!
64//! Gds21 includes built-in support for a subset of serde-formats via its [`SerializationFormat`] enumeration,
65//! and support for directly reading and writing files in each format via its accompanying [`SerdeFile`] trait.
66//! Example using [`SerializationFormat::Yaml`]:
67//!
68//! ```skip
69//! use gds21::SerializationFormat::Yaml;
70//! let lib = gds21::GdsLibrary::new("mylib");
71//!
72//! // Write to YAML-format file
73//! Yaml.save(&lib, "mylib.gds.yaml")?;
74//! // And read back from file
75//! let lib2: gds21::GdsLibrary = Yaml.open("mylib.gds.yaml")?;  
76//! ```
77//!
78//! Note these text-based representations will generally be substantially larger than binary GDSII data.
79//!
80
81// Std-Lib Imports
82use std::convert::{TryFrom, TryInto};
83use std::error::Error;
84use std::fs::File;
85#[allow(unused_imports)]
86use std::io::prelude::*;
87use std::io::{BufWriter, Cursor, Read, Seek, SeekFrom, Write};
88use std::path::Path;
89use std::{fmt, mem, str};
90
91// Crates.io
92use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
93use chrono::prelude::*;
94use chrono::{Datelike, NaiveDate, NaiveDateTime};
95use derive_more::{self, Add, AddAssign, Sub, SubAssign};
96use num_derive::FromPrimitive;
97use num_traits::FromPrimitive;
98use serde::{Deserialize, Serialize};
99#[macro_use]
100extern crate derive_builder;
101
102// Local Imports
103use layout21utils as utils;
104#[doc(inline)]
105pub use utils::{SerdeFile, SerializationFormat};
106
107// Internal Modules
108#[doc(hidden)]
109mod read;
110use read::{GdsParser, GdsScanner, GdsStructScan};
111#[doc(hidden)]
112mod write;
113use write::GdsWriter;
114#[cfg(test)]
115mod tests;
116
117///
118/// # Gds Record Types
119///
120/// In the numeric-order specified by GDSII, for automatic [FromPrimitive] conversions.
121///
122#[derive(FromPrimitive, Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
123pub enum GdsRecordType {
124    Header = 0x00,
125    BgnLib,
126    LibName,
127    Units,
128    EndLib,
129    BgnStruct,
130    StructName, // STRNAME
131    EndStruct,
132    Boundary,
133    Path,
134    StructRef,
135    ArrayRef,
136    Text,
137    Layer,
138    DataType,
139    Width,
140    Xy,
141    EndElement,
142    StructRefName, // SNAME
143    ColRow,
144    TextNode, // "Not currently used"
145    Node,
146    TextType,
147    Presentation,
148    Spacing, // "Discontinued"
149    String,
150    Strans,
151    Mag,
152    Angle,
153    Uinteger, // "No longer used"
154    Ustring,  // "No longer used"
155    RefLibs,
156    Fonts,
157    PathType,
158    Generations,
159    AttrTable,
160    StypTable, // "Unreleased Feature"
161    StrType,   // "Unreleased Feature"
162    ElemFlags,
163    ElemKey,  // "Unreleased Feature"
164    LinkType, // "Unreleased Feature"
165    LinkKeys, // "Unreleased Feature"
166    Nodetype,
167    PropAttr,
168    PropValue,
169    Box,
170    BoxType,
171    Plex,
172    BeginExtn, // "Only occurs in CustomPlus"
173    EndExtn,   // "Only occurs in CustomPlus"
174    TapeNum,
175    TapeCode,
176    StrClass, // "Only for Calma internal use"
177    Reserved, // "Reserved for future use"
178    Format,
179    Mask,
180    EndMasks,
181    LibDirSize,
182    SrfName,
183    LibSecur,
184}
185impl GdsRecordType {
186    /// Boolean indication of valid record types
187    /// Many are either deprecated or provisioned without ever being implemented;
188    /// all from this list are deemed invalid.
189    pub fn valid(&self) -> bool {
190        match self {
191            Self::TextNode | // "Not currently used"
192            Self::Spacing | // "Discontinued"
193            Self::Uinteger | // "No longer used"
194            Self::Ustring |  // "No longer used"
195            Self::StypTable | // "Unreleased Feature"
196            Self::StrType |   // "Unreleased Feature"
197            Self::ElemKey |   // "Unreleased Feature"
198            Self::LinkType |  // "Unreleased Feature"
199            Self::LinkKeys |  // "Unreleased Feature"
200            Self::StrClass | // "Only for Calma internal use"
201            Self::Reserved   // "Reserved for future use"
202              => false,
203            _ => true,
204        }
205    }
206}
207
208/// # Gds DataType Enumeration
209/// In order as decoded from 16-bit integers in binary data
210#[derive(FromPrimitive, Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
211pub enum GdsDataType {
212    NoData = 0,
213    BitArray = 1,
214    I16 = 2,
215    I32 = 3,
216    F32 = 4,
217    F64 = 5,
218    Str = 6,
219}
220
221/// # Gds Record Header
222/// Decoded contents of a record's four header bytes,
223/// including its record-type, data-type, and length in bytes.
224#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
225pub struct GdsRecordHeader {
226    rtype: GdsRecordType,
227    dtype: GdsDataType,
228    len: u16,
229}
230
231///
232/// # Gds Record Enumeration
233///
234/// Keeps each record in relatively "raw" form,
235/// other than assuring correct data-types,
236/// and converting one-entry arrays into scalars.
237/// Invalid record-types are not included.
238///
239#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
240pub enum GdsRecord {
241    Header { version: i16 },
242    BgnLib { dates: Vec<i16> }, // Note: always length 12
243    LibName(String),
244    Units(f64, f64),
245    EndLib,
246    BgnStruct { dates: Vec<i16> }, // Note: always length 12
247    StructName(String),            // STRNAME Record
248    StructRefName(String),         // SNAME Record
249    EndStruct,
250    Boundary,
251    Path,
252    StructRef,
253    ArrayRef,
254    Text,
255    Layer(i16),
256    DataType(i16),
257    Width(i32),
258    Xy(Vec<i32>),
259    EndElement,
260    ColRow { cols: i16, rows: i16 },
261    Node,
262    TextType(i16),
263    Presentation(u8, u8),
264    String(String),
265    Strans(u8, u8),
266    Mag(f64),
267    Angle(f64),
268    RefLibs(String),
269    Fonts(String),
270    PathType(i16),
271    Generations(i16),
272    AttrTable(String),
273    ElemFlags(u8, u8),
274    Nodetype(i16),
275    PropAttr(i16),
276    PropValue(String),
277    Box,
278    BoxType(i16),
279    Plex(i32),
280    BeginExtn(i32),
281    EndExtn(i32),
282    TapeNum(i16),
283    TapeCode(Vec<i16>), // Note: always length 6
284    Format(i16),
285    Mask(String),
286    EndMasks,
287    LibDirSize(i16),
288    SrfName(String),
289    LibSecur(i16),
290}
291
292/// # GDSII's Home-Grown Floating-Point Format  
293///
294/// Incredibly, GDSII is old enough to have its own float-format,
295/// like most computers did before IEEE754.
296///
297/// The [GdsFloat64] struct is not used as a data-store, but largely a namespace
298/// for the `encode` and `decode` operations to and from IEEE754 double-precision format.
299///
300pub struct GdsFloat64;
301impl GdsFloat64 {
302    /// Decode GDSII's eight-byte representation, stored as a `u64`, to IEEE (and Rust)-compatible `f64`
303    pub fn decode(val: u64) -> f64 {
304        // Extract the MSB Sign bit
305        let neg = (val & 0x8000_0000_0000_0000) != 0;
306        // Extract the 7b exponent
307        let exp: i32 = ((val & 0x7F00_0000_0000_0000) >> 8 * 7) as i32 - 64;
308        // Create the initially integer-valued mantissa from the 7 least-significant bytes
309        let mantissa: u64 = val & 0x00FF_FFFF_FFFF_FFFF;
310        // And apply its normalization to the range (1/16, 1)
311        let mantissa: f64 = mantissa as f64 / 2f64.powi(8 * 7);
312        // Combine everything into our overall value
313        if neg {
314            -1.0 * mantissa * 16f64.powi(exp)
315        } else {
316            mantissa * 16f64.powi(exp)
317        }
318    }
319    /// Encode `f64` to GDSII's eight bytes, stored as `u64`.
320    pub fn encode(mut val: f64) -> u64 {
321        if val == 0.0 {
322            return 0;
323        };
324        let mut top: u8 = 0;
325        if val < 0.0 {
326            top = 0x80;
327            val = -val;
328        }
329        let fexp: f64 = 0.25 * val.log2();
330        let mut exponent = fexp.ceil() as i32;
331        if fexp == fexp.ceil() {
332            exponent += 1;
333        }
334        let mantissa: u64 = (val * 16_f64.powi(14 - exponent)).round() as u64;
335        top += (64 + exponent) as u8;
336        let result: u64 = (top as u64).wrapping_shl(56) | (mantissa & 0x00FF_FFFF_FFFF_FFFF);
337        result
338    }
339}
340
341/// # Unsupported (But Spec-Valid) Features
342#[derive(Default, Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
343pub struct Unsupported;
344
345/// # Gds Translation Settings
346/// Reflection, rotation, and magnification for text-elements and references.
347/// As configured by `STRANS` records.
348#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
349pub struct GdsStrans {
350    // Required Fields
351    /// Reflection, about the x-axis.
352    /// Applied before rotation.
353    pub reflected: bool,
354    /// Absolute Magnification Setting
355    pub abs_mag: bool,
356    /// Absolute Angle Setting
357    pub abs_angle: bool,
358
359    // Optional Fields
360    /// Magnification Factor. Defaults to 1.0 if not specified.
361    #[serde(default, skip_serializing_if = "Option::is_none")]
362    pub mag: Option<f64>,
363    /// Angle, in degrees counter-clockwise. Defaults to zero if not specified.
364    #[serde(default, skip_serializing_if = "Option::is_none")]
365    pub angle: Option<f64>,
366}
367
368/// # Gds Text-Presentation Flags
369/// Sets fonts, text justification, and the like.
370/// Stored in raw `u8` form.
371#[derive(Default, Debug, Clone, Deserialize, Serialize, PartialEq)]
372pub struct GdsPresentation(u8, u8);
373
374/// # Gds Element Flags
375/// As configured by `ELFLAGS` records.
376/// Two bytes of bit-fields stored in raw `u8` form.
377#[derive(Default, Debug, Clone, Deserialize, Serialize, PartialEq)]
378pub struct GdsElemFlags(u8, u8);
379
380/// # Gds Plex
381/// From the spec:
382/// "A unique positive number which is common to all elements of the Plex to which this element belongs."
383/// In Gds21's experience, `PLEX` records and settings are highly uncommon.
384#[derive(Default, Debug, Clone, Deserialize, Serialize, PartialEq)]
385pub struct GdsPlex(i32);
386
387/// # Gds Library Units
388///
389/// Each GDSII Library has two length-units, referred to as "DB Units" and "User Units" respectively.
390/// Essentially all spatial data throughout the Library is denoted in "DB Units".
391/// "User units" are a sort of recommendation for GUI programs to use when displaying the Library.  
392///
393/// From the spec's `UNITS` record-description:  
394/// ```text
395/// Contains two eight-byte real numbers.
396/// The first number is the size of a database-unit, in user-units.
397/// The second is the size of a database-unit in meters.
398/// To calculate the size of a user-unit in meters, divide the second number by the first.
399/// ```
400///
401/// These two numbers are stored as-is in the [GdsUnits] tuple-struct.
402///
403#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
404pub struct GdsUnits(f64, f64);
405impl GdsUnits {
406    /// Create a new [GdsUnits]
407    pub fn new(num1: f64, num2: f64) -> Self {
408        Self(num1, num2)
409    }
410    /// Get the database-unit size, in meters. Used for all spatial data.
411    pub fn db_unit(&self) -> f64 {
412        self.1
413    }
414    /// Get the user-unit size, in meters. Largely for display/ debug.
415    pub fn user_unit(&self) -> f64 {
416        self.0 / self.1
417    }
418}
419impl Default for GdsUnits {
420    /// Default values for GDS Units:
421    /// * DB-Unit = 1nm
422    /// * User-Unit = 1µm (1000x the DB-Unit)
423    fn default() -> Self {
424        Self(1e-3, 1e-9)
425    }
426}
427
428/// # Gds Spatial Point
429/// Coordinate in (x,y) layout-space.
430/// Denoted in each [GdsLibrary]'s [GdsUnits].
431#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, Eq)]
432pub struct GdsPoint {
433    pub x: i32,
434    pub y: i32,
435}
436impl GdsPoint {
437    /// Create a new [GdsPoint]
438    pub fn new(x: i32, y: i32) -> Self {
439        GdsPoint { x, y }
440    }
441    /// Create a vector of [GdsPoint] from an array of tuples
442    pub fn vec(pts: &[(i32, i32)]) -> Vec<Self> {
443        pts.iter().map(|pt| Self::new(pt.0, pt.1)).collect()
444    }
445    /// Convert from a two-element vector
446    fn parse(from: &Vec<i32>) -> GdsResult<Self> {
447        if from.len() != 2 {
448            return Err(GdsError::Str(
449                "GdsPoint coordinate vector: Invalid number of elements".into(),
450            ));
451        }
452        Ok(GdsPoint {
453            x: from[0],
454            y: from[1],
455        })
456    }
457    /// Convert an n-element vector if `i32` into an n/2-element vector of [GdsPoint]s.
458    fn parse_vec(from: &[i32]) -> GdsResult<Vec<GdsPoint>> {
459        if from.len() % 2 != 0 {
460            return Err(GdsError::Str(
461                "GdsPoint coordinate vector: Invalid number of elements".into(),
462            ));
463        }
464        let mut rv = Vec::with_capacity(from.len() / 2);
465        for i in 0..from.len() / 2 {
466            rv.push(GdsPoint {
467                x: from[i * 2],
468                y: from[i * 2 + 1],
469            });
470        }
471        Ok(rv)
472    }
473    /// Flatten to a two-element vector
474    fn flatten(&self) -> Vec<i32> {
475        vec![self.x, self.y]
476    }
477    /// Convert an n-element vector of [GdsPoint]s to a 2n-element i32 vector.
478    fn flatten_vec(src: &Vec<GdsPoint>) -> Vec<i32> {
479        let mut rv = Vec::with_capacity(src.len() * 2);
480        for pt in src.iter() {
481            rv.push(pt.x);
482            rv.push(pt.y);
483        }
484        rv
485    }
486}
487/// # Gds Mask-Format Enumeration
488/// As set by the FORMAT record
489#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
490pub enum GdsFormatType {
491    /// Default, sole fully-supported case.
492    Archive,
493    /// Filtered-format includes a list of Mask records. Not supported.
494    Filtered(Vec<Unsupported>),
495}
496/// # Gds Property
497/// Spec BNF:
498/// ```text
499/// PROPATTR PROPVALUE
500/// ```
501#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
502pub struct GdsProperty {
503    /// Attribute Number
504    pub attr: i16,
505    /// Attribute Value
506    pub value: String,
507}
508
509///
510/// # Gds Path Element
511///
512/// Spec BNF:
513/// ```text
514/// PATH [ELFLAGS] [PLEX] LAYER DATATYPE [PATHTYPE] [WIDTH] XY [BGNEXTN] [ENDEXTN])
515/// ```
516///
517#[derive(Default, Clone, Builder, Debug, Deserialize, Serialize, PartialEq)]
518#[builder(pattern = "owned", setter(into), private)]
519pub struct GdsPath {
520    // Required Fields
521    /// Layer Number
522    pub layer: i16,
523    /// DataType ID
524    pub datatype: i16,
525    /// Vector of x,y coordinates
526    pub xy: Vec<GdsPoint>,
527
528    // Optional Fields
529    #[serde(default, skip_serializing_if = "Option::is_none")]
530    #[builder(default, setter(strip_option))]
531    pub width: Option<i32>,
532    #[serde(default, skip_serializing_if = "Option::is_none")]
533    #[builder(default, setter(strip_option))]
534    pub path_type: Option<i16>,
535    #[serde(default, skip_serializing_if = "Option::is_none")]
536    #[builder(default, setter(strip_option))]
537    pub begin_extn: Option<i32>,
538    #[serde(default, skip_serializing_if = "Option::is_none")]
539    #[builder(default, setter(strip_option))]
540    pub end_extn: Option<i32>,
541    #[serde(default, skip_serializing_if = "Option::is_none")]
542    #[builder(default, setter(strip_option))]
543    pub elflags: Option<GdsElemFlags>,
544    #[serde(default, skip_serializing_if = "Option::is_none")]
545    #[builder(default, setter(strip_option))]
546    pub plex: Option<GdsPlex>,
547    #[serde(default, skip_serializing_if = "Vec::is_empty")]
548    #[builder(default, setter(strip_option))]
549    pub properties: Vec<GdsProperty>,
550}
551
552///
553/// # Gds Boundary Element
554///
555/// The most common type for closed-form shapes in GDSII.
556/// Most IC layout is comprised of [GdsBoundary] elements, which represent individual polygons.
557/// GDSII dictates that the first two and final two coordinates in each [GdsBoundary]
558/// shall be identical, "closing" the polygon.
559/// Hence an N-sided polygon is represented by an (N+1)-point `xy` vector.
560///
561/// Spec BNF:
562/// ```text
563/// BOUNDARY [ELFLAGS] [PLEX] LAYER DATATYPE XY
564/// ```
565///
566#[derive(Default, Clone, Builder, Debug, Deserialize, Serialize, PartialEq)]
567#[builder(pattern = "owned", setter(into), private)]
568pub struct GdsBoundary {
569    // Required Fields
570    /// Layer Number
571    pub layer: i16,
572    /// DataType ID
573    pub datatype: i16,
574    /// Vector of x,y coordinates
575    pub xy: Vec<GdsPoint>,
576
577    // Optional Fields
578    #[serde(default, skip_serializing_if = "Option::is_none")]
579    #[builder(default, setter(strip_option))]
580    pub elflags: Option<GdsElemFlags>,
581    #[serde(default, skip_serializing_if = "Option::is_none")]
582    #[builder(default, setter(strip_option))]
583    pub plex: Option<GdsPlex>,
584    #[serde(default, skip_serializing_if = "Vec::is_empty")]
585    #[builder(default, setter(strip_option))]
586    pub properties: Vec<GdsProperty>,
587}
588///
589/// # Gds Struct Reference (Cell Instance)
590///
591/// Represents an instance of a layout-cell.
592/// Coordinate vector `xy` is dictated by spec to have exactly one point (or two numbers),
593/// specifying the instance's lower-left coordinate.
594/// Options for rotation and reflection are configured in the [GdsStrans] attribute `strans`.
595///
596/// Spec BNF:
597/// ```text
598/// SREF [ELFLAGS] [PLEX] SNAME [<strans>] XY
599/// ```
600///
601#[derive(Default, Clone, Builder, Debug, Deserialize, Serialize, PartialEq)]
602#[builder(pattern = "owned", setter(into), private)]
603pub struct GdsStructRef {
604    // Required Fields
605    /// Struct (Cell) Name
606    pub name: String,
607    /// Location x,y coordinates
608    pub xy: GdsPoint,
609
610    // Optional Fields
611    #[serde(default, skip_serializing_if = "Option::is_none")]
612    #[builder(default, setter(strip_option))]
613    /// Translation & Reflection Options
614    pub strans: Option<GdsStrans>,
615    #[serde(default, skip_serializing_if = "Option::is_none")]
616    #[builder(default, setter(strip_option))]
617    pub elflags: Option<GdsElemFlags>,
618    #[serde(default, skip_serializing_if = "Option::is_none")]
619    #[builder(default, setter(strip_option))]
620    pub plex: Option<GdsPlex>,
621    #[serde(default, skip_serializing_if = "Vec::is_empty")]
622    #[builder(default, setter(strip_option))]
623    pub properties: Vec<GdsProperty>,
624}
625///
626/// # Gds Array Reference
627///
628/// A two-dimensional array of struct (cell) instances.
629///
630/// Spec BNF:
631/// ```text
632/// AREF [ELFLAGS] [PLEX] SNAME [<strans>] COLROW XY
633/// ```
634///
635#[derive(Default, Clone, Builder, Debug, Deserialize, Serialize, PartialEq)]
636#[builder(pattern = "owned", setter(into), private)]
637pub struct GdsArrayRef {
638    // Required Fields
639    /// Struct (Cell) Name
640    pub name: String,
641    /// Vector of x,y coordinates
642    pub xy: [GdsPoint; 3],
643    /// Number of columns
644    pub cols: i16,
645    /// Number of rows
646    pub rows: i16,
647    // Optional Fields
648    /// Translation & Reflection Options
649    #[serde(default, skip_serializing_if = "Option::is_none")]
650    #[builder(default)]
651    pub strans: Option<GdsStrans>,
652    #[serde(default, skip_serializing_if = "Option::is_none")]
653    #[builder(default, setter(strip_option))]
654    pub elflags: Option<GdsElemFlags>,
655    #[serde(default, skip_serializing_if = "Option::is_none")]
656    #[builder(default, setter(strip_option))]
657    pub plex: Option<GdsPlex>,
658    #[serde(default, skip_serializing_if = "Vec::is_empty")]
659    #[builder(default, setter(strip_option))]
660    pub properties: Vec<GdsProperty>,
661}
662///
663/// # Gds Text Element
664///
665/// Spec BNF:
666/// ```text
667/// TEXT [ELFLAGS] [PLEX] LAYER
668/// TEXTTYPE [PRESENTATION] [PATHTYPE] [WIDTH] [<strans>] XY STRING
669/// ```
670#[derive(Default, Clone, Builder, Debug, Deserialize, Serialize, PartialEq)]
671#[builder(pattern = "owned", setter(into), private)]
672pub struct GdsTextElem {
673    // Required Fields
674    /// Text Value
675    pub string: String,
676    /// Layer Number
677    pub layer: i16,
678    /// Text-Type ID
679    pub texttype: i16,
680    /// Vector of x,y coordinates
681    pub xy: GdsPoint,
682
683    // Optional Fields
684    #[serde(default, skip_serializing_if = "Option::is_none")]
685    #[builder(default, setter(strip_option))]
686    pub presentation: Option<GdsPresentation>,
687    #[serde(default, skip_serializing_if = "Option::is_none")]
688    #[builder(default, setter(strip_option))]
689    pub path_type: Option<i16>,
690    #[serde(default, skip_serializing_if = "Option::is_none")]
691    #[builder(default, setter(strip_option))]
692    pub width: Option<i32>,
693    #[serde(default, skip_serializing_if = "Option::is_none")]
694    #[builder(default)]
695    /// Translation & Reflection Options
696    pub strans: Option<GdsStrans>,
697    #[serde(default, skip_serializing_if = "Option::is_none")]
698    #[builder(default, setter(strip_option))]
699    pub elflags: Option<GdsElemFlags>,
700    #[serde(default, skip_serializing_if = "Option::is_none")]
701    #[builder(default, setter(strip_option))]
702    pub plex: Option<GdsPlex>,
703    #[serde(default, skip_serializing_if = "Vec::is_empty")]
704    #[builder(default, setter(strip_option))]
705    pub properties: Vec<GdsProperty>,
706}
707///
708/// # Gds Node Element
709///
710/// Spec BNF:
711/// ```text
712/// NODE [ELFLAGS] [PLEX] LAYER NODETYPE XY
713/// ```
714///
715#[derive(Default, Clone, Builder, Debug, Deserialize, Serialize, PartialEq)]
716#[builder(pattern = "owned", setter(into), private)]
717pub struct GdsNode {
718    // Required Fields
719    /// Layer Number
720    pub layer: i16,
721    /// Node-Type ID
722    pub nodetype: i16,
723    /// Vector of x,y coordinates
724    pub xy: Vec<GdsPoint>,
725
726    // Optional Fields
727    #[serde(default, skip_serializing_if = "Option::is_none")]
728    #[builder(default, setter(strip_option))]
729    pub elflags: Option<GdsElemFlags>,
730    #[serde(default, skip_serializing_if = "Option::is_none")]
731    #[builder(default, setter(strip_option))]
732    pub plex: Option<GdsPlex>,
733    #[serde(default, skip_serializing_if = "Vec::is_empty")]
734    #[builder(default, setter(strip_option))]
735    pub properties: Vec<GdsProperty>,
736}
737///
738/// # Gds Box Element
739///
740/// Spec BNF:
741/// ```text
742/// BOX [ELFLAGS] [PLEX] LAYER BOXTYPE XY
743/// ```
744///
745#[derive(Default, Clone, Builder, Debug, Deserialize, Serialize, PartialEq)]
746#[builder(pattern = "owned", setter(into), private)]
747pub struct GdsBox {
748    // Required Fields
749    /// Layer Number
750    pub layer: i16,
751    /// Box-Type ID
752    pub boxtype: i16,
753    /// Vector of x,y coordinates
754    pub xy: [GdsPoint; 5],
755
756    // Optional Fields
757    #[serde(default, skip_serializing_if = "Option::is_none")]
758    #[builder(default, setter(strip_option))]
759    pub elflags: Option<GdsElemFlags>,
760    #[serde(default, skip_serializing_if = "Option::is_none")]
761    #[builder(default, setter(strip_option))]
762    pub plex: Option<GdsPlex>,
763    #[serde(default, skip_serializing_if = "Vec::is_empty")]
764    #[builder(default, setter(strip_option))]
765    pub properties: Vec<GdsProperty>,
766}
767///
768/// # Gds Element Enumeration  
769///
770/// Primary union of geometric elements, instances, and arrays which comprise a GDSII struct (cell).
771///
772/// Spec BNF:
773/// ```text
774/// {<boundary> | <path> | <SREF> | <AREF> | <text> | <node> | <box>} {<property>}* ENDEL
775/// ```
776///
777/// Note the `properties` vectors are pushed down to each enum variant.
778///
779#[derive(derive_more::From, Debug, Clone, Deserialize, Serialize, PartialEq)]
780pub enum GdsElement {
781    GdsBoundary(GdsBoundary),
782    GdsPath(GdsPath),
783    GdsStructRef(GdsStructRef),
784    GdsArrayRef(GdsArrayRef),
785    GdsTextElem(GdsTextElem),
786    GdsNode(GdsNode),
787    GdsBox(GdsBox),
788}
789
790/// # Gds Summary Stats  
791///
792/// Summary statistics for a [GdsLibrary] or [GdsStruct].  
793/// Total numbers of elements of each type.
794#[derive(Debug, Default, Deserialize, Serialize, PartialEq, Add, AddAssign, Sub, SubAssign)]
795pub struct GdsStats {
796    libraries: usize,
797    structs: usize,
798    boundaries: usize,
799    paths: usize,
800    struct_refs: usize,
801    array_refs: usize,
802    text_elems: usize,
803    nodes: usize,
804    boxes: usize,
805}
806
807/// # Gds Modification Dates & Times
808#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
809pub struct GdsDateTimes {
810    /// Last Modification Date & Time
811    pub modified: NaiveDateTime,
812    /// Last Access Date & Time
813    pub accessed: NaiveDateTime,
814}
815
816impl Default for GdsDateTimes {
817    /// Default dates & times: what better time than now!
818    fn default() -> Self {
819        let now = Utc::now().naive_utc();
820        Self {
821            modified: now.clone(),
822            accessed: now.clone(),
823        }
824    }
825}
826///
827/// # Gds Struct (Cell) Definition
828///
829/// GDSII's primary hierarchical layout-definition object is its "struct",
830/// which most other layout systems would call a "cell" or "module".
831/// (Most GDSII software calls them one of these as well.)  
832///
833/// [GdsStruct]s are principally composed of an un-ordered, un-indexed vector
834/// of [GdsElement]s, which can be polygons ([GdsBoundary]),
835/// instances of other layouts ([GdsStructRef]),
836/// two-dimensional arrays thereof ([GdsArrayRef]),
837/// and a handful of other [GdsElement]s.  
838///
839/// Spec BNF:
840/// ```text
841/// BGNSTR STRNAME [STRCLASS] {<element>}* ENDSTR
842/// ```
843///
844#[derive(Default, Clone, Builder, Debug, Deserialize, Serialize, PartialEq)]
845#[builder(pattern = "owned", setter(into), private)]
846pub struct GdsStruct {
847    /// Struct Name
848    pub name: String,
849    /// Creation/ Modification-Date Info
850    pub dates: GdsDateTimes,
851    /// Elements List
852    pub elems: Vec<GdsElement>,
853}
854impl GdsStruct {
855    /// Create a new and empty [GdsStruct]
856    pub fn new(name: impl Into<String>) -> Self {
857        Self {
858            name: name.into(),
859            ..Default::default()
860        }
861    }
862    /// Count and return our element statistics
863    fn stats(&self) -> GdsStats {
864        let mut stats = GdsStats::default();
865        stats.structs += 1;
866        for elem in &self.elems {
867            use GdsElement::*;
868            match elem {
869                GdsBoundary(_) => stats.boundaries += 1,
870                GdsPath(_) => stats.paths += 1,
871                GdsStructRef(_) => stats.struct_refs += 1,
872                GdsArrayRef(_) => stats.array_refs += 1,
873                GdsTextElem(_) => stats.text_elems += 1,
874                GdsNode(_) => stats.nodes += 1,
875                GdsBox(_) => stats.boxes += 1,
876            };
877        }
878        stats
879    }
880}
881
882///
883/// # Gds Library
884///
885/// The Library is GDSII's primary idiom for a suite of layout-cells.
886/// A Library generally corresponds one-to-one with a `.gds` file.
887/// Libraries consist primarily of cell-definitions ([GdsStruct]s),
888/// and secondarily include library-level meta-data, including the distance units, GDS-spec version, and modification dates.
889///
890/// Several more esoteric library-level GDSII features are included as [GdsLibrary] fields,
891/// but are not materially supported. The empty [Unsupported] value generally denotes these fields.
892///
893/// Spec BNF:
894/// ```text
895/// HEADER BGNLIB [LIBDIRSIZE] [SRFNAME] [LIBSECUR] LIBNAME [REFLIBS] [FONTS] [ATTRTABLE] [GENERATIONS] [<FormatType>]
896/// UNITS {<structure>}* ENDLIB
897/// ```
898///
899#[derive(Default, Clone, Builder, Debug, Deserialize, Serialize, PartialEq)]
900#[builder(pattern = "owned", setter(into), private)]
901pub struct GdsLibrary {
902    // Required fields
903    /// Library Name
904    pub name: String,
905    /// Gds Spec Version
906    pub version: i16,
907    // Modification Date(s)
908    pub dates: GdsDateTimes,
909    /// Spatial Units    
910    pub units: GdsUnits,
911    /// Struct Definitions
912    pub structs: Vec<GdsStruct>,
913
914    // Unsupported Fields
915    #[serde(default, skip_serializing)]
916    #[builder(default)]
917    pub libdirsize: Unsupported,
918    #[serde(default, skip_serializing)]
919    #[builder(default)]
920    pub srfname: Unsupported,
921    #[serde(default, skip_serializing)]
922    #[builder(default)]
923    pub libsecur: Unsupported,
924    #[serde(default, skip_serializing)]
925    #[builder(default)]
926    pub reflibs: Unsupported,
927    #[serde(default, skip_serializing)]
928    #[builder(default)]
929    pub fonts: Unsupported,
930    #[serde(default, skip_serializing)]
931    #[builder(default)]
932    pub attrtable: Unsupported,
933    #[serde(default, skip_serializing)]
934    #[builder(default)]
935    pub generations: Unsupported,
936    #[serde(default, skip_serializing)]
937    #[builder(default)]
938    pub format_type: Unsupported,
939}
940impl GdsLibrary {
941    /// Create a new and empty [GdsLibrary]
942    pub fn new(name: impl Into<String>) -> Self {
943        Self {
944            name: name.into(),
945            version: 3,
946            ..Default::default()
947        }
948    }
949    /// Read a GDS loaded from file at path `fname`
950    pub fn load(fname: impl AsRef<Path>) -> GdsResult<GdsLibrary> {
951        // Create the parser, and parse a Library
952        GdsParser::open(fname)?.parse_lib()
953    }
954    /// Read a [GdsLibrary] from byte-vector `bytes`
955    pub fn from_bytes(bytes: Vec<u8>) -> GdsResult<GdsLibrary> {
956        // Create the parser, and parse a Library
957        GdsParser::from_bytes(bytes)?.parse_lib()
958    }
959    /// Run a first-pass scan of GDSII data in `fname`.
960    /// Returns a vector of [GdsStructScan]s including summary info per struct.
961    #[allow(dead_code)] // FIXME!
962    fn scan(fname: impl AsRef<Path>) -> GdsResult<Vec<GdsStructScan>> {
963        GdsScanner::scan(fname)
964    }
965    /// Collect and return the library's aggregate statistics
966    /// (numbers of structs, elements by type)
967    pub fn stats(&self) -> GdsStats {
968        let mut stats = GdsStats::default();
969        stats.libraries += 1;
970        for strukt in self.structs.iter() {
971            stats += strukt.stats();
972        }
973        stats
974    }
975    /// Save to file `fname`
976    pub fn save(&self, fname: impl AsRef<Path>) -> GdsResult<()> {
977        let mut wr = GdsWriter::open(fname)?;
978        wr.write_lib(self)
979    }
980    /// Write to file `file`
981    pub fn write(&self, file: impl Write) -> GdsResult<()> {
982        let mut wr = GdsWriter::new(file);
983        wr.write_lib(self)
984    }
985}
986// Enable [GdsLibrary] and [GdsStruct] serialization to file, in each of `utils` supported formats.
987impl SerdeFile for GdsLibrary {}
988impl SerdeFile for GdsStruct {}
989
990/// # Gds Layer Spec
991///
992/// Each GDSII element's layer is specified by a set of two numbers,
993/// commonly referred to as `layer` and `datatype`.
994/// Several element-types refer to their analog of `datatype` by different names,
995/// e.g. `texttype` and `nodetype`.  
996///
997/// `GdsLayerSpecs` generalize across these via the `xtype` field,
998/// which holds whichever is appropriate for the given element.
999pub struct GdsLayerSpec {
1000    /// Layer ID Number
1001    pub layer: i16,
1002    /// DataType (or TextType, NodeType, etc.) ID Number
1003    pub xtype: i16,
1004}
1005/// # Has-Layer Trait  
1006/// Sole function `layerspec` returns a [GdsLayerSpec] including the two numbers `layer` and `xtype`.
1007pub trait HasLayer {
1008    fn layerspec(&self) -> GdsLayerSpec;
1009}
1010impl GdsLayerSpec {
1011    /// Create a new [GdsLayerSpec] ]
1012    pub fn new(layer: i16, xtype: i16) -> GdsLayerSpec {
1013        GdsLayerSpec { layer, xtype }
1014    }
1015}
1016impl HasLayer for GdsBoundary {
1017    fn layerspec(&self) -> GdsLayerSpec {
1018        GdsLayerSpec::new(self.layer, self.datatype)
1019    }
1020}
1021impl HasLayer for GdsTextElem {
1022    fn layerspec(&self) -> GdsLayerSpec {
1023        GdsLayerSpec::new(self.layer, self.texttype)
1024    }
1025}
1026impl HasLayer for GdsNode {
1027    fn layerspec(&self) -> GdsLayerSpec {
1028        GdsLayerSpec::new(self.layer, self.nodetype)
1029    }
1030}
1031impl HasLayer for GdsBox {
1032    fn layerspec(&self) -> GdsLayerSpec {
1033        GdsLayerSpec::new(self.layer, self.boxtype)
1034    }
1035}
1036impl HasLayer for GdsPath {
1037    fn layerspec(&self) -> GdsLayerSpec {
1038        GdsLayerSpec::new(self.layer, self.datatype)
1039    }
1040}
1041
1042/// Enumeration of each context in which a record can be parsed, primarily for error reporting
1043#[derive(Debug, Clone)]
1044pub enum GdsContext {
1045    Library,
1046    Struct,
1047    StructRef,
1048    ArrayRef,
1049    Boundary,
1050    Box,
1051    Path,
1052    Text,
1053    Node,
1054    Property,
1055}
1056/// # GdsResult Type-Alias
1057pub type GdsResult<T> = Result<T, GdsError>;
1058/// # Gds Error Enumeration
1059/// Most errors are tied in some sense to parsing and decoding.
1060/// Once a valid [GdsLibrary] is created in memory, it can generally be streamed to bytes.
1061#[derive(Debug)]
1062pub enum GdsError {
1063    /// Invalid binary -> record conversion
1064    RecordDecode(GdsRecordType, GdsDataType, u16),
1065    /// Invalid record length
1066    RecordLen(usize),
1067    /// Invalid data type
1068    InvalidDataType(u8),
1069    /// Invalid record type
1070    InvalidRecordType(u8),
1071    /// Unsupported feature, in the decoded context
1072    Unsupported(Option<GdsRecord>, Option<GdsContext>),
1073    /// Parser Errors
1074    Parse {
1075        msg: String,
1076        record: GdsRecord,
1077        recordnum: usize,
1078        bytepos: u64,
1079        ctx: Vec<GdsContext>,
1080    },
1081    /// Boxed (External) Errors
1082    Boxed(Box<dyn Error>),
1083    /// Other errors
1084    Str(String),
1085}
1086impl std::fmt::Display for GdsError {
1087    /// Display a [GdsError].
1088    /// This functionally delegates to the (derived) [std::fmt::Debug] implementation.
1089    /// Maybe more info that wanted in some cases. But certainly enough.
1090    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1091        write!(f, "{:?}", self)
1092    }
1093}
1094impl std::error::Error for GdsError {}
1095impl From<std::io::Error> for GdsError {
1096    fn from(e: std::io::Error) -> Self {
1097        Self::Boxed(Box::new(e))
1098    }
1099}
1100impl From<std::str::Utf8Error> for GdsError {
1101    fn from(e: std::str::Utf8Error) -> Self {
1102        Self::Boxed(Box::new(e))
1103    }
1104}
1105impl From<String> for GdsError {
1106    fn from(e: String) -> Self {
1107        GdsError::Str(e)
1108    }
1109}
1110impl From<&str> for GdsError {
1111    fn from(e: &str) -> Self {
1112        GdsError::Str(e.to_string())
1113    }
1114}
1115#[cfg(any(test, feature = "selftest"))]
1116/// Check `lib` matches across a write-read round-trip cycle
1117pub fn roundtrip(lib: &GdsLibrary) -> GdsResult<()> {
1118    use tempfile::tempfile;
1119
1120    // Write to a temporary file
1121    let mut file = tempfile()?;
1122    lib.write(&mut file)?;
1123
1124    // Rewind to the file-start, and read it back
1125    file.seek(SeekFrom::Start(0))?;
1126    let mut bytes = Vec::new();
1127    file.read_to_end(&mut bytes)?;
1128    let lib2 = GdsLibrary::from_bytes(bytes)?;
1129
1130    // And check the two line up
1131    assert_eq!(*lib, lib2);
1132    Ok(())
1133}