hehe_core/resource/
mod.rs

1use bytes::Bytes;
2use camino::Utf8PathBuf;
3use serde::{Deserialize, Serialize};
4use url::Url;
5
6use crate::error::Result;
7
8#[derive(Clone, Debug, Serialize, Deserialize)]
9#[serde(tag = "type", rename_all = "snake_case")]
10pub enum ResourceRef {
11    #[serde(skip)]
12    Inline(Bytes),
13    Base64 { data: String },
14    File { path: Utf8PathBuf },
15    Url { url: Url },
16    ContentAddress { hash: String },
17}
18
19impl ResourceRef {
20    pub fn inline(data: impl Into<Bytes>) -> Self {
21        Self::Inline(data.into())
22    }
23
24    pub fn base64(data: impl Into<String>) -> Self {
25        Self::Base64 { data: data.into() }
26    }
27
28    pub fn file(path: impl Into<Utf8PathBuf>) -> Self {
29        Self::File { path: path.into() }
30    }
31
32    pub fn url(url: Url) -> Self {
33        Self::Url { url }
34    }
35
36    pub fn content_address(hash: impl Into<String>) -> Self {
37        Self::ContentAddress { hash: hash.into() }
38    }
39
40    pub fn is_inline(&self) -> bool {
41        matches!(self, Self::Inline(_) | Self::Base64 { .. })
42    }
43
44    pub fn is_remote(&self) -> bool {
45        matches!(self, Self::Url { .. })
46    }
47
48    pub fn is_local(&self) -> bool {
49        matches!(self, Self::File { .. })
50    }
51}
52
53#[derive(Clone, Debug, Default, Serialize, Deserialize)]
54pub struct ResourceMeta {
55    #[serde(skip_serializing_if = "Option::is_none")]
56    pub media_type: Option<String>,
57    #[serde(skip_serializing_if = "Option::is_none")]
58    pub size: Option<u64>,
59    #[serde(skip_serializing_if = "Option::is_none")]
60    pub filename: Option<String>,
61    #[serde(skip_serializing_if = "Option::is_none")]
62    pub checksum: Option<String>,
63    #[serde(skip_serializing_if = "Option::is_none")]
64    pub encoding: Option<String>,
65}
66
67impl ResourceMeta {
68    pub fn new() -> Self {
69        Self::default()
70    }
71
72    pub fn with_media_type(mut self, media_type: impl Into<String>) -> Self {
73        self.media_type = Some(media_type.into());
74        self
75    }
76
77    pub fn with_size(mut self, size: u64) -> Self {
78        self.size = Some(size);
79        self
80    }
81
82    pub fn with_filename(mut self, filename: impl Into<String>) -> Self {
83        self.filename = Some(filename.into());
84        self
85    }
86
87    pub fn with_checksum(mut self, checksum: impl Into<String>) -> Self {
88        self.checksum = Some(checksum.into());
89        self
90    }
91}
92
93#[derive(Clone, Debug)]
94pub struct Resource {
95    pub reference: ResourceRef,
96    pub meta: ResourceMeta,
97}
98
99impl Resource {
100    pub fn new(reference: ResourceRef) -> Self {
101        Self {
102            reference,
103            meta: ResourceMeta::default(),
104        }
105    }
106
107    pub fn with_meta(mut self, meta: ResourceMeta) -> Self {
108        self.meta = meta;
109        self
110    }
111
112    pub fn inline(data: impl Into<Bytes>) -> Self {
113        Self::new(ResourceRef::inline(data))
114    }
115
116    pub fn from_base64(data: impl Into<String>) -> Self {
117        Self::new(ResourceRef::base64(data))
118    }
119
120    pub fn from_file(path: impl Into<Utf8PathBuf>) -> Self {
121        Self::new(ResourceRef::file(path))
122    }
123
124    pub fn from_url(url: Url) -> Self {
125        Self::new(ResourceRef::url(url))
126    }
127}
128
129#[async_trait::async_trait]
130pub trait ResourceResolver: Send + Sync {
131    async fn resolve(&self, resource: &ResourceRef) -> Result<Bytes>;
132    async fn resolve_base64(&self, resource: &ResourceRef) -> Result<String>;
133    async fn metadata(&self, resource: &ResourceRef) -> Result<ResourceMeta>;
134}
135
136#[async_trait::async_trait]
137pub trait ResourceStore: Send + Sync {
138    async fn store(&self, data: Bytes, meta: ResourceMeta) -> Result<String>;
139    async fn get(&self, content_address: &str) -> Result<Option<Bytes>>;
140    async fn exists(&self, content_address: &str) -> Result<bool>;
141    async fn delete(&self, content_address: &str) -> Result<bool>;
142}