Skip to main content

trueno_db/experiment/
artifact_record.rs

1//! Artifact Record - content-addressable storage for run outputs
2
3use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5
6/// Artifact Record represents a stored artifact from a run.
7///
8/// Artifacts are stored using content-addressable storage (CAS),
9/// where the `cas_hash` uniquely identifies the artifact content.
10///
11/// ## CAS Hash Format
12///
13/// The `cas_hash` follows the format: `algorithm:hex_digest`
14///
15/// Examples:
16/// - `sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855`
17/// - `blake3:af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262`
18#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
19pub struct ArtifactRecord {
20    run_id: String,
21    key: String,
22    cas_hash: String,
23    size_bytes: u64,
24    created_at: DateTime<Utc>,
25}
26
27impl ArtifactRecord {
28    /// Create a new artifact record.
29    ///
30    /// # Arguments
31    ///
32    /// * `run_id` - ID of the parent run
33    /// * `key` - Artifact name/key (e.g., "model.pt", "checkpoint.ckpt")
34    /// * `cas_hash` - Content-addressable hash (e.g., "sha256:abc123")
35    /// * `size_bytes` - Size of the artifact in bytes
36    ///
37    /// # Returns
38    ///
39    /// A new `ArtifactRecord` with the current timestamp.
40    #[must_use]
41    pub fn new(
42        run_id: impl Into<String>,
43        key: impl Into<String>,
44        cas_hash: impl Into<String>,
45        size_bytes: u64,
46    ) -> Self {
47        Self {
48            run_id: run_id.into(),
49            key: key.into(),
50            cas_hash: cas_hash.into(),
51            size_bytes,
52            created_at: Utc::now(),
53        }
54    }
55
56    /// Get the run ID.
57    #[must_use]
58    pub fn run_id(&self) -> &str {
59        &self.run_id
60    }
61
62    /// Get the artifact key/name.
63    #[must_use]
64    pub fn key(&self) -> &str {
65        &self.key
66    }
67
68    /// Get the content-addressable hash.
69    #[must_use]
70    pub fn cas_hash(&self) -> &str {
71        &self.cas_hash
72    }
73
74    /// Get the artifact size in bytes.
75    #[must_use]
76    pub const fn size_bytes(&self) -> u64 {
77        self.size_bytes
78    }
79
80    /// Get the creation timestamp.
81    #[must_use]
82    pub const fn created_at(&self) -> DateTime<Utc> {
83        self.created_at
84    }
85}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90
91    #[test]
92    fn test_artifact_record_new() {
93        let artifact = ArtifactRecord::new("run-1", "model.pt", "sha256:abc123", 1000);
94        assert_eq!(artifact.run_id(), "run-1");
95        assert_eq!(artifact.key(), "model.pt");
96        assert_eq!(artifact.cas_hash(), "sha256:abc123");
97        assert_eq!(artifact.size_bytes(), 1000);
98    }
99
100    #[test]
101    fn test_artifact_cas_hash_format() {
102        let artifact = ArtifactRecord::new("run-1", "data.bin", "sha256:e3b0c44298fc1c14", 0);
103        assert!(artifact.cas_hash().starts_with("sha256:"));
104    }
105}