azure_storage_blobs/container/
mod.rs
1use azure_core::{
2 date,
3 error::{Error, ErrorKind, ResultExt},
4 headers::{self, AsHeaders, Headers},
5};
6pub mod operations;
7
8use azure_core::{
9 headers::{
10 BLOB_PUBLIC_ACCESS, HAS_IMMUTABILITY_POLICY, HAS_LEGAL_HOLD, LEASE_DURATION, LEASE_STATE,
11 LEASE_STATUS, META_PREFIX,
12 },
13 LeaseDuration, LeaseState, LeaseStatus,
14};
15use azure_storage::parsing_xml::{cast_must, cast_optional, traverse};
16use std::collections::HashMap;
17use time::OffsetDateTime;
18use xml::{Element, Xml};
19
20create_enum!(
21 PublicAccess,
22 (None, "none"),
23 (Container, "container"),
24 (Blob, "blob")
25);
26
27impl AsHeaders for PublicAccess {
28 type Iter = std::option::IntoIter<(headers::HeaderName, headers::HeaderValue)>;
29
30 fn as_headers(&self) -> Self::Iter {
31 match self {
32 PublicAccess::Blob => Some((BLOB_PUBLIC_ACCESS, "blob".into())).into_iter(),
33 PublicAccess::Container => Some((BLOB_PUBLIC_ACCESS, "container".into())).into_iter(),
34 PublicAccess::None => None.into_iter(),
35 }
36 }
37}
38
39pub(crate) fn public_access_from_header(header_map: &Headers) -> azure_core::Result<PublicAccess> {
40 match header_map.get_optional_as(&BLOB_PUBLIC_ACCESS)? {
41 Some(p) => Ok(p),
42 None => Ok(PublicAccess::None),
43 }
44}
45
46#[derive(Debug, Clone)]
47pub struct Container {
48 pub name: String,
49 pub last_modified: OffsetDateTime,
50 pub e_tag: String,
51 pub lease_status: LeaseStatus,
52 pub lease_state: LeaseState,
53 pub lease_duration: Option<LeaseDuration>,
54 pub public_access: PublicAccess,
55 pub has_immutability_policy: bool,
56 pub has_legal_hold: bool,
57 pub metadata: HashMap<String, String>,
58}
59
60impl AsRef<str> for Container {
61 fn as_ref(&self) -> &str {
62 &self.name
63 }
64}
65
66impl Container {
67 pub fn new(name: &str) -> Container {
68 Container {
69 name: name.to_owned(),
70 last_modified: OffsetDateTime::now_utc(),
71 e_tag: String::new(),
72 lease_status: LeaseStatus::Unlocked,
73 lease_state: LeaseState::Available,
74 lease_duration: None,
75 public_access: PublicAccess::None,
76 has_immutability_policy: false,
77 has_legal_hold: false,
78 metadata: HashMap::new(),
79 }
80 }
81
82 pub(crate) fn from_response<NAME>(
83 name: NAME,
84 headers: &Headers,
85 ) -> azure_core::Result<Container>
86 where
87 NAME: Into<String>,
88 {
89 let last_modified = headers.get_str(&headers::LAST_MODIFIED)?;
90 let last_modified = date::parse_rfc1123(last_modified)?;
91
92 let e_tag = headers.get_as(&headers::ETAG)?;
93
94 let lease_status = headers.get_as(&LEASE_STATUS)?;
95 let lease_state = headers.get_as(&LEASE_STATE)?;
96
97 let lease_duration = headers.get_optional_as(&LEASE_DURATION)?;
98
99 let public_access = public_access_from_header(headers)?;
100
101 let has_immutability_policy = headers.get_as(&HAS_IMMUTABILITY_POLICY)?;
102 let has_legal_hold = headers.get_as(&HAS_LEGAL_HOLD)?;
103
104 let mut metadata: HashMap<String, String> = HashMap::new();
105 for (key, value) in headers.iter() {
106 if key.as_str().starts_with(META_PREFIX.as_str()) {
107 metadata.insert(key.as_str().to_owned(), value.as_str().to_owned());
108 }
109 }
110
111 Ok(Container {
112 name: name.into(),
113 last_modified,
114 e_tag,
115 lease_status,
116 lease_state,
117 lease_duration,
118 public_access,
119 has_immutability_policy,
120 has_legal_hold,
121 metadata,
122 })
123 }
124
125 pub(crate) fn parse(elem: &Element) -> azure_core::Result<Container> {
126 let name = cast_must(elem, &["Name"]).map_kind(ErrorKind::DataConversion)?;
127 let last_modified = cast_must(elem, &["Properties", "Last-Modified"])?;
128 let e_tag = cast_must(elem, &["Properties", "Etag"])?;
129 let lease_state = cast_must(elem, &["Properties", "LeaseState"])?;
130 let lease_duration = cast_optional(elem, &["Properties", "LeaseDuration"])?;
131 let lease_status = cast_must(elem, &["Properties", "LeaseStatus"])?;
132 let public_access =
133 cast_optional(elem, &["Properties", "PublicAccess"])?.unwrap_or(PublicAccess::None);
134 let has_immutability_policy = cast_must(elem, &["Properties", "HasImmutabilityPolicy"])?;
135 let has_legal_hold = cast_must(elem, &["Properties", "HasLegalHold"])?;
136 let metadata = {
137 let mut hm = HashMap::new();
138 let metadata = traverse(elem, &["Metadata"], true)?;
139
140 for m in metadata {
141 for key in &m.children {
142 let Xml::ElementNode(elem) = key else {
143 return Err(Error::message(
144 ErrorKind::DataConversion,
145 "Metadata should contain an ElementNode",
146 ));
147 };
148
149 if elem.children.is_empty() {
150 return Err(Error::message(
151 ErrorKind::DataConversion,
152 "Metadata node should not be empty",
153 ));
154 }
155
156 let key = elem.name.clone();
157
158 let content = {
159 match &elem.children[0] {
160 Xml::CharacterNode(content) => content.clone(),
161 _ => {
162 return Err(Error::message(ErrorKind::DataConversion,
163 "Metadata node should contain a CharacterNode with metadata value",
164 ));
165 }
166 }
167 };
168
169 hm.insert(key, content);
170 }
171 }
172
173 hm
174 };
175
176 Ok(Container {
177 name,
178 last_modified,
179 e_tag,
180 lease_status,
181 lease_state,
182 lease_duration,
183 public_access,
184 has_immutability_policy,
185 has_legal_hold,
186 metadata,
187 })
188 }
189}