Skip to main content

artifact_api/
policy.rs

1use serde::{Deserialize, Serialize};
2
3/// How callers distinguish resources containing the same bytes.
4#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
5#[serde(rename_all = "lowercase")]
6pub enum ArtifactIdentity {
7    #[default]
8    Auto,
9    Man,
10}
11
12impl ArtifactIdentity {
13    pub const fn code(self) -> &'static str {
14        match self {
15            Self::Auto => "auto",
16            Self::Man => "man",
17        }
18    }
19    pub fn from_code(value: &str) -> Result<Self, crate::ArtifactReferenceError> {
20        match value {
21            "auto" => Ok(Self::Auto),
22            "man" => Ok(Self::Man),
23            _ => Err(crate::invalid("unsupported artifact identity")),
24        }
25    }
26    /// Manual IDs are supplied and retained verbatim by callers.
27    pub fn validate_creation_id(
28        self,
29        id: Option<&str>,
30    ) -> Result<(), crate::ArtifactReferenceError> {
31        match (self, id) {
32            (Self::Man, Some(id)) if !id.is_empty() && id.len() <= 128
33                && id.bytes().all(|b| b.is_ascii_alphanumeric() || matches!(b, b'-' | b'_')) => Ok(()),
34            (Self::Man, _) => Err(crate::invalid("man identity requires a caller-supplied creation ID of 1-128 ASCII letters, digits, hyphens or underscores")),
35            (Self::Auto, None) => Ok(()),
36            (Self::Auto, Some(_)) => Err(crate::invalid("creation ID requires man identity")),
37        }
38    }
39}
40
41/// Retention is independent of identity and physical storage technology.
42#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
43#[serde(rename_all = "lowercase")]
44pub enum ArtifactRetention {
45    #[default]
46    Std,
47    Tmp,
48}
49
50impl ArtifactRetention {
51    pub const fn code(self) -> &'static str {
52        match self {
53            Self::Std => "std",
54            Self::Tmp => "tmp",
55        }
56    }
57    pub const fn is_temporary(self) -> bool {
58        matches!(self, Self::Tmp)
59    }
60    pub fn from_code(value: &str) -> Result<Self, crate::ArtifactReferenceError> {
61        match value {
62            "std" => Ok(Self::Std),
63            "tmp" => Ok(Self::Tmp),
64            _ => Err(crate::invalid("unsupported artifact retention")),
65        }
66    }
67}
68
69/// Both independent policy dimensions participate in resource identity.
70#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
71#[serde(deny_unknown_fields)]
72pub struct ArtifactPolicy {
73    pub identity: ArtifactIdentity,
74    pub retention: ArtifactRetention,
75}
76
77impl ArtifactPolicy {
78    pub const AUTO_STD: Self = Self::new(ArtifactIdentity::Auto, ArtifactRetention::Std);
79    pub const AUTO_TMP: Self = Self::new(ArtifactIdentity::Auto, ArtifactRetention::Tmp);
80    pub const MAN_STD: Self = Self::new(ArtifactIdentity::Man, ArtifactRetention::Std);
81    pub const MAN_TMP: Self = Self::new(ArtifactIdentity::Man, ArtifactRetention::Tmp);
82    pub const fn new(identity: ArtifactIdentity, retention: ArtifactRetention) -> Self {
83        Self {
84            identity,
85            retention,
86        }
87    }
88    pub const fn is_temporary(self) -> bool {
89        self.retention.is_temporary()
90    }
91    pub fn validate_creation_id(
92        self,
93        id: Option<&str>,
94    ) -> Result<(), crate::ArtifactReferenceError> {
95        self.identity.validate_creation_id(id)
96    }
97}