libreda-oasis 0.0.3

OASIS input/output for libreda-db.
Documentation
/*
 * Copyright (c) 2018-2021 Thomas Kramer.
 *
 * This file is part of LibrEDA 
 * (see https://codeberg.org/libreda/libreda-oasis).
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

//! Library for reading and writing OASIS files.
//!
//! OASIS is a binary format for storing two-dimensional geometrical data as it is commonly used
//! for silicon chip layouts. Its purpose is very similar to the older GDS2 format.
//!
//! # Examples
//!
//! ## Read a layout from OASIS
//!
//! ```
//! use std::fs::File;
//! use libreda_oasis::OASISStreamReader;
//! // Import the `LayoutStreamReader` trait.
//! use libreda_db::prelude::*;
//!
//! let filename = "./tests/data/INVX1_no_compression.oas";
//! // Open the OASIS file for reading.
//! let mut f = File::open(filename).unwrap();
//!
//! // Create an empty layout that will be populated by the OASIS reader.
//! let mut layout = Chip::new();
//!
//! // Create a default OASIS reader and parse the data from the file.
//! let result = OASISStreamReader::default()
//!     .read_layout(&mut f, &mut layout);
//!
//! // Assert that there was no error.
//! assert!(result.is_ok());
//! ```
//!
//! ## Write a layout to OASIS
//!
//! ```
//! use std::fs::File;
//! use libreda_oasis::OASISStreamWriter;
//! // Import the `LayoutStreamReader` trait.
//! use libreda_db::prelude::*;
//!
//! // Create an empty layout.
//! let layout = Chip::new();
//!
//! let mut f = File::create("./tests/data/empty_layout_out.oas").unwrap();
//!
//! let writer = OASISStreamWriter::default();
//!
//! // Write the (empty) layout to the file.
//! let write_result = writer.write_layout(&mut f, &layout);
//!
//! // Assert that there was no error.
//! assert!(write_result.is_ok());
//! ```

#![deny(missing_docs)]

// As long as there are some unimplemented parts be less strict with warnings.
// TODO: Remove this.
#![allow(dead_code, unused_assignments)]

#[macro_use]
extern crate log;

pub use libreda_db;

use libreda_db as db;
use db::layout::prelude::*;

mod base_types;
mod reader;
mod writer;

use base_types::*;
use crate::base_types::Repetition;
use std::io::{Read, Write};

pub use writer::OASISWriterConfig;
pub use libreda_db::prelude::{LayoutBase, LayoutEdit, LayoutStreamReader, LayoutStreamWriter};

/// Reader for the OASIS file format.
#[derive(Clone, Default, Debug)]
pub struct OASISStreamReader {}

impl OASISStreamReader {
    /// Create a new OASIS reader.
    pub fn new() -> Self {
        OASISStreamReader {}
    }
}

/// Writer for the OASIS file format.
#[derive(Clone, Default, Debug)]
pub struct OASISStreamWriter {
    config: OASISWriterConfig
}

impl OASISStreamWriter {
    /// Create a new OASIS writer with a custom configuration.
    pub fn with_config(config: OASISWriterConfig) -> Self{
        Self {config}
    }
}

/// Reader implementation.
impl LayoutStreamReader for OASISStreamReader {
    type Error = OASISReadError;
    fn read_layout<R: Read, L: LayoutEdit<Coord=SInt>>(&self, r: &mut R, layout: &mut L) -> Result<(), Self::Error> {
        reader::read_layout(r, layout)
    }
}

/// Writer implementation.
impl LayoutStreamWriter for OASISStreamWriter {
    type Error = OASISWriteError;
    fn write_layout<W: Write, L: LayoutBase<Coord=SInt>>(&self, w: &mut W, layout: &L) -> Result<(), Self::Error> {
        writer::write_layout(w, layout, &self.config)
    }
}

/// Type for the `xy-mode` modal variable.
#[derive(Copy, Clone, Debug)]
enum XYMode {
    Absolute,
    Relative,
}

impl Default for XYMode {
    fn default() -> Self {
        XYMode::Absolute
    }
}

/// Datatype for either a name string or a reference number to a name string.
/// In OASIS names can be given explicitly with a string or by reference number.
/// In case of a reference number, the name string might be specified later in the file.
#[derive(Debug, Clone)]
pub(crate) enum NameOrRef {
    /// A known name string.
    Name(String),
    /// A reference number to a name string.
    NameRef(UInt),
}

/// Modal variables.
/// Modal variables are used for implicit and context dependent values.
/// At the beginning at the file or of a CELL record all modal variables are set to `None` except
/// `placement_x`, `placement_y`, `geometry_x`, `geometry_y`, `text_x`, and `text_y`.
#[derive(Debug)]
struct Modal<C> {
    /// Related records: PLACEMENT, TEXT, POLYGON, PATH,
    /// RECTANGLE, TRAPEZOID, CTRAPEZOID,
    /// CIRCLE, XGEOMETRY
    repetition: Option<Repetition>,

    /// Related records: PLACEMENT
    placement_x: SInt,
    /// Related records: PLACEMENT
    placement_y: SInt,
    /// Reference to an implicit cell to be used in the PLACEMENT record.
    /// Related records: PLACEMENT
    placement_cell: Option<C>,

    /// Related records: POLYGON, PATH, RECTANGLE, TRAPEZOID,
    /// CTRAPEZOID, CIRCLE, XGEOMETRY
    layer: Option<UInt>,
    /// Related records: POLYGON, PATH, RECTANGLE, TRAPEZOID,
    /// CTRAPEZOID, CIRCLE, XGEOMETRY
    datatype: Option<UInt>,

    /// Related records: TEXT
    textlayer: Option<UInt>,
    /// Related records: TEXT
    texttype: Option<UInt>,
    /// Related records: TEXT
    text_x: SInt,
    /// Related records: TEXT
    text_y: SInt,
    /// Related records: TEXT
    text_string: Option<String>,

    /// Related records: POLYGON, PATH, RECTANGLE, TRAPEZOID
    /// CTRAPEZOID, CIRCLE, XGEOMETRY
    geometry_x: SInt,
    /// Related records: POLYGON, PATH, RECTANGLE, TRAPEZOID
    /// CTRAPEZOID, CIRCLE, XGEOMETRY
    geometry_y: SInt,

    /// Related records: PLACEMENT, TEXT, POLYGON, PATH,
    /// RECTANGLE, TRAPEZOID, CTRAPEZOID,
    /// CIRCLE, XGEOMETRY,
    /// XYABSOLUTE, XYRELATIVE
    /// Controls how `x` and `y` values are treated in the related records.
    xy_mode: XYMode,

    /// Related records: RECTANGLE, TRAPEZOID, CTRAPEZOID
    geometry_w: Option<UInt>,
    /// Related records: RECTANGLE, TRAPEZOID, CTRAPEZOID
    geometry_h: Option<UInt>,

    /// Related records: POLYGON
    polygon_point_list: Option<PointList>,

    /// Related records: PATH
    path_halfwidth: Option<UInt>,
    /// Related records: PATH
    path_point_list: Option<PointList>,
    /// Related records: PATH
    path_start_extension: Option<SInt>,
    /// Related records: PATH
    path_end_extension: Option<SInt>,

    /// Related records: CTRAPEZOID
    ctrapezoid_type: Option<()>,

    /// Related records: CIRCLE
    circle_radius: Option<()>,

    /// Related records: PROPERTY
    last_property_name: Option<NameOrRef>,
    /// Related records: PROPERTY
    last_value_list: Option<Vec<PropertyValue>>,
}

impl<C> Default for Modal<C> {
    fn default() -> Self {
        Self {
            repetition: None,
            placement_x: Default::default(),
            placement_y: Default::default(),
            placement_cell: None,
            layer: None,
            datatype: None,
            textlayer: None,
            texttype: None,
            text_x: Default::default(),
            text_y: Default::default(),
            text_string: None,
            geometry_x: Default::default(),
            geometry_y: Default::default(),
            xy_mode: Default::default(),
            geometry_w: None,
            geometry_h: None,
            polygon_point_list: None,
            path_halfwidth: None,
            path_point_list: None,
            path_start_extension: None,
            path_end_extension: None,
            ctrapezoid_type: None,
            circle_radius: None,
            last_property_name: None,
            last_value_list: None
        }
    }
}

impl<C> Modal<C> {
    /// Set all modal variables to `None` except
    /// `placement_x`, `placement_y`, `geometry_x`, `geometry_y`, `text_x`, and `text_y` which are set to `0`.
    fn reset(&mut self) -> () {
        let reset = Self::default();
        *self = reset;
    }
}