atlas_cli/storage/
traits.rs

1use crate::error::Error;
2use crate::error::Result;
3use atlas_c2pa_lib::manifest::Manifest;
4use serde::{Deserialize, Serialize};
5use std::any::Any;
6use std::fmt;
7use std::path::PathBuf;
8
9/// Represents metadata about a stored manifest
10///
11/// # Examples
12///
13/// ```
14/// use atlas_cli::storage::traits::{ManifestMetadata, ManifestType};
15///
16/// let metadata = ManifestMetadata {
17///     id: "model-123".to_string(),
18///     name: "My Model".to_string(),
19///     manifest_type: ManifestType::Model,
20///     created_at: "2025-01-23T12:00:00Z".to_string(),
21/// };
22///
23/// assert_eq!(metadata.id, "model-123");
24/// assert_eq!(metadata.manifest_type, ManifestType::Model);
25/// ```
26#[derive(Clone, Serialize, Deserialize)]
27pub struct ManifestMetadata {
28    pub id: String,
29    pub name: String,
30    pub manifest_type: ManifestType,
31    pub created_at: String,
32}
33
34pub trait StorageBackend {
35    fn store_manifest(&self, manifest: &Manifest) -> Result<String>;
36    fn retrieve_manifest(&self, id: &str) -> Result<Manifest>;
37    fn list_manifests(&self) -> Result<Vec<ManifestMetadata>>;
38    fn delete_manifest(&self, id: &str) -> Result<()>;
39    fn as_any(&self) -> &dyn Any;
40}
41
42#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
43pub enum ManifestType {
44    Dataset,
45    Model,
46    Software,
47    Unknown,
48}
49
50/// Represents the location and verification info for an artifact
51///
52/// # Examples
53///
54/// ```no_run
55/// use atlas_cli::storage::traits::ArtifactLocation;
56/// use std::path::PathBuf;
57///
58/// // Create from a file path
59/// let path = PathBuf::from("model.onnx");
60/// let location = ArtifactLocation::new(path).unwrap();
61///
62/// // Verify the file hasn't changed
63/// assert!(location.verify().unwrap());
64/// ```
65///
66/// ```
67/// use atlas_cli::storage::traits::ArtifactLocation;
68/// use std::path::PathBuf;
69///
70/// // Create manually
71/// let location = ArtifactLocation {
72///     url: "file:///path/to/file".to_string(),
73///     file_path: Some(PathBuf::from("/path/to/file")),
74///     hash: "a".repeat(64),
75/// };
76///
77/// assert!(location.file_path.is_some());
78/// ```
79#[derive(Clone, Serialize, Deserialize)]
80pub struct ArtifactLocation {
81    pub url: String,
82    pub file_path: Option<PathBuf>,
83    pub hash: String,
84}
85
86impl fmt::Display for ManifestType {
87    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88        match self {
89            ManifestType::Dataset => write!(f, "Dataset"),
90            ManifestType::Model => write!(f, "Model"),
91            ManifestType::Software => write!(f, "Software"),
92            ManifestType::Unknown => write!(f, "Unknown"),
93        }
94    }
95}
96
97impl ArtifactLocation {
98    pub fn new(path: PathBuf) -> Result<Self> {
99        let hash = crate::hash::calculate_file_hash(&path)?;
100        let url = format!("file://{}", path.to_string_lossy());
101
102        Ok(Self {
103            url,
104            file_path: Some(path),
105            hash,
106        })
107    }
108
109    pub fn verify(&self) -> Result<bool> {
110        match &self.file_path {
111            Some(path) => {
112                let current_hash = crate::hash::calculate_file_hash(path)?;
113                Ok(current_hash == self.hash)
114            }
115            None => Err(Error::Validation(
116                "No file path available for verification".to_string(),
117            )),
118        }
119    }
120}