Skip to main content

haystack_core/codecs/
mod.rs

1//! Haystack wire format codecs for serialization and deserialization.
2//!
3//! Provides the [`Codec`] trait and five built-in implementations:
4//!
5//! | MIME Type | Module | Description |
6//! |----------|--------|-------------|
7//! | `text/zinc` | [`zinc`] | Zinc — the default Haystack text format (fastest encode/decode) |
8//! | `text/trio` | [`trio`] | Trio — tag-oriented format for defining entities and defs |
9//! | `application/json` | [`json`] (v4) | Haystack JSON v4 — standard JSON encoding |
10//! | `application/json;v=3` | [`json`] (v3) | Haystack JSON v3 — legacy JSON encoding |
11//! | `text/csv` | [`csv`] | CSV — comma-separated values for spreadsheet interop |
12//!
13//! Use [`codec_for()`] to look up a codec by MIME type:
14//!
15//! ```rust
16//! use haystack_core::codecs::codec_for;
17//!
18//! let zinc = codec_for("text/zinc").unwrap();
19//! let grid = zinc.decode_grid("ver:\"3.0\"\nempty\n").unwrap();
20//! let encoded = zinc.encode_grid(&grid).unwrap();
21//! ```
22//!
23//! The [`shared`] submodule provides common encoding/decoding helper functions
24//! used by multiple codec implementations.
25
26pub mod csv;
27pub mod json;
28pub mod shared;
29pub mod trio;
30pub mod zinc;
31
32use crate::data::{HCol, HDict, HGrid};
33use crate::kinds::Kind;
34
35/// Errors that can occur during encoding or decoding.
36#[derive(Debug, thiserror::Error)]
37pub enum CodecError {
38    #[error("parse error at position {pos}: {message}")]
39    Parse { pos: usize, message: String },
40    #[error("encoding error: {0}")]
41    Encode(String),
42    #[error("unsupported kind for this codec")]
43    UnsupportedKind,
44}
45
46/// Trait for Haystack wire format codecs.
47pub trait Codec: Send + Sync {
48    /// The MIME type for this codec (e.g. `"text/zinc"`).
49    fn mime_type(&self) -> &str;
50
51    /// Encode an HGrid to a string.
52    fn encode_grid(&self, grid: &HGrid) -> Result<String, CodecError>;
53
54    /// Decode a string to an HGrid.
55    fn decode_grid(&self, input: &str) -> Result<HGrid, CodecError>;
56
57    /// Encode a single scalar Kind value to a string.
58    fn encode_scalar(&self, val: &Kind) -> Result<String, CodecError>;
59
60    /// Decode a string to a single scalar Kind value.
61    fn decode_scalar(&self, input: &str) -> Result<Kind, CodecError>;
62
63    /// Encode the grid header (version line + meta + column definitions).
64    ///
65    /// Default implementation returns the full encoded grid (no streaming benefit).
66    fn encode_grid_header(&self, grid: &HGrid) -> Result<Vec<u8>, CodecError> {
67        self.encode_grid(grid).map(|s| s.into_bytes())
68    }
69
70    /// Encode a single grid row given the column definitions.
71    ///
72    /// Default implementation returns an empty vec (header contained everything).
73    fn encode_grid_row(&self, _cols: &[HCol], _row: &HDict) -> Result<Vec<u8>, CodecError> {
74        Ok(Vec::new())
75    }
76}
77
78static ZINC: zinc::ZincCodec = zinc::ZincCodec;
79static TRIO: trio::TrioCodec = trio::TrioCodec;
80static JSON4: json::Json4Codec = json::Json4Codec;
81static JSON3: json::Json3Codec = json::Json3Codec;
82static CSV: csv::CsvCodec = csv::CsvCodec;
83
84/// Look up a codec by MIME type.
85///
86/// Returns a static codec reference for the given MIME type, or `None` if
87/// the MIME type is not supported.
88pub fn codec_for(mime_type: &str) -> Option<&'static dyn Codec> {
89    match mime_type {
90        "text/zinc" => Some(&ZINC),
91        "text/trio" => Some(&TRIO),
92        "application/json" => Some(&JSON4),
93        "application/json;v=3" => Some(&JSON3),
94        "text/csv" => Some(&CSV),
95        _ => None,
96    }
97}