aws_sdk_s3/endpoint_lib/
arn.rs

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
7use crate::endpoint_lib::diagnostic::DiagnosticCollector;
8use std::borrow::Cow;
9use std::error::Error;
10use std::fmt::{Display, Formatter};
11
12#[derive(Debug, Eq, PartialEq)]
13pub(crate) struct Arn<'a> {
14    partition: &'a str,
15    service: &'a str,
16    region: &'a str,
17    account_id: &'a str,
18    resource_id: Vec<&'a str>,
19}
20
21#[allow(unused)]
22impl<'a> Arn<'a> {
23    pub(crate) fn partition(&self) -> &'a str {
24        self.partition
25    }
26    pub(crate) fn service(&self) -> &'a str {
27        self.service
28    }
29    pub(crate) fn region(&self) -> &'a str {
30        self.region
31    }
32    pub(crate) fn account_id(&self) -> &'a str {
33        self.account_id
34    }
35    pub(crate) fn resource_id(&self) -> &Vec<&'a str> {
36        &self.resource_id
37    }
38}
39
40#[derive(Debug, PartialEq)]
41pub(crate) struct InvalidArn {
42    message: Cow<'static, str>,
43}
44
45impl InvalidArn {
46    fn from_static(message: &'static str) -> InvalidArn {
47        Self {
48            message: Cow::Borrowed(message),
49        }
50    }
51}
52impl Display for InvalidArn {
53    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
54        write!(f, "{}", self.message)
55    }
56}
57impl Error for InvalidArn {}
58
59impl<'a> Arn<'a> {
60    pub(crate) fn parse(arn: &'a str) -> Result<Self, InvalidArn> {
61        let mut split = arn.splitn(6, ':');
62        let invalid_format = || InvalidArn::from_static("ARN must have 6 components delimited by `:`");
63        let arn = split.next().ok_or_else(invalid_format)?;
64        let partition = split.next().ok_or_else(invalid_format)?;
65        let service = split.next().ok_or_else(invalid_format)?;
66        let region = split.next().ok_or_else(invalid_format)?;
67        let account_id = split.next().ok_or_else(invalid_format)?;
68        let resource_id = split.next().ok_or_else(invalid_format)?;
69
70        if arn != "arn" {
71            return Err(InvalidArn::from_static("first component of the ARN must be `arn`"));
72        }
73        if partition.is_empty() || service.is_empty() || resource_id.is_empty() {
74            return Err(InvalidArn::from_static("partition, service, and resource id must all be non-empty"));
75        }
76
77        let resource_id = resource_id.split([':', '/']).collect::<Vec<_>>();
78        Ok(Self {
79            partition,
80            service,
81            region,
82            account_id,
83            resource_id,
84        })
85    }
86}
87
88pub(crate) fn parse_arn<'a>(input: &'a str, e: &mut DiagnosticCollector) -> Option<Arn<'a>> {
89    e.capture(Arn::parse(input))
90}
91
92#[cfg(test)]
93mod test {
94    use super::Arn;
95
96    #[test]
97    fn arn_parser() {
98        let arn = "arn:aws:s3:us-east-2:012345678:outpost:op-1234";
99        let parsed = Arn::parse(arn).expect("valid ARN");
100        assert_eq!(
101            parsed,
102            Arn {
103                partition: "aws",
104                service: "s3",
105                region: "us-east-2",
106                account_id: "012345678",
107                resource_id: vec!["outpost", "op-1234"]
108            }
109        );
110    }
111
112    #[test]
113    fn allow_slash_arns() {
114        let arn = "arn:aws:s3:us-east-2:012345678:outpost/op-1234";
115        let parsed = Arn::parse(arn).expect("valid ARN");
116        assert_eq!(
117            parsed,
118            Arn {
119                partition: "aws",
120                service: "s3",
121                region: "us-east-2",
122                account_id: "012345678",
123                resource_id: vec!["outpost", "op-1234"]
124            }
125        );
126    }
127
128    #[test]
129    fn resource_id_must_be_nonempty() {
130        let arn = "arn:aws:s3:us-east-2:012345678:";
131        Arn::parse(arn).expect_err("empty resource");
132    }
133
134    #[test]
135    fn arns_with_empty_parts() {
136        let arn = "arn:aws:s3:::my_corporate_bucket/Development/*";
137        assert_eq!(
138            Arn::parse(arn).expect("valid arn"),
139            Arn {
140                partition: "aws",
141                service: "s3",
142                region: "",
143                account_id: "",
144                resource_id: vec!["my_corporate_bucket", "Development", "*"]
145            }
146        );
147    }
148}