oci_api/services/object_storage/
models.rs

1use crate::error::{Error, Result};
2use crate::services::object_storage::client::Bucket;
3use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64};
4use serde::{Deserialize, Serialize};
5use sha2::{Digest as ShaDigest, Sha256, Sha384};
6
7/// Checksum type
8#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
9pub struct Checksum {
10    pub algorithm: ChecksumAlgorithm,
11    pub value: String,
12}
13
14/// Checksum Algorithm
15#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
16pub enum ChecksumAlgorithm {
17    SHA256,
18    SHA384,
19    CRC32C,
20}
21
22/// Object Storage Object
23#[derive(Clone, Debug, Serialize, Deserialize)]
24pub struct Object {
25    /// Object name
26    pub name: String,
27    /// Object content
28    pub value: String,
29    /// MD5 Checksum (Default)
30    pub md5: String,
31    /// Additional Checksum (Optional)
32    pub checksum: Option<Checksum>,
33}
34
35impl Object {
36    pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
37        let value = value.into();
38        let digest = md5::compute(value.as_bytes());
39        let md5 = BASE64.encode(*digest);
40
41        Self {
42            name: name.into(),
43            value,
44            md5,
45            checksum: None,
46        }
47    }
48
49    /// Verify checksums
50    pub fn verify_checksums(&self) -> Result<()> {
51        let data = self.value.as_bytes();
52
53        // Verify MD5 (Default)
54        let digest = md5::compute(data);
55        let calculated = BASE64.encode(*digest);
56        if calculated != self.md5 {
57            return Err(Error::Other(format!(
58                "MD5 checksum mismatch: expected {}, calculated {}",
59                self.md5, calculated
60            )));
61        }
62
63        // Verify Additional Checksum
64        if let Some(checksum) = &self.checksum {
65            match checksum.algorithm {
66                ChecksumAlgorithm::SHA256 => {
67                    let mut hasher = Sha256::new();
68                    hasher.update(data);
69                    let result = hasher.finalize();
70                    let calculated = BASE64.encode(result);
71                    if calculated != checksum.value {
72                        return Err(Error::Other(format!(
73                            "SHA256 checksum mismatch: expected {}, calculated {}",
74                            checksum.value, calculated
75                        )));
76                    }
77                }
78                ChecksumAlgorithm::SHA384 => {
79                    let mut hasher = Sha384::new();
80                    hasher.update(data);
81                    let result = hasher.finalize();
82                    let calculated = BASE64.encode(result);
83                    if calculated != checksum.value {
84                        return Err(Error::Other(format!(
85                            "SHA384 checksum mismatch: expected {}, calculated {}",
86                            checksum.value, calculated
87                        )));
88                    }
89                }
90                ChecksumAlgorithm::CRC32C => {
91                    let calculated_crc = crc32c::crc32c(data);
92                    let bytes = calculated_crc.to_be_bytes();
93                    let calculated = BASE64.encode(bytes);
94
95                    if calculated != checksum.value {
96                        return Err(Error::Other(format!(
97                            "CRC32C checksum mismatch: expected {}, calculated {}",
98                            checksum.value, calculated
99                        )));
100                    }
101                }
102            }
103        }
104
105        Ok(())
106    }
107}
108
109/// Retention Rule Duration
110#[derive(Debug, Clone, Serialize, Deserialize)]
111#[serde(rename_all = "camelCase")]
112pub struct RetentionDuration {
113    /// Time amount
114    pub time_amount: u64,
115    /// Time unit (YEARS, DAYS)
116    pub time_unit: RetentionTimeUnit,
117}
118
119/// Retention Time Unit
120#[derive(Debug, Clone, Serialize, Deserialize)]
121#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
122pub enum RetentionTimeUnit {
123    Years,
124    Days,
125}
126
127/// Retention Rule
128#[derive(Debug, Clone, Serialize, Deserialize)]
129#[serde(rename_all = "camelCase")]
130pub struct RetentionRule {
131    /// Retention Rule ID
132    pub id: String,
133    /// Display name
134    pub display_name: String,
135    /// Duration
136    #[serde(skip_serializing_if = "Option::is_none")]
137    pub duration: Option<RetentionDuration>,
138    /// Time rule locked
139    #[serde(skip_serializing_if = "Option::is_none")]
140    pub time_rule_locked: Option<String>,
141    /// Time created
142    pub time_created: String,
143    /// Time modified
144    pub time_modified: String,
145    /// ETag
146    pub etag: String,
147}
148
149impl From<RetentionRule> for String {
150    fn from(rule: RetentionRule) -> Self {
151        rule.id
152    }
153}
154
155impl From<&RetentionRule> for String {
156    fn from(rule: &RetentionRule) -> Self {
157        rule.id.clone()
158    }
159}
160
161/// Retention Rule Details
162#[derive(Debug, Clone, Serialize, Deserialize, Default)]
163#[serde(rename_all = "camelCase")]
164pub struct RetentionRuleDetails {
165    /// Display name
166    #[serde(skip_serializing_if = "Option::is_none")]
167    pub display_name: Option<String>,
168    /// Duration
169    #[serde(skip_serializing_if = "Option::is_none")]
170    pub duration: Option<RetentionDuration>,
171    /// Time rule locked
172    #[serde(skip_serializing_if = "Option::is_none")]
173    pub time_rule_locked: Option<String>,
174}
175
176impl RetentionRule {
177    /// Delete this retention rule
178    pub async fn delete(&self, bucket: &Bucket) -> Result<()> {
179        bucket.delete_retention_rule(&self.id).await
180    }
181
182    /// Update this retention rule
183    pub async fn update(
184        &self,
185        bucket: &Bucket,
186        details: RetentionRuleDetails,
187    ) -> Result<RetentionRule> {
188        bucket.update_retention_rule(&self.id, details).await
189    }
190}