Skip to main content

aliyun_oss/operations/
bucket_style.rs

1//! Image style operations.
2
3use crate::client::{BucketOperations, OSSClientInner};
4use crate::error::{ErrorContext, OssError, OssErrorKind, Result};
5use crate::http::client::{HttpRequest, HttpResponse};
6use crate::types::bucket::BucketName;
7use serde::{Deserialize, Serialize};
8use std::sync::Arc;
9
10#[derive(Debug, Clone, Serialize)]
11#[serde(rename = "Style")]
12struct StyleConfig {
13    #[serde(rename = "Content")]
14    content: String,
15}
16
17#[derive(Debug, Clone, Deserialize)]
18#[serde(rename = "Style")]
19struct StyleConfigResp {
20    #[serde(rename = "Name")]
21    name: String,
22    #[serde(rename = "Content")]
23    content: String,
24}
25
26#[derive(Debug, Clone, Deserialize)]
27#[serde(rename = "StyleList")]
28struct StyleListResp {
29    #[serde(rename = "Style", default)]
30    styles: Vec<StyleConfigResp>,
31}
32
33pub struct PutBucketStyleBuilder {
34    client: Arc<OSSClientInner>,
35    bucket: BucketName,
36    name: String,
37    content: String,
38}
39impl PutBucketStyleBuilder {
40    pub(crate) fn new(
41        c: Arc<OSSClientInner>,
42        b: BucketName,
43        n: impl Into<String>,
44        ct: impl Into<String>,
45    ) -> Self {
46        Self {
47            client: c,
48            bucket: b,
49            name: n.into(),
50            content: ct.into(),
51        }
52    }
53    pub async fn send(self) -> Result<PutBucketStyleOutput> {
54        put_style_impl(self.client, self.bucket, self.name, self.content).await
55    }
56}
57#[derive(Debug, Clone)]
58pub struct PutBucketStyleOutput {
59    pub request_id: String,
60}
61
62pub struct GetBucketStyleBuilder {
63    client: Arc<OSSClientInner>,
64    bucket: BucketName,
65    name: String,
66}
67impl GetBucketStyleBuilder {
68    pub(crate) fn new(c: Arc<OSSClientInner>, b: BucketName, n: impl Into<String>) -> Self {
69        Self {
70            client: c,
71            bucket: b,
72            name: n.into(),
73        }
74    }
75    pub async fn send(self) -> Result<GetBucketStyleOutput> {
76        get_style_impl(self.client, self.bucket, self.name).await
77    }
78}
79#[derive(Debug, Clone)]
80pub struct GetBucketStyleOutput {
81    pub name: String,
82    pub content: String,
83}
84
85pub struct ListBucketStyleBuilder {
86    client: Arc<OSSClientInner>,
87    bucket: BucketName,
88}
89impl ListBucketStyleBuilder {
90    pub(crate) fn new(c: Arc<OSSClientInner>, b: BucketName) -> Self {
91        Self {
92            client: c,
93            bucket: b,
94        }
95    }
96    pub async fn send(self) -> Result<ListBucketStyleOutput> {
97        list_style_impl(self.client, self.bucket).await
98    }
99}
100#[derive(Debug, Clone)]
101pub struct ListBucketStyleOutput {
102    pub styles: Vec<StyleEntry>,
103}
104#[derive(Debug, Clone)]
105pub struct StyleEntry {
106    pub name: String,
107    pub content: String,
108}
109
110pub struct DeleteBucketStyleBuilder {
111    client: Arc<OSSClientInner>,
112    bucket: BucketName,
113    name: String,
114}
115impl DeleteBucketStyleBuilder {
116    pub(crate) fn new(c: Arc<OSSClientInner>, b: BucketName, n: impl Into<String>) -> Self {
117        Self {
118            client: c,
119            bucket: b,
120            name: n.into(),
121        }
122    }
123    pub async fn send(self) -> Result<DeleteBucketStyleOutput> {
124        delete_style_impl(self.client, self.bucket, self.name).await
125    }
126}
127#[derive(Debug, Clone)]
128pub struct DeleteBucketStyleOutput {
129    pub request_id: String,
130}
131
132async fn put_style_impl(
133    client: Arc<OSSClientInner>,
134    bucket: BucketName,
135    name: String,
136    content: String,
137) -> Result<PutBucketStyleOutput> {
138    let ep = client.endpoint.clone();
139    let uri = format!(
140        "https://{}.{}?style&styleName={}",
141        bucket.as_str(),
142        ep,
143        name
144    );
145    let qp = vec![("style".into(), String::new()), ("styleName".into(), name)];
146    let xml = crate::util::xml::to_xml(&StyleConfig { content })?;
147    let req = HttpRequest::builder()
148        .method(http::Method::PUT)
149        .uri(&uri)
150        .body(bytes::Bytes::from(xml))
151        .build();
152    let r = client
153        .send_signed(req, Some(&bucket), qp)
154        .await
155        .map_err(|e| terr("PutBucketStyle", &bucket, &ep, e))?;
156    if r.status().is_success() {
157        Ok(PutBucketStyleOutput {
158            request_id: rid(&r),
159        })
160    } else {
161        Err(serr("PutBucketStyle", &bucket, r))
162    }
163}
164
165async fn get_style_impl(
166    client: Arc<OSSClientInner>,
167    bucket: BucketName,
168    name: String,
169) -> Result<GetBucketStyleOutput> {
170    let ep = client.endpoint.clone();
171    let uri = format!(
172        "https://{}.{}?style&styleName={}",
173        bucket.as_str(),
174        ep,
175        name
176    );
177    let qp = vec![("style".into(), String::new()), ("styleName".into(), name)];
178    let req = HttpRequest::builder()
179        .method(http::Method::GET)
180        .uri(&uri)
181        .build();
182    let r = client
183        .send_signed(req, Some(&bucket), qp)
184        .await
185        .map_err(|e| terr("GetBucketStyle", &bucket, &ep, e))?;
186    if r.is_success() {
187        let s: StyleConfigResp = crate::util::xml::from_xml(r.body_as_str().unwrap_or(""))
188            .map_err(|e| OssError {
189                kind: OssErrorKind::DeserializationError,
190                context: Box::new(ErrorContext {
191                    operation: Some("parse XML".into()),
192                    bucket: Some(bucket.to_string()),
193                    ..Default::default()
194                }),
195                source: Some(Box::new(e)),
196            })?;
197        Ok(GetBucketStyleOutput {
198            name: s.name,
199            content: s.content,
200        })
201    } else {
202        Err(serr("GetBucketStyle", &bucket, r))
203    }
204}
205
206async fn list_style_impl(
207    client: Arc<OSSClientInner>,
208    bucket: BucketName,
209) -> Result<ListBucketStyleOutput> {
210    let ep = client.endpoint.clone();
211    let uri = format!("https://{}.{}?style", bucket.as_str(), ep);
212    let qp = vec![("style".into(), String::new())];
213    let req = HttpRequest::builder()
214        .method(http::Method::GET)
215        .uri(&uri)
216        .build();
217    let r = client
218        .send_signed(req, Some(&bucket), qp)
219        .await
220        .map_err(|e| terr("ListBucketStyle", &bucket, &ep, e))?;
221    if r.is_success() {
222        let l: StyleListResp =
223            crate::util::xml::from_xml(r.body_as_str().unwrap_or("")).map_err(|e| OssError {
224                kind: OssErrorKind::DeserializationError,
225                context: Box::new(ErrorContext {
226                    operation: Some("parse XML".into()),
227                    bucket: Some(bucket.to_string()),
228                    ..Default::default()
229                }),
230                source: Some(Box::new(e)),
231            })?;
232        Ok(ListBucketStyleOutput {
233            styles: l
234                .styles
235                .into_iter()
236                .map(|s| StyleEntry {
237                    name: s.name,
238                    content: s.content,
239                })
240                .collect(),
241        })
242    } else {
243        Err(serr("ListBucketStyle", &bucket, r))
244    }
245}
246
247async fn delete_style_impl(
248    client: Arc<OSSClientInner>,
249    bucket: BucketName,
250    name: String,
251) -> Result<DeleteBucketStyleOutput> {
252    let ep = client.endpoint.clone();
253    let uri = format!(
254        "https://{}.{}?style&styleName={}",
255        bucket.as_str(),
256        ep,
257        name
258    );
259    let qp = vec![("style".into(), String::new()), ("styleName".into(), name)];
260    let req = HttpRequest::builder()
261        .method(http::Method::DELETE)
262        .uri(&uri)
263        .build();
264    let r = client
265        .send_signed(req, Some(&bucket), qp)
266        .await
267        .map_err(|e| terr("DeleteBucketStyle", &bucket, &ep, e))?;
268    if r.status().is_success() {
269        Ok(DeleteBucketStyleOutput {
270            request_id: rid(&r),
271        })
272    } else {
273        Err(serr("DeleteBucketStyle", &bucket, r))
274    }
275}
276
277fn rid(r: &HttpResponse) -> String {
278    r.headers
279        .get("x-oss-request-id")
280        .and_then(|v| v.to_str().ok())
281        .unwrap_or("")
282        .to_string()
283}
284fn terr(op: &str, b: &BucketName, ep: &str, e: OssError) -> OssError {
285    OssError {
286        kind: OssErrorKind::TransportError,
287        context: Box::new(ErrorContext {
288            operation: Some(op.into()),
289            bucket: Some(b.to_string()),
290            endpoint: Some(ep.into()),
291            ..Default::default()
292        }),
293        source: Some(Box::new(e)),
294    }
295}
296fn serr(op: &str, b: &BucketName, r: HttpResponse) -> OssError {
297    OssError {
298        kind: OssErrorKind::ServiceError(Box::new(crate::error::OssServiceError {
299            status_code: r.status().as_u16(),
300            code: String::new(),
301            message: String::new(),
302            request_id: String::new(),
303            host_id: String::new(),
304            resource: Some(b.to_string()),
305            string_to_sign: None,
306        })),
307        context: Box::new(ErrorContext {
308            operation: Some(op.into()),
309            bucket: Some(b.to_string()),
310            ..Default::default()
311        }),
312        source: None,
313    }
314}
315
316impl BucketOperations {
317    pub fn put_style(
318        &self,
319        name: impl Into<String>,
320        content: impl Into<String>,
321    ) -> PutBucketStyleBuilder {
322        PutBucketStyleBuilder::new(
323            self.client_inner().clone(),
324            self.bucket_name().clone(),
325            name,
326            content,
327        )
328    }
329    pub fn get_style(&self, name: impl Into<String>) -> GetBucketStyleBuilder {
330        GetBucketStyleBuilder::new(
331            self.client_inner().clone(),
332            self.bucket_name().clone(),
333            name,
334        )
335    }
336    pub fn list_style(&self) -> ListBucketStyleBuilder {
337        ListBucketStyleBuilder::new(self.client_inner().clone(), self.bucket_name().clone())
338    }
339    pub fn delete_style(&self, name: impl Into<String>) -> DeleteBucketStyleBuilder {
340        DeleteBucketStyleBuilder::new(
341            self.client_inner().clone(),
342            self.bucket_name().clone(),
343            name,
344        )
345    }
346}
347
348#[cfg(test)]
349mod tests {
350    use super::*;
351    #[test]
352    fn style_xml() {
353        let c = StyleConfig {
354            content: "image/resize,m_fixed,w_100".into(),
355        };
356        let x = crate::util::xml::to_xml(&c).unwrap();
357        assert!(x.contains("<Content>image/resize,m_fixed,w_100</Content>"));
358    }
359}