1use http::Method;
9
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct Route {
12 pub action: &'static str,
13 pub id: Option<String>,
14 pub second_id: Option<String>,
15 pub with_tags: bool,
16}
17
18impl Route {
19 fn just(action: &'static str) -> Self {
20 Self {
21 action,
22 id: None,
23 second_id: None,
24 with_tags: false,
25 }
26 }
27
28 fn with_id(action: &'static str, id: &str) -> Self {
29 Self {
30 action,
31 id: Some(id.to_string()),
32 second_id: None,
33 with_tags: false,
34 }
35 }
36
37 fn with_two(action: &'static str, id: &str, second: &str) -> Self {
38 Self {
39 action,
40 id: Some(id.to_string()),
41 second_id: Some(second.to_string()),
42 with_tags: false,
43 }
44 }
45
46 fn flag_with_tags(mut self) -> Self {
47 self.with_tags = true;
48 self
49 }
50}
51
52pub fn route(method: &Method, path: &str, raw_query: &str) -> Option<Route> {
53 let path = path.strip_prefix("/2020-05-31").unwrap_or(path);
54 let path = path.trim_start_matches('/');
55 let segs: Vec<&str> = path.split('/').filter(|s| !s.is_empty()).collect();
60 let q = QueryFlags::parse(raw_query);
61 match (method, segs.as_slice()) {
62 (&Method::POST, ["distribution"]) if q.with_tags => {
64 Some(Route::just("CreateDistributionWithTags").flag_with_tags())
65 }
66 (&Method::POST, ["distribution"]) => Some(Route::just("CreateDistribution")),
67 (&Method::GET, ["distribution"]) => Some(Route::just("ListDistributions")),
68 (&Method::GET, ["distribution", id]) => Some(Route::with_id("GetDistribution", id)),
69 (&Method::GET, ["distribution", id, "config"]) => {
70 Some(Route::with_id("GetDistributionConfig", id))
71 }
72 (&Method::PUT, ["distribution", id, "config"]) => {
73 Some(Route::with_id("UpdateDistribution", id))
74 }
75 (&Method::PUT, ["distribution", id, "promote-staging-config"]) => {
76 Some(Route::with_id("UpdateDistributionWithStagingConfig", id))
77 }
78 (&Method::DELETE, ["distribution", id]) => Some(Route::with_id("DeleteDistribution", id)),
79 (&Method::POST, ["distribution", id, "copy"]) => {
80 Some(Route::with_id("CopyDistribution", id))
81 }
82 (&Method::PUT, ["distribution", id, "associate-alias"]) => {
83 Some(Route::with_id("AssociateAlias", id))
84 }
85 (&Method::PUT, ["distribution", id, "associate-web-acl"]) => {
86 Some(Route::with_id("AssociateDistributionWebACL", id))
87 }
88 (&Method::PUT, ["distribution", id, "disassociate-web-acl"]) => {
89 Some(Route::with_id("DisassociateDistributionWebACL", id))
90 }
91
92 (&Method::GET, ["distributionsByCachePolicyId", id]) => {
94 Some(Route::with_id("ListDistributionsByCachePolicyId", id))
95 }
96 (&Method::GET, ["distributionsByOriginRequestPolicyId", id]) => Some(Route::with_id(
97 "ListDistributionsByOriginRequestPolicyId",
98 id,
99 )),
100 (&Method::GET, ["distributionsByResponseHeadersPolicyId", id]) => Some(Route::with_id(
101 "ListDistributionsByResponseHeadersPolicyId",
102 id,
103 )),
104 (&Method::GET, ["distributionsByKeyGroupId", id]) => {
105 Some(Route::with_id("ListDistributionsByKeyGroup", id))
106 }
107 (&Method::GET, ["distributionsByWebACLId", id]) => {
108 Some(Route::with_id("ListDistributionsByWebACLId", id))
109 }
110 (&Method::GET, ["distributionsByVpcOriginId", id]) => {
111 Some(Route::with_id("ListDistributionsByVpcOriginId", id))
112 }
113 (&Method::GET, ["distributionsByAnycastIpListId", id]) => {
114 Some(Route::with_id("ListDistributionsByAnycastIpListId", id))
115 }
116 (&Method::GET, ["distributionsByConnectionMode", id]) => {
117 Some(Route::with_id("ListDistributionsByConnectionMode", id))
118 }
119 (&Method::GET, ["distributionsByConnectionFunction"]) => {
120 Some(Route::just("ListDistributionsByConnectionFunction"))
121 }
122 (&Method::GET, ["distributionsByOwnedResource", arn]) => {
123 Some(Route::with_id("ListDistributionsByOwnedResource", arn))
124 }
125 (&Method::GET, ["distributionsByTrustStore"]) => {
126 Some(Route::just("ListDistributionsByTrustStore"))
127 }
128 (&Method::POST, ["distributionsByRealtimeLogConfig"]) => {
129 Some(Route::just("ListDistributionsByRealtimeLogConfig"))
130 }
131 (&Method::GET, ["conflicting-alias"]) => Some(Route::just("ListConflictingAliases")),
132
133 (&Method::POST, ["distribution", dist, "invalidation"]) => {
135 Some(Route::with_id("CreateInvalidation", dist))
136 }
137 (&Method::GET, ["distribution", dist, "invalidation"]) => {
138 Some(Route::with_id("ListInvalidations", dist))
139 }
140 (&Method::GET, ["distribution", dist, "invalidation", id]) => {
141 Some(Route::with_two("GetInvalidation", dist, id))
142 }
143
144 (&Method::GET, ["tagging"]) => Some(Route::just("ListTagsForResource")),
146 (&Method::POST, ["tagging"]) if q.tag_op.as_deref() == Some("Tag") => {
147 Some(Route::just("TagResource"))
148 }
149 (&Method::POST, ["tagging"]) if q.tag_op.as_deref() == Some("Untag") => {
150 Some(Route::just("UntagResource"))
151 }
152
153 (&Method::POST, ["distributions", dist, "monitoring-subscription"]) => {
155 Some(Route::with_id("CreateMonitoringSubscription", dist))
156 }
157 (&Method::GET, ["distributions", dist, "monitoring-subscription"]) => {
158 Some(Route::with_id("GetMonitoringSubscription", dist))
159 }
160 (&Method::DELETE, ["distributions", dist, "monitoring-subscription"]) => {
161 Some(Route::with_id("DeleteMonitoringSubscription", dist))
162 }
163
164 (&Method::POST, ["origin-access-control"]) => {
166 Some(Route::just("CreateOriginAccessControl"))
167 }
168 (&Method::GET, ["origin-access-control"]) => Some(Route::just("ListOriginAccessControls")),
169 (&Method::GET, ["origin-access-control", id]) => {
170 Some(Route::with_id("GetOriginAccessControl", id))
171 }
172 (&Method::GET, ["origin-access-control", id, "config"]) => {
173 Some(Route::with_id("GetOriginAccessControlConfig", id))
174 }
175 (&Method::PUT, ["origin-access-control", id, "config"]) => {
176 Some(Route::with_id("UpdateOriginAccessControl", id))
177 }
178 (&Method::DELETE, ["origin-access-control", id]) => {
179 Some(Route::with_id("DeleteOriginAccessControl", id))
180 }
181
182 (&Method::POST, ["cache-policy"]) => Some(Route::just("CreateCachePolicy")),
184 (&Method::GET, ["cache-policy"]) => Some(Route::just("ListCachePolicies")),
185 (&Method::GET, ["cache-policy", id]) => Some(Route::with_id("GetCachePolicy", id)),
186 (&Method::GET, ["cache-policy", id, "config"]) => {
187 Some(Route::with_id("GetCachePolicyConfig", id))
188 }
189 (&Method::PUT, ["cache-policy", id]) => Some(Route::with_id("UpdateCachePolicy", id)),
190 (&Method::DELETE, ["cache-policy", id]) => Some(Route::with_id("DeleteCachePolicy", id)),
191
192 (&Method::POST, ["origin-request-policy"]) => {
194 Some(Route::just("CreateOriginRequestPolicy"))
195 }
196 (&Method::GET, ["origin-request-policy"]) => Some(Route::just("ListOriginRequestPolicies")),
197 (&Method::GET, ["origin-request-policy", id]) => {
198 Some(Route::with_id("GetOriginRequestPolicy", id))
199 }
200 (&Method::GET, ["origin-request-policy", id, "config"]) => {
201 Some(Route::with_id("GetOriginRequestPolicyConfig", id))
202 }
203 (&Method::PUT, ["origin-request-policy", id]) => {
204 Some(Route::with_id("UpdateOriginRequestPolicy", id))
205 }
206 (&Method::DELETE, ["origin-request-policy", id]) => {
207 Some(Route::with_id("DeleteOriginRequestPolicy", id))
208 }
209
210 (&Method::POST, ["response-headers-policy"]) => {
212 Some(Route::just("CreateResponseHeadersPolicy"))
213 }
214 (&Method::GET, ["response-headers-policy"]) => {
215 Some(Route::just("ListResponseHeadersPolicies"))
216 }
217 (&Method::GET, ["response-headers-policy", id]) => {
218 Some(Route::with_id("GetResponseHeadersPolicy", id))
219 }
220 (&Method::GET, ["response-headers-policy", id, "config"]) => {
221 Some(Route::with_id("GetResponseHeadersPolicyConfig", id))
222 }
223 (&Method::PUT, ["response-headers-policy", id]) => {
224 Some(Route::with_id("UpdateResponseHeadersPolicy", id))
225 }
226 (&Method::DELETE, ["response-headers-policy", id]) => {
227 Some(Route::with_id("DeleteResponseHeadersPolicy", id))
228 }
229
230 (&Method::POST, ["continuous-deployment-policy"]) => {
232 Some(Route::just("CreateContinuousDeploymentPolicy"))
233 }
234 (&Method::GET, ["continuous-deployment-policy"]) => {
235 Some(Route::just("ListContinuousDeploymentPolicies"))
236 }
237 (&Method::GET, ["continuous-deployment-policy", id]) => {
238 Some(Route::with_id("GetContinuousDeploymentPolicy", id))
239 }
240 (&Method::GET, ["continuous-deployment-policy", id, "config"]) => {
241 Some(Route::with_id("GetContinuousDeploymentPolicyConfig", id))
242 }
243 (&Method::PUT, ["continuous-deployment-policy", id]) => {
244 Some(Route::with_id("UpdateContinuousDeploymentPolicy", id))
245 }
246 (&Method::DELETE, ["continuous-deployment-policy", id]) => {
247 Some(Route::with_id("DeleteContinuousDeploymentPolicy", id))
248 }
249
250 (&Method::POST, ["function"]) => Some(Route::just("CreateFunction")),
252 (&Method::GET, ["function"]) => Some(Route::just("ListFunctions")),
253 (&Method::GET, ["function", name]) => Some(Route::with_id("GetFunction", name)),
254 (&Method::GET, ["function", name, "describe"]) => {
255 Some(Route::with_id("DescribeFunction", name))
256 }
257 (&Method::PUT, ["function", name]) => Some(Route::with_id("UpdateFunction", name)),
258 (&Method::DELETE, ["function", name]) => Some(Route::with_id("DeleteFunction", name)),
259 (&Method::POST, ["function", name, "publish"]) => {
260 Some(Route::with_id("PublishFunction", name))
261 }
262 (&Method::POST, ["function", name, "test"]) => Some(Route::with_id("TestFunction", name)),
263
264 (&Method::POST, ["public-key"]) => Some(Route::just("CreatePublicKey")),
266 (&Method::GET, ["public-key"]) => Some(Route::just("ListPublicKeys")),
267 (&Method::GET, ["public-key", id]) => Some(Route::with_id("GetPublicKey", id)),
268 (&Method::GET, ["public-key", id, "config"]) => {
269 Some(Route::with_id("GetPublicKeyConfig", id))
270 }
271 (&Method::PUT, ["public-key", id, "config"]) => Some(Route::with_id("UpdatePublicKey", id)),
272 (&Method::DELETE, ["public-key", id]) => Some(Route::with_id("DeletePublicKey", id)),
273
274 (&Method::POST, ["key-group"]) => Some(Route::just("CreateKeyGroup")),
276 (&Method::GET, ["key-group"]) => Some(Route::just("ListKeyGroups")),
277 (&Method::GET, ["key-group", id]) => Some(Route::with_id("GetKeyGroup", id)),
278 (&Method::GET, ["key-group", id, "config"]) => {
279 Some(Route::with_id("GetKeyGroupConfig", id))
280 }
281 (&Method::PUT, ["key-group", id]) => Some(Route::with_id("UpdateKeyGroup", id)),
282 (&Method::DELETE, ["key-group", id]) => Some(Route::with_id("DeleteKeyGroup", id)),
283
284 (&Method::POST, ["key-value-store"]) => Some(Route::just("CreateKeyValueStore")),
286 (&Method::GET, ["key-value-store"]) => Some(Route::just("ListKeyValueStores")),
287 (&Method::GET, ["key-value-store", name]) => {
288 Some(Route::with_id("DescribeKeyValueStore", name))
289 }
290 (&Method::PUT, ["key-value-store", name]) => {
291 Some(Route::with_id("UpdateKeyValueStore", name))
292 }
293 (&Method::DELETE, ["key-value-store", name]) => {
294 Some(Route::with_id("DeleteKeyValueStore", name))
295 }
296
297 (&Method::POST, ["origin-access-identity", "cloudfront"]) => {
299 Some(Route::just("CreateCloudFrontOriginAccessIdentity"))
300 }
301 (&Method::GET, ["origin-access-identity", "cloudfront"]) => {
302 Some(Route::just("ListCloudFrontOriginAccessIdentities"))
303 }
304 (&Method::GET, ["origin-access-identity", "cloudfront", id]) => {
305 Some(Route::with_id("GetCloudFrontOriginAccessIdentity", id))
306 }
307 (&Method::GET, ["origin-access-identity", "cloudfront", id, "config"]) => Some(
308 Route::with_id("GetCloudFrontOriginAccessIdentityConfig", id),
309 ),
310 (&Method::PUT, ["origin-access-identity", "cloudfront", id, "config"]) => {
311 Some(Route::with_id("UpdateCloudFrontOriginAccessIdentity", id))
312 }
313 (&Method::DELETE, ["origin-access-identity", "cloudfront", id]) => {
314 Some(Route::with_id("DeleteCloudFrontOriginAccessIdentity", id))
315 }
316
317 (&Method::POST, ["streaming-distribution"]) if q.with_tags => {
319 Some(Route::just("CreateStreamingDistributionWithTags").flag_with_tags())
320 }
321 (&Method::POST, ["streaming-distribution"]) => {
322 Some(Route::just("CreateStreamingDistribution"))
323 }
324 (&Method::GET, ["streaming-distribution"]) => {
325 Some(Route::just("ListStreamingDistributions"))
326 }
327 (&Method::GET, ["streaming-distribution", id]) => {
328 Some(Route::with_id("GetStreamingDistribution", id))
329 }
330 (&Method::GET, ["streaming-distribution", id, "config"]) => {
331 Some(Route::with_id("GetStreamingDistributionConfig", id))
332 }
333 (&Method::PUT, ["streaming-distribution", id, "config"]) => {
334 Some(Route::with_id("UpdateStreamingDistribution", id))
335 }
336 (&Method::DELETE, ["streaming-distribution", id]) => {
337 Some(Route::with_id("DeleteStreamingDistribution", id))
338 }
339
340 (&Method::POST, ["field-level-encryption"]) => {
342 Some(Route::just("CreateFieldLevelEncryptionConfig"))
343 }
344 (&Method::GET, ["field-level-encryption"]) => {
345 Some(Route::just("ListFieldLevelEncryptionConfigs"))
346 }
347 (&Method::GET, ["field-level-encryption", id]) => {
348 Some(Route::with_id("GetFieldLevelEncryption", id))
349 }
350 (&Method::GET, ["field-level-encryption", id, "config"]) => {
351 Some(Route::with_id("GetFieldLevelEncryptionConfig", id))
352 }
353 (&Method::PUT, ["field-level-encryption", id, "config"]) => {
354 Some(Route::with_id("UpdateFieldLevelEncryptionConfig", id))
355 }
356 (&Method::DELETE, ["field-level-encryption", id]) => {
357 Some(Route::with_id("DeleteFieldLevelEncryptionConfig", id))
358 }
359 (&Method::POST, ["field-level-encryption-profile"]) => {
360 Some(Route::just("CreateFieldLevelEncryptionProfile"))
361 }
362 (&Method::GET, ["field-level-encryption-profile"]) => {
363 Some(Route::just("ListFieldLevelEncryptionProfiles"))
364 }
365 (&Method::GET, ["field-level-encryption-profile", id]) => {
366 Some(Route::with_id("GetFieldLevelEncryptionProfile", id))
367 }
368 (&Method::GET, ["field-level-encryption-profile", id, "config"]) => {
369 Some(Route::with_id("GetFieldLevelEncryptionProfileConfig", id))
370 }
371 (&Method::PUT, ["field-level-encryption-profile", id, "config"]) => {
372 Some(Route::with_id("UpdateFieldLevelEncryptionProfile", id))
373 }
374 (&Method::DELETE, ["field-level-encryption-profile", id]) => {
375 Some(Route::with_id("DeleteFieldLevelEncryptionProfile", id))
376 }
377
378 (&Method::POST, ["realtime-log-config"]) => Some(Route::just("CreateRealtimeLogConfig")),
380 (&Method::GET, ["realtime-log-config"]) => Some(Route::just("ListRealtimeLogConfigs")),
381 (&Method::PUT, ["realtime-log-config"]) => Some(Route::just("UpdateRealtimeLogConfig")),
382 (&Method::POST, ["get-realtime-log-config"]) => Some(Route::just("GetRealtimeLogConfig")),
383 (&Method::POST, ["delete-realtime-log-config"]) => {
384 Some(Route::just("DeleteRealtimeLogConfig"))
385 }
386
387 (&Method::POST, ["get-resource-policy"]) => Some(Route::just("GetResourcePolicy")),
389 (&Method::POST, ["put-resource-policy"]) => Some(Route::just("PutResourcePolicy")),
390 (&Method::POST, ["delete-resource-policy"]) => Some(Route::just("DeleteResourcePolicy")),
391
392 (&Method::POST, ["vpc-origin"]) => Some(Route::just("CreateVpcOrigin")),
394 (&Method::GET, ["vpc-origin"]) => Some(Route::just("ListVpcOrigins")),
395 (&Method::GET, ["vpc-origin", id]) => Some(Route::with_id("GetVpcOrigin", id)),
396 (&Method::PUT, ["vpc-origin", id]) => Some(Route::with_id("UpdateVpcOrigin", id)),
397 (&Method::DELETE, ["vpc-origin", id]) => Some(Route::with_id("DeleteVpcOrigin", id)),
398
399 (&Method::POST, ["anycast-ip-list"]) => Some(Route::just("CreateAnycastIpList")),
401 (&Method::GET, ["anycast-ip-list"]) => Some(Route::just("ListAnycastIpLists")),
402 (&Method::GET, ["anycast-ip-list", id]) => Some(Route::with_id("GetAnycastIpList", id)),
403 (&Method::PUT, ["anycast-ip-list", id]) => Some(Route::with_id("UpdateAnycastIpList", id)),
404 (&Method::DELETE, ["anycast-ip-list", id]) => {
405 Some(Route::with_id("DeleteAnycastIpList", id))
406 }
407
408 (&Method::POST, ["trust-store"]) => Some(Route::just("CreateTrustStore")),
410 (&Method::POST, ["trust-stores"]) => Some(Route::just("ListTrustStores")),
411 (&Method::GET, ["trust-store", id]) => Some(Route::with_id("GetTrustStore", id)),
412 (&Method::PUT, ["trust-store", id]) => Some(Route::with_id("UpdateTrustStore", id)),
413 (&Method::DELETE, ["trust-store", id]) => Some(Route::with_id("DeleteTrustStore", id)),
414
415 (&Method::POST, ["distribution-tenant"]) => Some(Route::just("CreateDistributionTenant")),
417 (&Method::GET, ["distribution-tenant"]) => {
418 Some(Route::just("GetDistributionTenantByDomain"))
419 }
420 (&Method::GET, ["distribution-tenant", id]) => {
421 Some(Route::with_id("GetDistributionTenant", id))
422 }
423 (&Method::PUT, ["distribution-tenant", id]) => {
424 Some(Route::with_id("UpdateDistributionTenant", id))
425 }
426 (&Method::DELETE, ["distribution-tenant", id]) => {
427 Some(Route::with_id("DeleteDistributionTenant", id))
428 }
429 (&Method::POST, ["distribution-tenants"]) => Some(Route::just("ListDistributionTenants")),
430 (&Method::POST, ["distribution-tenants-by-customization"]) => {
431 Some(Route::just("ListDistributionTenantsByCustomization"))
432 }
433 (&Method::PUT, ["distribution-tenant", id, "associate-web-acl"]) => {
434 Some(Route::with_id("AssociateDistributionTenantWebACL", id))
435 }
436 (&Method::PUT, ["distribution-tenant", id, "disassociate-web-acl"]) => {
437 Some(Route::with_id("DisassociateDistributionTenantWebACL", id))
438 }
439 (&Method::POST, ["distribution-tenant", dist, "invalidation"]) => Some(Route::with_id(
440 "CreateInvalidationForDistributionTenant",
441 dist,
442 )),
443 (&Method::GET, ["distribution-tenant", dist, "invalidation"]) => Some(Route::with_id(
444 "ListInvalidationsForDistributionTenant",
445 dist,
446 )),
447 (&Method::GET, ["distribution-tenant", dist, "invalidation", id]) => Some(Route::with_two(
448 "GetInvalidationForDistributionTenant",
449 dist,
450 id,
451 )),
452 (&Method::POST, ["domain-association"]) => Some(Route::just("UpdateDomainAssociation")),
453 (&Method::POST, ["domain-conflicts"]) => Some(Route::just("ListDomainConflicts")),
454 (&Method::POST, ["verify-dns-configuration"]) => {
455 Some(Route::just("VerifyDnsConfiguration"))
456 }
457 (&Method::GET, ["managed-certificate", id]) => {
458 Some(Route::with_id("GetManagedCertificateDetails", id))
459 }
460
461 (&Method::POST, ["connection-function"]) => Some(Route::just("CreateConnectionFunction")),
463 (&Method::POST, ["connection-functions"]) => Some(Route::just("ListConnectionFunctions")),
464 (&Method::GET, ["connection-function", id]) => {
465 Some(Route::with_id("GetConnectionFunction", id))
466 }
467 (&Method::GET, ["connection-function", id, "describe"]) => {
468 Some(Route::with_id("DescribeConnectionFunction", id))
469 }
470 (&Method::PUT, ["connection-function", id]) => {
471 Some(Route::with_id("UpdateConnectionFunction", id))
472 }
473 (&Method::DELETE, ["connection-function", id]) => {
474 Some(Route::with_id("DeleteConnectionFunction", id))
475 }
476 (&Method::POST, ["connection-function", id, "publish"]) => {
477 Some(Route::with_id("PublishConnectionFunction", id))
478 }
479 (&Method::POST, ["connection-function", id, "test"]) => {
480 Some(Route::with_id("TestConnectionFunction", id))
481 }
482 (&Method::POST, ["connection-group"]) => Some(Route::just("CreateConnectionGroup")),
483 (&Method::POST, ["connection-groups"]) => Some(Route::just("ListConnectionGroups")),
484 (&Method::GET, ["connection-group"]) => {
485 Some(Route::just("GetConnectionGroupByRoutingEndpoint"))
486 }
487 (&Method::GET, ["connection-group", id]) => Some(Route::with_id("GetConnectionGroup", id)),
488 (&Method::PUT, ["connection-group", id]) => {
489 Some(Route::with_id("UpdateConnectionGroup", id))
490 }
491 (&Method::DELETE, ["connection-group", id]) => {
492 Some(Route::with_id("DeleteConnectionGroup", id))
493 }
494
495 _ => None,
496 }
497}
498
499#[derive(Default, Debug)]
500struct QueryFlags {
501 with_tags: bool,
502 tag_op: Option<String>,
503}
504
505impl QueryFlags {
506 fn parse(raw: &str) -> Self {
507 let mut out = QueryFlags::default();
508 for pair in raw.split('&').filter(|p| !p.is_empty()) {
509 if pair == "WithTags" || pair.starts_with("WithTags=") {
510 out.with_tags = true;
511 continue;
512 }
513 if let Some(rest) = pair.strip_prefix("Operation=") {
514 let value = decode(rest);
515 out.tag_op = Some(value);
516 }
517 }
518 out
519 }
520}
521
522fn decode(s: &str) -> String {
523 let mut out = String::with_capacity(s.len());
525 let mut bytes = s.bytes();
526 while let Some(b) = bytes.next() {
527 if b == b'%' {
528 let h1 = bytes.next();
529 let h2 = bytes.next();
530 if let (Some(a), Some(b2)) = (h1, h2) {
531 if let (Some(a), Some(b2)) = (hex(a), hex(b2)) {
532 out.push(((a << 4) | b2) as char);
533 continue;
534 }
535 }
536 out.push('%');
537 } else if b == b'+' {
538 out.push(' ');
539 } else {
540 out.push(b as char);
541 }
542 }
543 out
544}
545
546fn hex(b: u8) -> Option<u8> {
547 match b {
548 b'0'..=b'9' => Some(b - b'0'),
549 b'a'..=b'f' => Some(b - b'a' + 10),
550 b'A'..=b'F' => Some(b - b'A' + 10),
551 _ => None,
552 }
553}
554
555#[cfg(test)]
556mod tests {
557 use super::*;
558
559 #[test]
560 fn create_distribution() {
561 let r = route(&Method::POST, "/2020-05-31/distribution", "").unwrap();
562 assert_eq!(r.action, "CreateDistribution");
563 }
564
565 #[test]
566 fn create_distribution_with_tags() {
567 let r = route(&Method::POST, "/2020-05-31/distribution", "WithTags").unwrap();
568 assert_eq!(r.action, "CreateDistributionWithTags");
569 assert!(r.with_tags);
570 }
571
572 #[test]
573 fn get_distribution() {
574 let r = route(&Method::GET, "/2020-05-31/distribution/EDFDVBD632BHDS5", "").unwrap();
575 assert_eq!(r.action, "GetDistribution");
576 assert_eq!(r.id.as_deref(), Some("EDFDVBD632BHDS5"));
577 }
578
579 #[test]
580 fn trailing_slash_collection_routes_to_list() {
581 let r = route(&Method::GET, "/2020-05-31/distribution/", "").unwrap();
584 assert_eq!(r.action, "ListDistributions");
585 }
586
587 #[test]
588 fn create_invalidation() {
589 let r = route(
590 &Method::POST,
591 "/2020-05-31/distribution/EDFDVBD632BHDS5/invalidation",
592 "",
593 )
594 .unwrap();
595 assert_eq!(r.action, "CreateInvalidation");
596 assert_eq!(r.id.as_deref(), Some("EDFDVBD632BHDS5"));
597 }
598
599 #[test]
600 fn get_invalidation() {
601 let r = route(
602 &Method::GET,
603 "/2020-05-31/distribution/EDFDVBD632BHDS5/invalidation/IDFDVBD632BHDS5",
604 "",
605 )
606 .unwrap();
607 assert_eq!(r.action, "GetInvalidation");
608 assert_eq!(r.id.as_deref(), Some("EDFDVBD632BHDS5"));
609 assert_eq!(r.second_id.as_deref(), Some("IDFDVBD632BHDS5"));
610 }
611
612 #[test]
613 fn tag_resource() {
614 let r = route(
615 &Method::POST,
616 "/2020-05-31/tagging",
617 "Operation=Tag&Resource=arn:aws:cloudfront",
618 )
619 .unwrap();
620 assert_eq!(r.action, "TagResource");
621 }
622
623 #[test]
624 fn untag_resource() {
625 let r = route(
626 &Method::POST,
627 "/2020-05-31/tagging",
628 "Operation=Untag&Resource=arn:aws:cloudfront",
629 )
630 .unwrap();
631 assert_eq!(r.action, "UntagResource");
632 }
633
634 #[test]
635 fn list_tags() {
636 let r = route(
637 &Method::GET,
638 "/2020-05-31/tagging",
639 "Resource=arn:aws:cloudfront",
640 )
641 .unwrap();
642 assert_eq!(r.action, "ListTagsForResource");
643 }
644
645 #[test]
646 fn unknown_returns_none() {
647 assert!(route(&Method::GET, "/2020-05-31/totally-bogus", "").is_none());
648 }
649}