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}