Skip to main content

kaish_types/
value.rs

1//! Value types for kaish's AST and runtime.
2
3use serde::{Deserialize, Deserializer, Serialize, Serializer};
4
5/// A literal value.
6///
7/// Supports primitives (null, bool, int, float, string), structured JSON data
8/// (arrays and objects), and binary blob references.
9#[derive(Debug, Clone, PartialEq)]
10pub enum Value {
11    Null,
12    Bool(bool),
13    Int(i64),
14    Float(f64),
15    String(String),
16    /// Structured JSON data (arrays, objects, nested structures).
17    /// Use `jq` to query/extract values.
18    Json(serde_json::Value),
19    /// Reference to binary data stored in the virtual filesystem.
20    Blob(BlobRef),
21}
22
23impl Serialize for Value {
24    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
25        // Delegate to value_to_json for consistent JSON representation.
26        // Float NaN → null, BlobRef → {_type: "blob", ...}, Json → inline.
27        crate::result::value_to_json(self).serialize(serializer)
28    }
29}
30
31impl<'de> Deserialize<'de> for Value {
32    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
33        let json = serde_json::Value::deserialize(deserializer)?;
34        Ok(crate::result::json_to_value(json))
35    }
36}
37
38/// Reference to binary data stored in `/v/blobs/{id}`.
39///
40/// Binary data flows through the blob storage system rather than being
41/// encoded as base64 in text fields.
42#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
43pub struct BlobRef {
44    /// Unique identifier, also the path suffix: `/v/blobs/{id}`
45    pub id: String,
46    /// Size of the blob in bytes.
47    pub size: u64,
48    /// MIME content type (e.g., "image/png", "application/octet-stream").
49    pub content_type: String,
50    /// Optional hash for integrity verification (SHA-256).
51    pub hash: Option<Vec<u8>>,
52}
53
54impl BlobRef {
55    /// Create a new blob reference.
56    pub fn new(id: impl Into<String>, size: u64, content_type: impl Into<String>) -> Self {
57        Self {
58            id: id.into(),
59            size,
60            content_type: content_type.into(),
61            hash: None,
62        }
63    }
64
65    /// Create a blob reference with a hash.
66    pub fn with_hash(mut self, hash: Vec<u8>) -> Self {
67        self.hash = Some(hash);
68        self
69    }
70
71    /// Get the VFS path for this blob.
72    pub fn path(&self) -> String {
73        format!("/v/blobs/{}", self.id)
74    }
75
76    /// Format size for display (e.g., "1.2MB", "456KB").
77    pub fn formatted_size(&self) -> String {
78        const KB: u64 = 1024;
79        const MB: u64 = 1024 * KB;
80        const GB: u64 = 1024 * MB;
81
82        if self.size >= GB {
83            format!("{:.1}GB", self.size as f64 / GB as f64)
84        } else if self.size >= MB {
85            format!("{:.1}MB", self.size as f64 / MB as f64)
86        } else if self.size >= KB {
87            format!("{:.1}KB", self.size as f64 / KB as f64)
88        } else {
89            format!("{}B", self.size)
90        }
91    }
92}