Skip to main content

dune_raw/
lib.rs

1//! SPICE3 rawfile reader and writer.
2//!
3//! The format contract is `corpus/spice3f5/src/lib/fte/rawfile.c`
4//! (`raw_write`/`raw_read`, Wayne A. Christopher, 1986): a header of
5//! `Keyword: value` lines (`Title`, `Date`, `Plotname`, `Flags`,
6//! `No. Variables`, `No. Points`, optional `Dimensions`, `Command`,
7//! `Option`), a `Variables:` table, and a `Values:` (ASCII) or `Binary:`
8//! (native-endian `f64`) data section. One file may carry several plots
9//! appended back to back; a plot is complex as a whole if any of its
10//! vectors is complex.
11//!
12//! The writer reproduces the 3f5 output byte for byte (15-digit `%e`
13//! floats, point index starting each ASCII row, one value per line, a
14//! blank line after each point). The reader is deliberately flexible,
15//! like the C one: it only requires one `Variables:` and one data section
16//! per plot, in that order, and treats whitespace loosely. Known
17//! divergences between 3f5 and ngspice output are listed in
18//! `docs/dune-raw.md`.
19//!
20//! Malformed input produces a structured [`Error`] with a line number —
21//! never a panic.
22
23#![forbid(unsafe_code)]
24
25mod error;
26mod plot;
27mod read;
28mod types;
29mod write;
30
31pub use error::Error;
32pub use plot::{Data, Plot, Variable};
33pub use types::VarType;
34pub use write::{Format, fmt_e};
35
36/// A parsed rawfile: one or more plots, in file order.
37#[derive(Clone, Debug, PartialEq)]
38pub struct Rawfile {
39    pub plots: Vec<Plot>,
40    /// Non-fatal reader diagnostics (unknown flags, bad attributes…),
41    /// mirroring the warnings the 3f5 reader prints to stderr.
42    pub warnings: Vec<String>,
43}
44
45impl Rawfile {
46    /// Parse a rawfile from bytes (ASCII or binary data sections).
47    pub fn parse(bytes: &[u8]) -> Result<Self, Error> {
48        read::parse(bytes)
49    }
50
51    /// Read and parse a rawfile from disk.
52    pub fn read_from(path: impl AsRef<std::path::Path>) -> Result<Self, Error> {
53        Self::parse(&std::fs::read(path)?)
54    }
55
56    /// Write every plot, back to back, as the 3f5 `raw_write` would.
57    pub fn write(&self, w: &mut impl std::io::Write, format: Format) -> std::io::Result<()> {
58        for plot in &self.plots {
59            plot.write(w, format)?;
60        }
61        Ok(())
62    }
63
64    /// Serialize to an in-memory buffer.
65    pub fn to_bytes(&self, format: Format) -> Vec<u8> {
66        let mut buf = Vec::new();
67        self.write(&mut buf, format)
68            .expect("writing to a Vec cannot fail");
69        buf
70    }
71}