azure_storage_blob/models/
mod.rs1mod download_result;
7pub(crate) mod drains;
8pub(crate) mod error;
9pub(crate) mod extensions;
10pub(crate) mod http_ranges;
11mod method_options;
12
13pub use http_ranges::HttpRange;
14pub(crate) mod response_ext;
15mod upload_result;
16
17pub use crate::generated::models::*;
18pub use download_result::{
19 BlobClientDownloadIntoResult, BlobClientDownloadResult, BlobDownloadProperties,
20};
21pub use method_options::BlobClientDownloadOptions;
22pub use method_options::BlockBlobClientUploadOptions;
23pub use method_options::BlockBlobClientUploadOptions as BlobClientUploadOptions;
24pub use upload_result::BlockBlobClientUploadResult;
25pub use upload_result::BlockBlobClientUploadResult as BlobClientUploadResult;
26
27use azure_core::fmt::SafeDebug;
28use serde::{Deserialize, Serialize};
29use std::collections::HashMap;
30
31#[derive(Clone, Default, SafeDebug)]
33#[non_exhaustive]
34pub struct BlobMetadata {
35 pub values: Option<HashMap<String, String>>,
37
38 pub encrypted: Option<String>,
40}
41
42impl<'de> Deserialize<'de> for BlobMetadata {
43 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
44 where
45 D: serde::Deserializer<'de>,
46 {
47 struct BlobMetadataVisitor;
48 impl<'de> serde::de::Visitor<'de> for BlobMetadataVisitor {
49 type Value = BlobMetadata;
50 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
51 formatter.write_str("a BlobMetadata struct definition")
52 }
53 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
54 where
55 A: serde::de::MapAccess<'de>,
56 {
57 let mut values = HashMap::new();
58 let mut encrypted = None;
59 while let Some(key) = map.next_key::<String>()? {
60 match key.as_ref() {
61 "@Encrypted" => encrypted = Some(map.next_value()?),
62 _ => {
63 let value: String = map.next_value()?;
64 values.insert(key, value);
65 }
66 }
67 }
68 let values = match values.len() {
69 0 => None,
70 _ => Some(values),
71 };
72 Ok(BlobMetadata { values, encrypted })
73 }
74 }
75 deserializer.deserialize_map(BlobMetadataVisitor)
76 }
77}
78
79impl Serialize for BlobMetadata {
80 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
81 where
82 S: serde::Serializer,
83 {
84 use serde::ser::SerializeMap;
85 let mut map = serializer.serialize_map(Some(
86 1 + match &self.values {
87 Some(values) => values.len(),
88 None => 0,
89 },
90 ))?;
91 if let Some(values) = &self.values {
92 for (k, v) in values {
93 map.serialize_entry(k, v)?;
94 }
95 }
96 if let Some(encrypted) = &self.encrypted {
97 map.serialize_entry("@Encrypted", encrypted)?;
98 }
99 map.end()
100 }
101}
102
103pub(crate) mod blob_name {
129 use super::BlobName;
130 use percent_encoding::percent_decode_str;
131 use serde::{de::Error, Deserialize, Deserializer};
132
133 pub fn deserialize<'de, D>(deserializer: D) -> Result<String, D::Error>
144 where
145 D: Deserializer<'de>,
146 {
147 let blob_name = BlobName::deserialize(deserializer)?;
148
149 let content = blob_name
150 .content
151 .ok_or_else(|| D::Error::custom("missing BlobName content"))?;
152
153 if blob_name.encoded.unwrap_or_default() {
154 let decoded = percent_decode_str(&content)
155 .decode_utf8()
156 .map_err(D::Error::custom)?;
157 Ok(decoded.into_owned())
158 } else {
159 Ok(content)
160 }
161 }
162
163 pub mod option {
165 use super::BlobName;
166 use percent_encoding::percent_decode_str;
167 use serde::{de::Error, Deserialize, Deserializer};
168
169 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
179 where
180 D: Deserializer<'de>,
181 {
182 let blob_name = Option::<BlobName>::deserialize(deserializer)?;
183
184 let Some(blob_name) = blob_name else {
185 return Ok(None);
186 };
187
188 let Some(content) = blob_name.content else {
189 return Ok(None);
190 };
191
192 if blob_name.encoded.unwrap_or_default() {
193 let decoded = percent_decode_str(&content)
194 .decode_utf8()
195 .map_err(D::Error::custom)?;
196 Ok(Some(decoded.into_owned()))
197 } else {
198 Ok(Some(content))
199 }
200 }
201 }
202}
203
204#[cfg(test)]
205mod tests {
206 use azure_core::xml;
207 use serde::Deserialize;
208
209 #[derive(Deserialize)]
210 struct RequiredName {
211 #[serde(
212 deserialize_with = "crate::models::blob_name::deserialize",
213 rename = "Name"
214 )]
215 name: String,
216 }
217
218 #[derive(Deserialize)]
219 struct OptionalName {
220 #[serde(
221 deserialize_with = "crate::models::blob_name::option::deserialize",
222 rename = "Name",
223 default
224 )]
225 name: Option<String>,
226 }
227
228 #[test]
229 fn deserialize_plain_name() {
230 let input = b"<Root><Name>hello</Name></Root>";
231 let result: RequiredName = xml::from_xml(input).unwrap();
232 assert_eq!(result.name, "hello");
233 }
234
235 #[test]
236 fn deserialize_encoded_name() {
237 let input = b"<Root><Name Encoded=\"true\">hello%20world</Name></Root>";
238 let result: RequiredName = xml::from_xml(input).unwrap();
239 assert_eq!(result.name, "hello world");
240 }
241
242 #[test]
243 fn deserialize_not_encoded_name() {
244 let input = b"<Root><Name Encoded=\"false\">hello%20world</Name></Root>";
245 let result: RequiredName = xml::from_xml(input).unwrap();
246 assert_eq!(result.name, "hello%20world");
247 }
248
249 #[test]
250 fn deserialize_option_some() {
251 let input = b"<Root><Name>hello</Name></Root>";
252 let result: OptionalName = xml::from_xml(input).unwrap();
253 assert_eq!(result.name.as_deref(), Some("hello"));
254 }
255
256 #[test]
257 fn deserialize_option_some_encoded() {
258 let input = b"<Root><Name Encoded=\"true\">hello%20world</Name></Root>";
259 let result: OptionalName = xml::from_xml(input).unwrap();
260 assert_eq!(result.name.as_deref(), Some("hello world"));
261 }
262
263 #[test]
264 fn deserialize_option_none() {
265 let input = b"<Root></Root>";
266 let result: OptionalName = xml::from_xml(input).unwrap();
267 assert_eq!(result.name, None);
268 }
269
270 #[test]
271 fn deserialize_encoded_xml_invalid_fffe() {
272 let input = b"<Root><Name Encoded=\"true\">blob%EF%BF%BEname</Name></Root>";
274 let result: RequiredName = xml::from_xml(input).unwrap();
275 assert_eq!(result.name, "blob\u{FFFE}name");
276 }
277
278 #[test]
279 fn deserialize_encoded_xml_invalid_ffff() {
280 let input = b"<Root><Name Encoded=\"true\">blob%EF%BF%BFname</Name></Root>";
282 let result: RequiredName = xml::from_xml(input).unwrap();
283 assert_eq!(result.name, "blob\u{FFFF}name");
284 }
285
286 #[test]
287 fn deserialize_option_encoded_xml_invalid_fffe() {
288 let input = b"<Root><Name Encoded=\"true\">blob%EF%BF%BEname</Name></Root>";
289 let result: OptionalName = xml::from_xml(input).unwrap();
290 assert_eq!(result.name.as_deref(), Some("blob\u{FFFE}name"));
291 }
292
293 #[test]
294 fn deserialize_option_encoded_xml_invalid_ffff() {
295 let input = b"<Root><Name Encoded=\"true\">blob%EF%BF%BFname</Name></Root>";
296 let result: OptionalName = xml::from_xml(input).unwrap();
297 assert_eq!(result.name.as_deref(), Some("blob\u{FFFF}name"));
298 }
299
300 #[test]
301 fn deserialize_encoded_mixed_invalid_and_normal() {
302 let input = b"<Root><Name Encoded=\"true\">dir%2Fblob%EF%BF%BEname%EF%BF%BF</Name></Root>";
304 let result: RequiredName = xml::from_xml(input).unwrap();
305 assert_eq!(result.name, "dir/blob\u{FFFE}name\u{FFFF}");
306 }
307}