oci_api/services/object_storage/
models.rs1use 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#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
9pub struct Checksum {
10 pub algorithm: ChecksumAlgorithm,
11 pub value: String,
12}
13
14#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
16pub enum ChecksumAlgorithm {
17 SHA256,
18 SHA384,
19 CRC32C,
20}
21
22#[derive(Clone, Debug, Serialize, Deserialize)]
24pub struct Object {
25 pub name: String,
27 pub value: String,
29 pub md5: String,
31 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 pub fn verify_checksums(&self) -> Result<()> {
51 let data = self.value.as_bytes();
52
53 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 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#[derive(Debug, Clone, Serialize, Deserialize)]
111#[serde(rename_all = "camelCase")]
112pub struct RetentionDuration {
113 pub time_amount: u64,
115 pub time_unit: RetentionTimeUnit,
117}
118
119#[derive(Debug, Clone, Serialize, Deserialize)]
121#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
122pub enum RetentionTimeUnit {
123 Years,
124 Days,
125}
126
127#[derive(Debug, Clone, Serialize, Deserialize)]
129#[serde(rename_all = "camelCase")]
130pub struct RetentionRule {
131 pub id: String,
133 pub display_name: String,
135 #[serde(skip_serializing_if = "Option::is_none")]
137 pub duration: Option<RetentionDuration>,
138 #[serde(skip_serializing_if = "Option::is_none")]
140 pub time_rule_locked: Option<String>,
141 pub time_created: String,
143 pub time_modified: String,
145 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#[derive(Debug, Clone, Serialize, Deserialize, Default)]
163#[serde(rename_all = "camelCase")]
164pub struct RetentionRuleDetails {
165 #[serde(skip_serializing_if = "Option::is_none")]
167 pub display_name: Option<String>,
168 #[serde(skip_serializing_if = "Option::is_none")]
170 pub duration: Option<RetentionDuration>,
171 #[serde(skip_serializing_if = "Option::is_none")]
173 pub time_rule_locked: Option<String>,
174}
175
176impl RetentionRule {
177 pub async fn delete(&self, bucket: &Bucket) -> Result<()> {
179 bucket.delete_retention_rule(&self.id).await
180 }
181
182 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}