use crate::Error;
use crate::parts::ArnParts;
#[derive(Debug, PartialEq, Clone)]
pub struct Arn<'a> {
pub partition: &'a str,
pub service: &'a str,
pub region: &'a str,
pub account: &'a str,
pub resource_type: &'a str,
pub resource_id: &'a str,
pub resource_revision: &'a str,
pub has_path: bool,
}
#[derive(Debug, PartialEq, Clone)]
pub struct ArnOwned {
pub partition: String,
pub service: String,
pub region: String,
pub account: String,
pub resource_type: String,
pub resource_id: String,
pub resource_revision: String,
pub has_path: bool,
}
impl<'a> ArnParts<'a> for Arn<'a> {
fn partition(&self) -> &str {
self.partition
}
fn service(&self) -> &str {
self.service
}
fn region(&self) -> &str {
self.region
}
fn account(&self) -> &str {
self.account
}
fn resource_revision(&self) -> &str {
self.resource_revision
}
fn resource_type(&self) -> &str {
self.resource_type
}
fn resource_id(&self) -> &str {
self.resource_id
}
fn has_path(&self) -> bool {
self.has_path
}
}
impl ArnParts<'static> for ArnOwned {
fn partition(&self) -> &str {
self.partition.as_str()
}
fn service(&self) -> &str {
self.service.as_str()
}
fn region(&self) -> &str {
self.region.as_str()
}
fn account(&self) -> &str {
self.account.as_str()
}
fn resource_revision(&self) -> &str {
self.resource_revision.as_str()
}
fn resource_type(&self) -> &str {
self.resource_type.as_str()
}
fn resource_id(&self) -> &str {
self.resource_id.as_str()
}
fn has_path(&self) -> bool {
self.has_path
}
}
impl<'a> PartialEq<ArnOwned> for Arn<'a> {
fn eq(&self, other: &ArnOwned) -> bool {
self.partition() == other.partition()
&& self.service() == other.service()
&& self.region() == other.region()
&& self.account() == other.account()
&& self.resource_type() == other.resource_type()
&& self.resource_id() == other.resource_id()
&& self.resource_revision() == other.resource_revision()
&& self.has_path() == other.has_path()
}
}
impl<'a> PartialEq<Arn<'a>> for ArnOwned {
fn eq(&self, other: &Arn<'a>) -> bool {
other == self
}
}
impl<'a> Arn<'a> {
pub fn to_owned(&self) -> ArnOwned {
ArnOwned {
partition: self.partition.to_owned(),
service: self.service.to_owned(),
region: self.region.to_owned(),
account: self.account.to_owned(),
resource_type: self.resource_type.to_owned(),
resource_id: self.resource_id.to_owned(),
resource_revision: self.resource_revision.to_owned(),
has_path: self.has_path,
}
}
pub fn new(arn_str: &'a str) -> Result<Self, Error> {
let arn_str = arn_str.trim();
if arn_str.len() > 2048 {
return Err(Error::TooLong);
}
if !arn_str
.chars()
.all(|c| c.is_ascii_alphanumeric() || ":/+=,.@_*#-".contains(c))
{
return Err(Error::BadCharacters);
}
let arn: Arn<'a> = parser::parse(arn_str).map_err(|_| Error::ParseError)?;
if !arn
.region()
.chars()
.all(|c| c.is_ascii_alphanumeric() || c == '-')
{
return Err(Error::BadCharacters);
}
Ok(arn)
}
}
mod parser {
use super::Arn;
use nom::{
IResult, Parser,
branch::alt,
bytes::complete::{tag, take_till},
character::complete::char,
combinator::{all_consuming, opt, rest, success},
sequence::{preceded, terminated},
};
fn terminated_by_colon(input: &str) -> IResult<&str, &str> {
terminated(take_till(|c| c == ':'), char(':')).parse(input)
}
fn terminated_by_colon_not_slash(input: &str) -> IResult<&str, &str> {
terminated(take_till(|c| c == ':' || c == '/'), char(':')).parse(input)
}
fn terminated_by_slash_not_colon(input: &str) -> IResult<&str, &str> {
terminated(take_till(|c| c == ':' || c == '/'), char('/')).parse(input)
}
pub fn parse<'a>(
input: &'a str,
) -> Result<super::Arn<'a>, nom::Err<nom::error::Error<&'a str>>> {
let (_, result) = all_consuming((
terminated(tag("arn"), char(':')),
terminated_by_colon, terminated_by_colon, terminated_by_colon, terminated_by_colon, alt((
(
terminated_by_slash_not_colon, terminated_by_colon_not_slash, rest, success(true), ),
(
terminated_by_colon_not_slash, rest, success(""), success(false), ),
(
preceded(opt(char('/')), terminated_by_slash_not_colon), rest, success(""), success(true), ),
(
success(""), rest, success(""), success(false), ),
)),
))
.parse(input)?;
let (
_,
partition,
service,
region,
account,
(resource_type, resource_id, resource_revision, has_path),
) = result;
Ok(Arn {
partition,
service,
region,
account,
resource_revision,
resource_type,
resource_id,
has_path,
})
}
}