ossify/ops/bucket/website/
put_bucket_website.rs1use std::future::Future;
6
7use http::Method;
8use serde::{Deserialize, Serialize};
9use serde_with::skip_serializing_none;
10
11use crate::body::XMLBody;
12use crate::error::Result;
13use crate::response::EmptyResponseProcessor;
14use crate::ser::OnlyKeyField;
15use crate::{Client, Ops, Prepared, Request};
16
17#[derive(Debug, Clone, Default, Serialize)]
18pub struct PutBucketWebsiteParams {
19 website: OnlyKeyField,
20}
21
22#[skip_serializing_none]
24#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
25#[serde(rename_all = "PascalCase")]
26pub struct IndexDocument {
27 pub suffix: String,
28 pub support_sub_dir: Option<bool>,
29 #[serde(rename = "Type")]
31 pub type_: Option<u32>,
32}
33
34#[skip_serializing_none]
36#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
37#[serde(rename_all = "PascalCase")]
38pub struct ErrorDocument {
39 pub key: String,
40 pub http_status: Option<u32>,
41}
42
43#[skip_serializing_none]
45#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
46#[serde(rename_all = "PascalCase")]
47pub struct RoutingRuleCondition {
48 pub key_prefix_equals: Option<String>,
49 pub http_error_code_returned_equals: Option<u32>,
50 pub key_suffix_equals: Option<String>,
51 #[serde(rename = "IncludeHeader", default, skip_serializing_if = "Vec::is_empty")]
52 pub include_headers: Vec<IncludeHeader>,
53}
54
55#[skip_serializing_none]
57#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
58#[serde(rename_all = "PascalCase")]
59pub struct IncludeHeader {
60 pub key: String,
61 pub equals: Option<String>,
62}
63
64#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
66#[serde(rename_all = "PascalCase")]
67pub struct MirrorSetHeader {
68 pub key: String,
69 pub value: String,
70}
71
72#[skip_serializing_none]
74#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
75#[serde(rename_all = "PascalCase")]
76pub struct MirrorHeaders {
77 pub pass_all: Option<bool>,
78 #[serde(rename = "Pass", default, skip_serializing_if = "Vec::is_empty")]
79 pub pass: Vec<String>,
80 #[serde(rename = "Remove", default, skip_serializing_if = "Vec::is_empty")]
81 pub remove: Vec<String>,
82 #[serde(rename = "Set", default, skip_serializing_if = "Vec::is_empty")]
83 pub set: Vec<MirrorSetHeader>,
84}
85
86#[skip_serializing_none]
88#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
89#[serde(rename_all = "PascalCase")]
90pub struct RoutingRuleRedirect {
91 pub redirect_type: Option<String>,
92 pub pass_query_string: Option<bool>,
93 #[serde(rename = "MirrorURL")]
94 pub mirror_url: Option<String>,
95 pub mirror_pass_query_string: Option<bool>,
96 pub mirror_follow_redirect: Option<bool>,
97 pub mirror_check_md5: Option<bool>,
98 pub mirror_headers: Option<MirrorHeaders>,
99 pub protocol: Option<String>,
100 pub host_name: Option<String>,
101 pub replace_key_prefix_with: Option<String>,
102 pub enable_replace_prefix: Option<bool>,
103 pub replace_key_with: Option<String>,
104 pub http_redirect_code: Option<u32>,
105}
106
107#[skip_serializing_none]
109#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
110#[serde(rename_all = "PascalCase")]
111pub struct RoutingRule {
112 pub rule_number: u32,
113 pub condition: RoutingRuleCondition,
114 pub redirect: RoutingRuleRedirect,
115}
116
117#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
119#[serde(rename_all = "PascalCase")]
120pub struct RoutingRules {
121 #[serde(rename = "RoutingRule", default, skip_serializing_if = "Vec::is_empty")]
122 pub rules: Vec<RoutingRule>,
123}
124
125#[skip_serializing_none]
127#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
128#[serde(rename = "WebsiteConfiguration", rename_all = "PascalCase")]
129pub struct WebsiteConfiguration {
130 pub index_document: Option<IndexDocument>,
131 pub error_document: Option<ErrorDocument>,
132 pub routing_rules: Option<RoutingRules>,
133}
134
135pub struct PutBucketWebsite {
136 pub config: WebsiteConfiguration,
137}
138
139impl Ops for PutBucketWebsite {
140 type Response = EmptyResponseProcessor;
141 type Body = XMLBody<WebsiteConfiguration>;
142 type Query = PutBucketWebsiteParams;
143
144 fn prepare(self) -> Result<Prepared<PutBucketWebsiteParams, WebsiteConfiguration>> {
145 Ok(Prepared {
146 method: Method::PUT,
147 query: Some(PutBucketWebsiteParams::default()),
148 body: Some(self.config),
149 ..Default::default()
150 })
151 }
152}
153
154pub trait PutBucketWebsiteOps {
155 fn put_bucket_website(&self, config: WebsiteConfiguration) -> impl Future<Output = Result<()>>;
160}
161
162impl PutBucketWebsiteOps for Client {
163 async fn put_bucket_website(&self, config: WebsiteConfiguration) -> Result<()> {
164 self.request(PutBucketWebsite { config }).await
165 }
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171
172 #[test]
173 fn params_serialize() {
174 assert_eq!(crate::ser::to_string(&PutBucketWebsiteParams::default()).unwrap(), "website");
175 }
176
177 #[test]
178 fn parse_minimal_config() {
179 let xml = r#"<WebsiteConfiguration>
180 <IndexDocument>
181 <Suffix>index.html</Suffix>
182 </IndexDocument>
183 <ErrorDocument>
184 <Key>error.html</Key>
185 <HttpStatus>404</HttpStatus>
186 </ErrorDocument>
187</WebsiteConfiguration>"#;
188 let parsed: WebsiteConfiguration = quick_xml::de::from_str(xml).unwrap();
189 assert_eq!(parsed.index_document.as_ref().unwrap().suffix, "index.html");
190 assert_eq!(parsed.error_document.as_ref().unwrap().http_status, Some(404));
191 }
192
193 #[test]
194 fn parse_routing_rules() {
195 let xml = r#"<WebsiteConfiguration>
196 <IndexDocument><Suffix>index.html</Suffix></IndexDocument>
197 <RoutingRules>
198 <RoutingRule>
199 <RuleNumber>1</RuleNumber>
200 <Condition>
201 <KeyPrefixEquals>abc/</KeyPrefixEquals>
202 <HttpErrorCodeReturnedEquals>404</HttpErrorCodeReturnedEquals>
203 </Condition>
204 <Redirect>
205 <RedirectType>Mirror</RedirectType>
206 <MirrorURL>http://example.com/</MirrorURL>
207 <MirrorHeaders>
208 <PassAll>true</PassAll>
209 <Pass>h1</Pass>
210 <Set><Key>k</Key><Value>v</Value></Set>
211 </MirrorHeaders>
212 </Redirect>
213 </RoutingRule>
214 </RoutingRules>
215</WebsiteConfiguration>"#;
216 let parsed: WebsiteConfiguration = quick_xml::de::from_str(xml).unwrap();
217 let rules = parsed.routing_rules.unwrap().rules;
218 assert_eq!(rules.len(), 1);
219 assert_eq!(rules[0].rule_number, 1);
220 let mh = rules[0].redirect.mirror_headers.as_ref().unwrap();
221 assert_eq!(mh.pass_all, Some(true));
222 assert_eq!(mh.pass, vec!["h1".to_string()]);
223 assert_eq!(mh.set[0].key, "k");
224 }
225}