1 + | // Code generated by software.amazon.smithy.rust.codegen.smithy-rs. DO NOT EDIT.
|
2 + | /*
|
3 + | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
4 + | * SPDX-License-Identifier: Apache-2.0
|
5 + | */
|
6 + |
|
7 + | //! Partition function to determine a partition for a given region
|
8 + | //!
|
9 + | //! This function supports adding regions dynamically, parsing a JSON file, and builder construction.
|
10 + | //!
|
11 + | //! If, at a future point, this interface stabilizes it is a good candidate for extraction into a
|
12 + | //! shared crate.
|
13 + | use crate::endpoint_lib::diagnostic::DiagnosticCollector;
|
14 + | use crate::endpoint_lib::partition::deser::deserialize_partitions;
|
15 + | use aws_smithy_json::deserialize::error::DeserializeError;
|
16 + | use regex_lite::Regex;
|
17 + | use std::borrow::Cow;
|
18 + | use std::collections::HashMap;
|
19 + |
|
20 + | /// Determine the AWS partition metadata for a given region
|
21 + | #[derive(Clone, Debug, Default)]
|
22 + | pub(crate) struct PartitionResolver {
|
23 + | partitions: Vec<PartitionMetadata>,
|
24 + | }
|
25 + |
|
26 + | impl PartitionResolver {
|
27 + | pub(crate) fn from_partitions(partitions: Vec<PartitionMetadata>) -> Self {
|
28 + | Self { partitions }
|
29 + | }
|
30 + | }
|
31 + |
|
32 + | /// Partition result returned from partition resolver
|
33 + | pub(crate) struct Partition<'a> {
|
34 + | name: &'a str,
|
35 + | dns_suffix: &'a str,
|
36 + | dual_stack_dns_suffix: &'a str,
|
37 + | supports_fips: bool,
|
38 + | supports_dual_stack: bool,
|
39 + | implicit_global_region: &'a str,
|
40 + | }
|
41 + |
|
42 + | #[allow(unused)]
|
43 + | impl Partition<'_> {
|
44 + | pub(crate) fn name(&self) -> &str {
|
45 + | self.name
|
46 + | }
|
47 + |
|
48 + | pub(crate) fn dns_suffix(&self) -> &str {
|
49 + | self.dns_suffix
|
50 + | }
|
51 + |
|
52 + | pub(crate) fn supports_fips(&self) -> bool {
|
53 + | self.supports_fips
|
54 + | }
|
55 + |
|
56 + | pub(crate) fn dual_stack_dns_suffix(&self) -> &str {
|
57 + | self.dual_stack_dns_suffix
|
58 + | }
|
59 + |
|
60 + | pub(crate) fn supports_dual_stack(&self) -> bool {
|
61 + | self.supports_dual_stack
|
62 + | }
|
63 + |
|
64 + | pub(crate) fn implicit_global_region(&self) -> &str {
|
65 + | self.implicit_global_region
|
66 + | }
|
67 + | }
|
68 + |
|
69 + | static DEFAULT_OVERRIDE: &PartitionOutputOverride = &PartitionOutputOverride {
|
70 + | name: None,
|
71 + | dns_suffix: None,
|
72 + | dual_stack_dns_suffix: None,
|
73 + | supports_fips: None,
|
74 + | supports_dual_stack: None,
|
75 + | implicit_global_region: None,
|
76 + | };
|
77 + |
|
78 + | /// Merge the base output and the override output, dealing with `Cow`s
|
79 + | macro_rules! merge {
|
80 + | ($base: expr, $output: expr, $field: ident) => {
|
81 + | $output.$field.as_ref().map(|s| s.as_ref()).unwrap_or($base.outputs.$field.as_ref())
|
82 + | };
|
83 + | }
|
84 + |
|
85 + | impl PartitionResolver {
|
86 + | #[allow(unused)]
|
87 + | pub(crate) fn empty() -> PartitionResolver {
|
88 + | PartitionResolver { partitions: vec![] }
|
89 + | }
|
90 + |
|
91 + | #[allow(unused)]
|
92 + | pub(crate) fn add_partition(&mut self, partition: PartitionMetadata) {
|
93 + | self.partitions.push(partition);
|
94 + | }
|
95 + |
|
96 + | pub(crate) fn new_from_json(partition_dot_json: &[u8]) -> Result<PartitionResolver, DeserializeError> {
|
97 + | deserialize_partitions(partition_dot_json)
|
98 + | }
|
99 + |
|
100 + | /// Resolve a partition for a given region
|
101 + | ///
|
102 + | /// 1. Enumerate each partition in the `partitions` array, and determine if the identifier to be
|
103 + | /// resolved matches an explicit region listed in the `regions` array for a given partition.
|
104 + | /// If identifier matches, proceed to step 4, otherwise continue to step 2.
|
105 + | /// 2. Enumerate each partition in the `partitions` array, use the regular expression
|
106 + | /// `regionRegex` to determine if the identifier matches the regular expression. If the
|
107 + | /// identifier matches, proceed to step 4, otherwise continue to step 3.
|
108 + | /// 3. If no partition is matched after exhausting step 1 and step 2, then fallback to matching
|
109 + | /// the identifier to the partition where `id == "aws"`, and proceed to step 4. If no `aws`
|
110 + | /// partition is present, return `None`.
|
111 + | /// 4. After matching the identifier to a partition using one of the previous steps, the partition function should return a
|
112 + | /// typed data structure containing the fields in `outputs` in the matched partition. **Important:** If a specific region
|
113 + | /// was matched, the properties associated with that region **MUST** be merged with the `outputs` field.
|
114 + | pub(crate) fn resolve_partition(&self, region: &str, e: &mut DiagnosticCollector) -> Option<Partition<'_>> {
|
115 + | let mut explicit_match_partition = self.partitions.iter().flat_map(|part| part.explicit_match(region));
|
116 + | let mut regex_match_partition = self.partitions.iter().flat_map(|part| part.regex_match(region));
|
117 + |
|
118 + | let (base, region_override) = explicit_match_partition.next().or_else(|| regex_match_partition.next()).or_else(|| {
|
119 + | match self.partitions.iter().find(|p| p.id == "aws") {
|
120 + | Some(partition) => Some((partition, None)),
|
121 + | None => {
|
122 + | e.report_error("no AWS partition!");
|
123 + | None
|
124 + | }
|
125 + | }
|
126 + | })?;
|
127 + | let region_override = region_override.as_ref().unwrap_or(&DEFAULT_OVERRIDE);
|
128 + | Some(Partition {
|
129 + | name: merge!(base, region_override, name),
|
130 + | dns_suffix: merge!(base, region_override, dns_suffix),
|
131 + | dual_stack_dns_suffix: merge!(base, region_override, dual_stack_dns_suffix),
|
132 + | supports_fips: region_override.supports_fips.unwrap_or(base.outputs.supports_fips),
|
133 + | supports_dual_stack: region_override.supports_dual_stack.unwrap_or(base.outputs.supports_dual_stack),
|
134 + | implicit_global_region: merge!(base, region_override, implicit_global_region),
|
135 + | })
|
136 + | }
|
137 + | }
|
138 + |
|
139 + | type Str = Cow<'static, str>;
|
140 + |
|
141 + | #[derive(Clone, Debug)]
|
142 + | pub(crate) struct PartitionMetadata {
|
143 + | id: Str,
|
144 + | region_regex: Regex,
|
145 + | regions: HashMap<Str, PartitionOutputOverride>,
|
146 + | outputs: PartitionOutput,
|
147 + | }
|
148 + |
|
149 + | #[derive(Default)]
|
150 + | pub(crate) struct PartitionMetadataBuilder {
|
151 + | pub(crate) id: Option<Str>,
|
152 + | pub(crate) region_regex: Option<Regex>,
|
153 + | pub(crate) regions: HashMap<Str, PartitionOutputOverride>,
|
154 + | pub(crate) outputs: Option<PartitionOutputOverride>,
|
155 + | }
|
156 + |
|
157 + | impl PartitionMetadataBuilder {
|
158 + | pub(crate) fn build(self) -> PartitionMetadata {
|
159 + | PartitionMetadata {
|
160 + | id: self.id.expect("id must be defined"),
|
161 + | region_regex: self.region_regex.expect("region regex must be defined"),
|
162 + | regions: self.regions,
|
163 + | outputs: self
|
164 + | .outputs
|
165 + | .expect("outputs must be defined")
|
166 + | .into_partition_output()
|
167 + | .expect("missing fields on outputs"),
|
168 + | }
|
169 + | }
|
170 + | }
|
171 + |
|
172 + | impl PartitionMetadata {
|
173 + | fn explicit_match(&self, region: &str) -> Option<(&PartitionMetadata, Option<&PartitionOutputOverride>)> {
|
174 + | self.regions.get(region).map(|output_override| (self, Some(output_override)))
|
175 + | }
|
176 + |
|
177 + | fn regex_match(&self, region: &str) -> Option<(&PartitionMetadata, Option<&PartitionOutputOverride>)> {
|
178 + | if self.region_regex.is_match(region) {
|
179 + | Some((self, None))
|
180 + | } else {
|
181 + | None
|
182 + | }
|
183 + | }
|
184 + | }
|
185 + |
|
186 + | #[derive(Clone, Debug)]
|
187 + | pub(crate) struct PartitionOutput {
|
188 + | name: Str,
|
189 + | dns_suffix: Str,
|
190 + | dual_stack_dns_suffix: Str,
|
191 + | supports_fips: bool,
|
192 + | supports_dual_stack: bool,
|
193 + | implicit_global_region: Str,
|
194 + | }
|
195 + |
|
196 + | #[derive(Clone, Debug, Default)]
|
197 + | pub(crate) struct PartitionOutputOverride {
|
198 + | name: Option<Str>,
|
199 + | dns_suffix: Option<Str>,
|
200 + | dual_stack_dns_suffix: Option<Str>,
|
201 + | supports_fips: Option<bool>,
|
202 + | supports_dual_stack: Option<bool>,
|
203 + | implicit_global_region: Option<Str>,
|
204 + | }
|
205 + |
|
206 + | impl PartitionOutputOverride {
|
207 + | pub(crate) fn into_partition_output(self) -> Result<PartitionOutput, Box<dyn std::error::Error>> {
|
208 + | Ok(PartitionOutput {
|
209 + | name: self.name.ok_or("missing name")?,
|
210 + | dns_suffix: self.dns_suffix.ok_or("missing dnsSuffix")?,
|
211 + | dual_stack_dns_suffix: self.dual_stack_dns_suffix.ok_or("missing dual_stackDnsSuffix")?,
|
212 + | supports_fips: self.supports_fips.ok_or("missing supports fips")?,
|
213 + | supports_dual_stack: self.supports_dual_stack.ok_or("missing supportsDualstack")?,
|
214 + | implicit_global_region: self.implicit_global_region.ok_or("missing implicitGlobalRegion")?,
|
215 + | })
|
216 + | }
|
217 + | }
|
218 + |
|
219 + | /// JSON deserializers for partition metadata
|
220 + | ///
|
221 + | /// This code was generated by smithy-rs and then hand edited for clarity
|
222 + | mod deser {
|
223 + | use crate::endpoint_lib::partition::{PartitionMetadata, PartitionMetadataBuilder, PartitionOutputOverride, PartitionResolver};
|
224 + | use aws_smithy_json::deserialize::token::{expect_bool_or_null, expect_start_object, expect_string_or_null, skip_value};
|
225 + | use aws_smithy_json::deserialize::{error::DeserializeError, json_token_iter, Token};
|
226 + | use regex_lite::Regex;
|
227 + | use std::borrow::Cow;
|
228 + | use std::collections::HashMap;
|
229 + |
|
230 + | pub(crate) fn deserialize_partitions(value: &[u8]) -> Result<PartitionResolver, DeserializeError> {
|
231 + | let mut tokens_owned = json_token_iter(value).peekable();
|
232 + | let tokens = &mut tokens_owned;
|
233 + | expect_start_object(tokens.next())?;
|
234 + | let mut resolver = None;
|
235 + | loop {
|
236 + | match tokens.next().transpose()? {
|
237 + | Some(Token::EndObject { .. }) => break,
|
238 + | Some(Token::ObjectKey { key, .. }) => match key.to_unescaped()?.as_ref() {
|
239 + | "partitions" => {
|
240 + | resolver = Some(PartitionResolver::from_partitions(deser_partitions(tokens)?));
|
241 + | }
|
242 + | _ => skip_value(tokens)?,
|
243 + | },
|
244 + | other => return Err(DeserializeError::custom(format!("expected object key or end object, found: {other:?}",))),
|
245 + | }
|
246 + | }
|
247 + | if tokens.next().is_some() {
|
248 + | return Err(DeserializeError::custom("found more JSON tokens after completing parsing"));
|
249 + | }
|
250 + | resolver.ok_or_else(|| DeserializeError::custom("did not find partitions array"))
|
251 + | }
|
252 + |
|
253 + | fn deser_partitions<'a, I>(tokens: &mut std::iter::Peekable<I>) -> Result<Vec<PartitionMetadata>, DeserializeError>
|
254 + | where
|
255 + | I: Iterator<Item = Result<Token<'a>, DeserializeError>>,
|
256 + | {
|
257 + | match tokens.next().transpose()? {
|
258 + | Some(Token::StartArray { .. }) => {
|
259 + | let mut items = Vec::new();
|
260 + | loop {
|
261 + | match tokens.peek() {
|
262 + | Some(Ok(Token::EndArray { .. })) => {
|
263 + | tokens.next().transpose().unwrap();
|
264 + | break;
|
265 + | }
|
266 + | _ => {
|
267 + | items.push(deser_partition(tokens)?);
|
268 + | }
|
269 + | }
|
270 + | }
|
271 + | Ok(items)
|
272 + | }
|
273 + | _ => Err(DeserializeError::custom("expected start array")),
|
274 + | }
|
275 + | }
|
276 + |
|
277 + | pub(crate) fn deser_partition<'a, I>(tokens: &mut std::iter::Peekable<I>) -> Result<PartitionMetadata, DeserializeError>
|
278 + | where
|
279 + | I: Iterator<Item = Result<Token<'a>, DeserializeError>>,
|
280 + | {
|
281 + | match tokens.next().transpose()? {
|
282 + | Some(Token::StartObject { .. }) => {
|
283 + | let mut builder = PartitionMetadataBuilder::default();
|
284 + | loop {
|
285 + | match tokens.next().transpose()? {
|
286 + | Some(Token::EndObject { .. }) => break,
|
287 + | Some(Token::ObjectKey { key, .. }) => match key.to_unescaped()?.as_ref() {
|
288 + | "id" => {
|
289 + | builder.id = token_to_str(tokens.next())?;
|
290 + | }
|
291 + | "regionRegex" => {
|
292 + | builder.region_regex = token_to_str(tokens.next())?
|
293 + | .map(|region_regex| Regex::new(®ion_regex))
|
294 + | .transpose()
|
295 + | .map_err(|_e| DeserializeError::custom("invalid regex"))?;
|
296 + | }
|
297 + | "regions" => {
|
298 + | builder.regions = deser_explicit_regions(tokens)?;
|
299 + | }
|
300 + | "outputs" => {
|
301 + | builder.outputs = deser_outputs(tokens)?;
|
302 + | }
|
303 + | _ => skip_value(tokens)?,
|
304 + | },
|
305 + | other => return Err(DeserializeError::custom(format!("expected object key or end object, found: {other:?}"))),
|
306 + | }
|
307 + | }
|
308 + | Ok(builder.build())
|
309 + | }
|
310 + | _ => Err(DeserializeError::custom("expected start object")),
|
311 + | }
|
312 + | }
|
313 + |
|
314 + | #[allow(clippy::type_complexity, non_snake_case)]
|
315 + | pub(crate) fn deser_explicit_regions<'a, I>(
|
316 + | tokens: &mut std::iter::Peekable<I>,
|
317 + | ) -> Result<HashMap<super::Str, PartitionOutputOverride>, DeserializeError>
|
318 + | where
|
319 + | I: Iterator<Item = Result<Token<'a>, DeserializeError>>,
|
320 + | {
|
321 + | match tokens.next().transpose()? {
|
322 + | Some(Token::StartObject { .. }) => {
|
323 + | let mut map = HashMap::new();
|
324 + | loop {
|
325 + | match tokens.next().transpose()? {
|
326 + | Some(Token::EndObject { .. }) => break,
|
327 + | Some(Token::ObjectKey { key, .. }) => {
|
328 + | let key = key.to_unescaped().map(|u| u.into_owned())?;
|
329 + | let value = deser_outputs(tokens)?;
|
330 + | if let Some(value) = value {
|
331 + | map.insert(key.into(), value);
|
332 + | }
|
333 + | }
|
334 + | other => return Err(DeserializeError::custom(format!("expected object key or end object, found: {other:?}"))),
|
335 + | }
|
336 + | }
|
337 + | Ok(map)
|
338 + | }
|
339 + | _ => Err(DeserializeError::custom("expected start object")),
|
340 + | }
|
341 + | }
|
342 + |
|
343 + | /// Convert a token to `Str` (a potentially static String)
|
344 + | fn token_to_str(token: Option<Result<Token, DeserializeError>>) -> Result<Option<super::Str>, DeserializeError> {
|
345 + | Ok(expect_string_or_null(token)?
|
346 + | .map(|s| s.to_unescaped().map(|u| u.into_owned()))
|
347 + | .transpose()?
|
348 + | .map(Cow::Owned))
|
349 + | }
|
350 + |
|
351 + | fn deser_outputs<'a, I>(tokens: &mut std::iter::Peekable<I>) -> Result<Option<PartitionOutputOverride>, DeserializeError>
|
352 + | where
|
353 + | I: Iterator<Item = Result<Token<'a>, DeserializeError>>,
|
354 + | {
|
355 + | match tokens.next().transpose()? {
|
356 + | Some(Token::StartObject { .. }) => {
|
357 + | #[allow(unused_mut)]
|
358 + | let mut builder = PartitionOutputOverride::default();
|
359 + | loop {
|
360 + | match tokens.next().transpose()? {
|
361 + | Some(Token::EndObject { .. }) => break,
|
362 + | Some(Token::ObjectKey { key, .. }) => match key.to_unescaped()?.as_ref() {
|
363 + | "name" => {
|
364 + | builder.name = token_to_str(tokens.next())?;
|
365 + | }
|
366 + | "dnsSuffix" => {
|
367 + | builder.dns_suffix = token_to_str(tokens.next())?;
|
368 + | }
|
369 + | "dualStackDnsSuffix" => {
|
370 + | builder.dual_stack_dns_suffix = token_to_str(tokens.next())?;
|
371 + | }
|
372 + | "supportsFIPS" => {
|
373 + | builder.supports_fips = expect_bool_or_null(tokens.next())?;
|
374 + | }
|
375 + | "supportsDualStack" => {
|
376 + | builder.supports_dual_stack = expect_bool_or_null(tokens.next())?;
|
377 + | }
|
378 + | "implicitGlobalRegion" => {
|
379 + | builder.implicit_global_region = token_to_str(tokens.next())?;
|
380 + | }
|
381 + | _ => skip_value(tokens)?,
|
382 + | },
|
383 + | other => return Err(DeserializeError::custom(format!("expected object key or end object, found: {other:?}",))),
|
384 + | }
|
385 + | }
|
386 + | Ok(Some(builder))
|
387 + | }
|
388 + | _ => Err(DeserializeError::custom("expected start object")),
|
389 + | }
|
390 + | }
|
391 + | }
|
392 + |
|
393 + | #[cfg(test)]
|
394 + | mod test {
|
395 + | use crate::endpoint_lib::diagnostic::DiagnosticCollector;
|
396 + | use crate::endpoint_lib::partition::{Partition, PartitionMetadata, PartitionOutput, PartitionOutputOverride, PartitionResolver};
|
397 + | use regex_lite::Regex;
|
398 + | use std::collections::HashMap;
|
399 + |
|
400 + | fn resolve<'a>(resolver: &'a PartitionResolver, region: &str) -> Partition<'a> {
|
401 + | resolver
|
402 + | .resolve_partition(region, &mut DiagnosticCollector::new())
|
403 + | .expect("could not resolve partition")
|
404 + | }
|
405 + |
|
406 + | #[test]
|
407 + | fn deserialize_partitions() {
|
408 + | let partitions = r#"{
|
409 + | "version": "1.1",
|
410 + | "partitions": [
|
411 + | {
|
412 + | "id": "aws",
|
413 + | "regionRegex": "^(us|eu|ap|sa|ca|me|af)-\\w+-\\d+$",
|
414 + | "regions": {
|
415 + | "af-south-1": {},
|
416 + | "af-east-1": {},
|
417 + | "ap-northeast-1": {},
|
418 + | "ap-northeast-2": {},
|
419 + | "ap-northeast-3": {},
|
420 + | "ap-south-1": {},
|
421 + | "ap-southeast-1": {},
|
422 + | "ap-southeast-2": {},
|
423 + | "ap-southeast-3": {},
|
424 + | "ca-central-1": {},
|
425 + | "eu-central-1": {},
|
426 + | "eu-north-1": {},
|
427 + | "eu-south-1": {},
|
428 + | "eu-west-1": {},
|
429 + | "eu-west-2": {},
|
430 + | "eu-west-3": {},
|
431 + | "me-south-1": {},
|
432 + | "sa-east-1": {},
|
433 + | "us-east-1": {},
|
434 + | "us-east-2": {},
|
435 + | "us-west-1": {},
|
436 + | "us-west-2": {},
|
437 + | "aws-global": {}
|
438 + | },
|
439 + | "outputs": {
|
440 + | "name": "aws",
|
441 + | "dnsSuffix": "amazonaws.com",
|
442 + | "dualStackDnsSuffix": "api.aws",
|
443 + | "supportsFIPS": true,
|
444 + | "supportsDualStack": true,
|
445 + | "implicitGlobalRegion": "us-east-1"
|
446 + | }
|
447 + | },
|
448 + | {
|
449 + | "id": "aws-us-gov",
|
450 + | "regionRegex": "^us\\-gov\\-\\w+\\-\\d+$",
|
451 + | "regions": {
|
452 + | "us-gov-west-1": {},
|
453 + | "us-gov-east-1": {},
|
454 + | "aws-us-gov-global": {}
|
455 + | },
|
456 + | "outputs": {
|
457 + | "name": "aws-us-gov",
|
458 + | "dnsSuffix": "amazonaws.com",
|
459 + | "dualStackDnsSuffix": "api.aws",
|
460 + | "supportsFIPS": true,
|
461 + | "supportsDualStack": true,
|
462 + | "implicitGlobalRegion": "us-gov-east-1"
|
463 + | }
|
464 + | },
|
465 + | {
|
466 + | "id": "aws-cn",
|
467 + | "regionRegex": "^cn\\-\\w+\\-\\d+$",
|
468 + | "regions": {
|
469 + | "cn-north-1": {},
|
470 + | "cn-northwest-1": {},
|
471 + | "aws-cn-global": {}
|
472 + | },
|
473 + | "outputs": {
|
474 + | "name": "aws-cn",
|
475 + | "dnsSuffix": "amazonaws.com.cn",
|
476 + | "dualStackDnsSuffix": "api.amazonwebservices.com.cn",
|
477 + | "supportsFIPS": true,
|
478 + | "supportsDualStack": true,
|
479 + | "implicitGlobalRegion": "cn-north-1"
|
480 + | }
|
481 + | },
|
482 + | {
|
483 + | "id": "aws-iso",
|
484 + | "regionRegex": "^us\\-iso\\-\\w+\\-\\d+$",
|
485 + | "outputs": {
|
486 + | "name": "aws-iso",
|
487 + | "dnsSuffix": "c2s.ic.gov",
|
488 + | "supportsFIPS": true,
|
489 + | "supportsDualStack": false,
|
490 + | "dualStackDnsSuffix": "c2s.ic.gov",
|
491 + | "implicitGlobalRegion": "us-iso-foo-1"
|
492 + | },
|
493 + | "regions": {}
|
494 + | },
|
495 + | {
|
496 + | "id": "aws-iso-b",
|
497 + | "regionRegex": "^us\\-isob\\-\\w+\\-\\d+$",
|
498 + | "outputs": {
|
499 + | "name": "aws-iso-b",
|
500 + | "dnsSuffix": "sc2s.sgov.gov",
|
501 + | "supportsFIPS": true,
|
502 + | "supportsDualStack": false,
|
503 + | "dualStackDnsSuffix": "sc2s.sgov.gov",
|
504 + | "implicitGlobalRegion": "us-isob-foo-1"
|
505 + | },
|
506 + | "regions": {}
|
507 + | }
|
508 + | ]
|
509 + | }"#;
|
510 + | let resolver = super::deser::deserialize_partitions(partitions.as_bytes()).expect("valid resolver");
|
511 + | assert_eq!(resolve(&resolver, "cn-north-1").name, "aws-cn");
|
512 + | assert_eq!(resolve(&resolver, "cn-north-1").dns_suffix, "amazonaws.com.cn");
|
513 + | assert_eq!(resolver.partitions.len(), 5);
|
514 + | assert_eq!(resolve(&resolver, "af-south-1").implicit_global_region, "us-east-1");
|
515 + | }
|
516 + |
|
517 + | #[test]
|
518 + | fn resolve_partitions() {
|
519 + | let mut resolver = PartitionResolver::empty();
|
520 + | let new_suffix = PartitionOutputOverride {
|
521 + | dns_suffix: Some("mars.aws".into()),
|
522 + | ..Default::default()
|
523 + | };
|
524 + | resolver.add_partition(PartitionMetadata {
|
525 + | id: "aws".into(),
|
526 + | region_regex: Regex::new("^(us|eu|ap|sa|ca|me|af)-\\w+-\\d+$").unwrap(),
|
527 + | regions: HashMap::from([("mars-east-2".into(), new_suffix)]),
|
528 + | outputs: PartitionOutput {
|
529 + | name: "aws".into(),
|
530 + | dns_suffix: "amazonaws.com".into(),
|
531 + | dual_stack_dns_suffix: "api.aws".into(),
|
532 + | supports_fips: true,
|
533 + | supports_dual_stack: true,
|
534 + | implicit_global_region: "us-east-1".into(),
|
535 + | },
|
536 + | });
|
537 + | resolver.add_partition(PartitionMetadata {
|
538 + | id: "other".into(),
|
539 + | region_regex: Regex::new("^(other)-\\w+-\\d+$").unwrap(),
|
540 + | regions: Default::default(),
|
541 + | outputs: PartitionOutput {
|
542 + | name: "other".into(),
|
543 + | dns_suffix: "other.amazonaws.com".into(),
|
544 + | dual_stack_dns_suffix: "other.aws".into(),
|
545 + | supports_fips: false,
|
546 + | supports_dual_stack: true,
|
547 + | implicit_global_region: "other-south-2".into(),
|
548 + | },
|
549 + | });
|
550 + | assert_eq!(resolve(&resolver, "us-east-1").name, "aws");
|
551 + | assert_eq!(resolve(&resolver, "other-west-2").name, "other");
|
552 + | // mars-east-1 hits aws through the default fallback
|
553 + | assert_eq!(resolve(&resolver, "mars-east-1").dns_suffix, "amazonaws.com");
|
554 + | // mars-east-2 hits aws through the region override
|
555 + | assert_eq!(resolve(&resolver, "mars-east-2").dns_suffix, "mars.aws");
|
556 + | }
|
557 + | }
|