Skip to main content

konjure_sdk/data/
mod.rs

1//! Immutable typed data shared by the SDK, language, and host adapters.
2//!
3//! Numeric values retain their declared bit width. Floating-point operations
4//! use portable IEEE binary16, binary32, binary64, and binary128 software
5//! arithmetic with round-to-nearest, ties-to-even. Nonfinite values are rejected.
6//! Integer operations check overflow; conversions must preserve the exact value.
7//! Serialized scalar values are decimal strings, including small values, so a
8//! JavaScript JSON parser cannot silently round a wide integer or binary128.
9//!
10//! Tensors store packed little-endian elements with immutable shared ownership.
11//! Indexing and slicing create views; arithmetic creates independent outputs.
12//! Media values validate the layout of decoded payloads, without implying a codec,
13//! capture device, playback engine, or live stream implementation.
14//!
15//! ```
16//! use konjure_sdk::data::{AxisIndex, BinaryOp, DType, Scalar, Tensor};
17//! let values = ["1", "2", "3", "4"].into_iter()
18//!     .map(|text| Scalar::parse(DType::I16, text)).collect::<Result<Vec<_>, _>>()?;
19//! let matrix = Tensor::from_scalars(DType::I16, vec![2, 2], values)?;
20//! let last_row = matrix.slice(&[AxisIndex::Index(-1)])?;
21//! assert_eq!(last_row.sum()?.to_string(), "7");
22//! assert_eq!(matrix.binary(BinaryOp::Add, &last_row)?.get(&[0, 0])?.to_string(), "4");
23//! # Ok::<(), konjure_sdk::data::DataError>(())
24//! ```
25
26mod buffer;
27mod inspect;
28mod media;
29mod scalar;
30mod tensor;
31
32/// JSON wire shapes for custom-serialized data values.
33///
34/// These declarations share the exact field layout emitted by [`Scalar`] and
35/// [`Tensor`]. They are exported for generated host bindings; callers should
36/// continue to construct validated data through the value types themselves.
37pub mod wire {
38    use alloc::{string::String, vec::Vec};
39    use serde::{Deserialize, Serialize};
40
41    use super::DType;
42
43    #[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
44    #[cfg_attr(feature = "typescript", ts(rename = "ScalarWire"))]
45    #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
46    #[serde(deny_unknown_fields)]
47    /// Exact JSON representation of a width-specific scalar.
48    pub struct Scalar {
49        /// Declared numeric representation.
50        pub dtype: DType,
51        /// Exact canonical decimal spelling.
52        pub value: String,
53    }
54
55    #[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
56    #[cfg_attr(feature = "typescript", ts(rename = "TensorWire"))]
57    #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
58    #[serde(deny_unknown_fields)]
59    /// Exact JSON representation of tensor metadata and decimal element values.
60    pub struct Tensor {
61        /// Declared element representation.
62        pub dtype: DType,
63        /// Extent of each tensor axis.
64        pub shape: Vec<usize>,
65        /// Exact decimal element spellings in logical row-major order.
66        pub data: Vec<String>,
67    }
68}
69
70pub use buffer::{Bin, Str};
71pub use inspect::{
72    ApproximateTensorStatistics, ApproximationUnavailable, HISTOGRAM_BUCKETS, HistogramBucket,
73    MAX_INSPECTION_PREVIEW, MAX_INSPECTION_SAMPLES, TensorInspection, TensorInspectionOptions,
74    TensorStatistics, inspect_tensor,
75};
76pub use media::{Audio, Image, ImageFormat, Video, VideoFrame};
77pub use scalar::{BinaryOp, DType, Scalar};
78pub use tensor::{AxisIndex, MAX_ELEMENTS, MAX_RANK, Tensor};
79
80/// One data-error name and its source-of-truth documentation.
81#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
82#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
83#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
84#[serde(deny_unknown_fields)]
85pub struct DataErrorDescriptor {
86    /// Stable variant name used by generated host and language bindings.
87    pub name: &'static str,
88    /// Short description of the condition that produced the error.
89    pub docs: &'static str,
90}
91
92macro_rules! data_errors {
93    ($( $variant:ident, $docs:literal, $display:literal; )+) => {
94        /// Failure to construct, index, convert, or evaluate typed data.
95        #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
96        #[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
97        #[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
98        #[serde(rename_all = "snake_case")]
99        pub enum DataError {
100            $(#[doc = $docs] $variant,)+
101        }
102
103        impl DataError {
104            /// Every data error with its stable name and documentation.
105            pub const VARIANTS: &[DataErrorDescriptor] = &[
106                $(DataErrorDescriptor { name: stringify!($variant), docs: $docs },)+
107            ];
108
109            /// Stable variant name used by generated host and language bindings.
110            pub const fn name(self) -> &'static str {
111                match self { $(Self::$variant => stringify!($variant),)+ }
112            }
113        }
114
115        impl core::fmt::Display for DataError {
116            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
117                f.write_str(match self { $(Self::$variant => $display,)+ })
118            }
119        }
120    };
121}
122
123data_errors! {
124    TypeMismatch, "An operation requires operands with the same declared element type.", "numeric types must match";
125    InvalidLiteral, "Text is not a valid literal of the requested type.", "invalid numeric literal";
126    InvalidUtf8, "Bytes are not valid UTF-8 where a text value is required.", "invalid UTF-8";
127    Overflow, "A value or checked integer operation exceeds the destination range.", "numeric overflow";
128    NonFinite, "Infinity and NaN cannot enter a portable data value.", "nonfinite values are unsupported";
129    InexactConversion, "A conversion would round, truncate, or discard a signed zero.", "conversion would lose information";
130    DivisionByZero, "Division or remainder has a zero divisor.", "division by zero";
131    InvalidShape, "Shape, rank, or buffer length is not a valid tensor layout.", "invalid tensor shape or element count";
132    ShapeMismatch, "Shapes cannot participate in the requested operation.", "incompatible tensor shapes";
133    Bounds, "An index is outside the requested value.", "index out of bounds";
134    InvalidSlice, "Slice selectors are malformed, including a zero step.", "invalid slice";
135    InvalidMetadata, "Decoded media metadata disagrees with its payload.", "invalid media metadata or payload layout";
136    AllocationLimit, "A checked allocation exceeds the portable data limit.", "data exceeds allocation limit";
137}
138
139impl core::error::Error for DataError {}