1use crate::{ArtifactKind, ArtifactReference, ArtifactReferenceError};
3use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
6#[serde(deny_unknown_fields)]
7pub struct MediaReference {
8 pub uri: ArtifactUri,
9}
10
11impl MediaReference {
12 pub fn reference(
13 &self,
14 kind: ArtifactKind,
15 ) -> Result<ArtifactReference, ArtifactReferenceError> {
16 let reference = ArtifactReference::parse(&self.uri)?;
17 if reference.metadata().kind() != kind {
18 return Err(super::invalid(
19 "media artifact kind does not match the field type",
20 ));
21 }
22 Ok(reference)
23 }
24
25 pub fn validate(
26 &self,
27 tenant: &str,
28 scope: &str,
29 kind: ArtifactKind,
30 ) -> Result<ArtifactReference, ArtifactReferenceError> {
31 let reference = self.reference(kind)?;
32 reference.ensure_scope(tenant, scope)?;
33 Ok(reference)
34 }
35}
36
37#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
39#[serde(rename_all = "camelCase", deny_unknown_fields)]
40pub struct ImportArtifactRequest {
41 #[serde(default)]
42 pub identity: crate::ArtifactIdentity,
43 #[serde(default)]
44 pub retention: crate::ArtifactRetention,
45 #[serde(
46 default,
47 skip_serializing_if = "Option::is_none",
48 deserialize_with = "crate::deserialize_optional_non_null"
49 )]
50 pub creation_id: Option<String>,
51 pub kind: ArtifactKind,
52 pub source_url: String,
53 #[serde(
54 default,
55 skip_serializing_if = "Option::is_none",
56 deserialize_with = "crate::deserialize_optional_non_null"
57 )]
58 pub mime_type: Option<String>,
59}
60
61impl ImportArtifactRequest {
62 pub const fn policy(&self) -> crate::ArtifactPolicy {
63 crate::ArtifactPolicy::new(self.identity, self.retention)
64 }
65}
66
67#[cfg(test)]
68mod tests {
69 use super::*;
70 #[test]
71 fn media_rejects_legacy_fields_and_wrong_scope_or_kind() {
72 for value in [
73 serde_json::json!({"url":"https://example.com/a"}),
74 serde_json::json!({"uri":"x","base64":"eA=="}),
75 serde_json::json!({"uri":"x","mimeType":"image/png"}),
76 serde_json::json!({"uri":"x","createdAt":1}),
77 ] {
78 assert!(serde_json::from_value::<MediaReference>(value).is_err());
79 }
80 let reference = ArtifactReference::new(
81 "tenant",
82 "scope",
83 "a".repeat(64),
84 crate::ArtifactMetadata::audio("audio/mpeg", 10, None).unwrap(),
85 )
86 .unwrap();
87 let media = MediaReference {
88 uri: reference.uri().unwrap().parse().unwrap(),
89 };
90 assert!(media
91 .validate("tenant", "scope", ArtifactKind::Audio)
92 .is_ok());
93 assert!(media
94 .validate("tenant", "other", ArtifactKind::Audio)
95 .is_err());
96 assert!(media
97 .validate("tenant", "scope", ArtifactKind::Image)
98 .is_err());
99 assert!(serde_json::from_value::<MediaReference>(serde_json::json!({
100 "uri": "https://example.com/a"
101 }))
102 .is_err());
103 }
104}
105
106#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
108#[serde(try_from = "String", into = "String")]
109pub struct ArtifactUri(String);
110
111impl ArtifactUri {
112 pub fn as_str(&self) -> &str {
113 &self.0
114 }
115 pub fn reference(&self) -> ArtifactReference {
116 ArtifactReference::parse(&self.0).expect("ArtifactUri was validated at construction")
117 }
118}
119impl std::str::FromStr for ArtifactUri {
120 type Err = ArtifactReferenceError;
121 fn from_str(value: &str) -> Result<Self, Self::Err> {
122 let reference = ArtifactReference::parse(value)?;
123 Ok(Self(reference.uri()?))
124 }
125}
126impl TryFrom<String> for ArtifactUri {
127 type Error = ArtifactReferenceError;
128 fn try_from(value: String) -> Result<Self, Self::Error> {
129 value.parse()
130 }
131}
132impl From<ArtifactUri> for String {
133 fn from(value: ArtifactUri) -> String {
134 value.0
135 }
136}
137impl std::ops::Deref for ArtifactUri {
138 type Target = str;
139 fn deref(&self) -> &str {
140 self.as_str()
141 }
142}
143impl AsRef<str> for ArtifactUri {
144 fn as_ref(&self) -> &str {
145 self.as_str()
146 }
147}
148impl std::fmt::Display for ArtifactUri {
149 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
150 self.0.fmt(f)
151 }
152}