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}