Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 57403929b6 | |||
| ec3cad1a05 | |||
| ef0d9f4395 | |||
| 7bd24ff0e7 | |||
| fa161be227 | |||
| 465aa8dfab | |||
| 6766f04578 | |||
| 59830deeff | |||
| fad42e9763 | |||
| 7ac8b7c19d | |||
| fe233f9fca |
22
.github/workflows/rust.yml
vendored
Normal file
22
.github/workflows/rust.yml
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
name: Rust
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "main" ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ "main" ]
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Build
|
||||||
|
run: cargo build --verbose
|
||||||
|
- name: Run tests
|
||||||
|
run: cargo test --verbose
|
||||||
15
Cargo.toml
15
Cargo.toml
@@ -13,7 +13,7 @@ categories.workspace = true
|
|||||||
readme.workspace = true
|
readme.workspace = true
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.2.0"
|
version = "0.3.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.81"
|
rust-version = "1.81"
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ categories = ["api-bindings"]
|
|||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
aws-macros = { path = "./aws_macros", version = "0.2.*" }
|
aws-macros = { path = "./aws_macros", version = "0.3.*" }
|
||||||
aws-config = { version = "1.*", default-features = false }
|
aws-config = { version = "1.*", default-features = false }
|
||||||
aws-sdk-ec2 = { version = "1.*", default-features = false, features = [
|
aws-sdk-ec2 = { version = "1.*", default-features = false, features = [
|
||||||
"rustls",
|
"rustls",
|
||||||
@@ -56,8 +56,15 @@ chrono = { version = "0.4.*", default-features = false, features = [
|
|||||||
serde = { version = "1.*", default-features = false, features = [
|
serde = { version = "1.*", default-features = false, features = [
|
||||||
"std",
|
"std",
|
||||||
"derive",
|
"derive",
|
||||||
] }
|
], optional = true }
|
||||||
serde_json = { version = "1.*", default-features = false, features = ["std"] }
|
serde_json = { version = "1.*", default-features = false, features = [
|
||||||
|
"std",
|
||||||
|
], optional = true }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
serde = ["dep:serde"]
|
||||||
|
serde-tags = ["dep:serde", "dep:serde_json"]
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|||||||
5
Makefile
5
Makefile
@@ -7,11 +7,12 @@ docs:
|
|||||||
|
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
test:
|
test:
|
||||||
@cargo test --workspace --color=always
|
cargo hack --feature-powerset --no-dev-deps check
|
||||||
|
cargo test --workspace --color=always
|
||||||
|
|
||||||
.PHONY: lint
|
.PHONY: lint
|
||||||
lint:
|
lint:
|
||||||
@cargo clippy --workspace --tests --color=always
|
cargo clippy --workspace --tests --color=always
|
||||||
|
|
||||||
.PHONY: fmt
|
.PHONY: fmt
|
||||||
fmt:
|
fmt:
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
# AWS
|
# AWS
|
||||||
|
|
||||||
* Helper functions for frequently used AWS operations (create EC2 instance, Route53 record, ...)
|
- Helper functions for frequently used AWS operations (create EC2 instance,
|
||||||
* Type-safe operations on AWS tags (e.g. from an EC2 instance), see [the separate README](./src/tags/README.md)
|
Route53 record, ...)
|
||||||
|
- Type-safe operations on AWS tags (e.g. from an EC2 instance), see
|
||||||
|
[the separate README](./src/tags/README.md)
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ fn parse_transparent_enum(mut e: syn::ItemEnum) -> (Translator, syn::Item) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn transform(attr: TokenStream, item: TokenStream) -> TokenStream {
|
pub(crate) fn transform(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
let root = quote! {::aws};
|
let root = quote! {::aws_lib};
|
||||||
|
|
||||||
let expr: syn::Expr = syn::parse(attr).expect("expected expr in macro attribute");
|
let expr: syn::Expr = syn::parse(attr).expect("expected expr in macro attribute");
|
||||||
|
|
||||||
@@ -209,7 +209,7 @@ pub(crate) fn transform(attr: TokenStream, item: TokenStream) -> TokenStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<#name> for RawTagValue {
|
impl From<#name> for #root::tags::RawTagValue {
|
||||||
fn from(value: #name) -> Self {
|
fn from(value: #name) -> Self {
|
||||||
<#ty as #root::tags::TagValue<#ty>>::into_raw_tag(value.0)
|
<#ty as #root::tags::TagValue<#ty>>::into_raw_tag(value.0)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,26 @@ struct Element {
|
|||||||
ty: syn::Path,
|
ty: syn::Path,
|
||||||
kind: ElementKind,
|
kind: ElementKind,
|
||||||
name: String,
|
name: String,
|
||||||
|
attrs: Vec<syn::Attribute>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_cfg_attribute(attr: &syn::Attribute) -> bool {
|
||||||
|
match attr.meta {
|
||||||
|
syn::Meta::List(ref meta_list) => {
|
||||||
|
let segments = &meta_list.path.segments;
|
||||||
|
match (segments.first(), segments.len()) {
|
||||||
|
(Some(segment), 1) => segment.ident == "cfg",
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cfg_attrs(v: &[syn::Attribute]) -> Vec<&syn::Attribute> {
|
||||||
|
v.iter()
|
||||||
|
.filter(|attr: &&syn::Attribute| is_cfg_attribute(attr))
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_type(input: syn::Type) -> (syn::Path, ElementKind) {
|
fn parse_type(input: syn::Type) -> (syn::Path, ElementKind) {
|
||||||
@@ -66,22 +86,31 @@ fn parse_type(input: syn::Type) -> (syn::Path, ElementKind) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_field_attrs(attrs: &[syn::Attribute]) -> Option<String> {
|
fn parse_field_attrs(attrs: &mut Vec<syn::Attribute>) -> Option<String> {
|
||||||
match (attrs.first(), attrs.len()) {
|
let index_of_tag_attribute = attrs
|
||||||
(Some(attr), 1) => {
|
.iter()
|
||||||
assert!(
|
.enumerate()
|
||||||
attr.style == syn::AttrStyle::Outer,
|
.filter(|&(_i, attr)| attr.style == syn::AttrStyle::Outer)
|
||||||
"field attribute style needs to be an outer attribute"
|
.find_map(|(i, attr)| match attr.meta {
|
||||||
);
|
|
||||||
match attr.meta {
|
|
||||||
syn::Meta::List(ref meta_list) => {
|
syn::Meta::List(ref meta_list) => {
|
||||||
let tag = &meta_list.path;
|
let tag = &meta_list.path;
|
||||||
let tag_name = match (tag.segments.first(), tag.segments.len()) {
|
if let (Some(segment), 1) = (tag.segments.first(), tag.segments.len()) {
|
||||||
(Some(segment), 1) => segment.ident.to_string(),
|
if segment.ident == "tag" {
|
||||||
(_, 0) => return None,
|
Some((i, meta_list.clone()))
|
||||||
_ => panic!("invalid field attribute path"),
|
} else {
|
||||||
};
|
None
|
||||||
assert!(tag_name == "tag", "invalid field attribute path {tag_name}");
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
});
|
||||||
|
|
||||||
|
match index_of_tag_attribute {
|
||||||
|
Some((i, meta_list)) => {
|
||||||
|
let removed_attribute = attrs.remove(i);
|
||||||
|
drop(removed_attribute);
|
||||||
|
|
||||||
let expr: syn::Expr = match meta_list.parse_args() {
|
let expr: syn::Expr = match meta_list.parse_args() {
|
||||||
Ok(expr) => expr,
|
Ok(expr) => expr,
|
||||||
@@ -112,22 +141,18 @@ fn parse_field_attrs(attrs: &[syn::Attribute]) -> Option<String> {
|
|||||||
_ => panic!("right side of tag field attribute not a literal"),
|
_ => panic!("right side of tag field attribute not a literal"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => panic!("invalid field attribute"),
|
None => None,
|
||||||
}
|
|
||||||
}
|
|
||||||
(_, 0) => None,
|
|
||||||
_ => panic!("invalid field attributes"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_fields(input: impl IntoIterator<Item = syn::Field>) -> Vec<Element> {
|
fn parse_fields(input: impl IntoIterator<Item = syn::Field>) -> Vec<Element> {
|
||||||
let mut elements = Vec::new();
|
let mut elements = Vec::new();
|
||||||
for field in input {
|
for mut field in input {
|
||||||
let ident = field.ident.expect("tuple structs not supported");
|
let ident = field.ident.expect("tuple structs not supported");
|
||||||
let vis = field.vis;
|
let vis = field.vis;
|
||||||
let (ty, kind) = parse_type(field.ty);
|
let (ty, kind) = parse_type(field.ty);
|
||||||
|
|
||||||
let name = parse_field_attrs(&field.attrs);
|
let name = parse_field_attrs(&mut field.attrs);
|
||||||
|
|
||||||
elements.push(Element {
|
elements.push(Element {
|
||||||
ident: ident.clone(),
|
ident: ident.clone(),
|
||||||
@@ -135,6 +160,7 @@ fn parse_fields(input: impl IntoIterator<Item = syn::Field>) -> Vec<Element> {
|
|||||||
ty,
|
ty,
|
||||||
kind,
|
kind,
|
||||||
name: name.unwrap_or_else(|| ident.to_string()),
|
name: name.unwrap_or_else(|| ident.to_string()),
|
||||||
|
attrs: field.attrs,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
elements
|
elements
|
||||||
@@ -152,7 +178,7 @@ fn parse_struct(input: syn::ItemStruct) -> Input {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn build_output(input: Input) -> TokenStream {
|
fn build_output(input: Input) -> TokenStream {
|
||||||
let root = quote! { ::aws };
|
let root = quote! { ::aws_lib };
|
||||||
|
|
||||||
let ident = input.ident;
|
let ident = input.ident;
|
||||||
let vis = input.vis;
|
let vis = input.vis;
|
||||||
@@ -165,14 +191,19 @@ fn build_output(input: Input) -> TokenStream {
|
|||||||
let ident = &element.ident;
|
let ident = &element.ident;
|
||||||
let vis = &element.vis;
|
let vis = &element.vis;
|
||||||
let ty = &element.ty;
|
let ty = &element.ty;
|
||||||
|
let attrs = &element.attrs;
|
||||||
match element.kind {
|
match element.kind {
|
||||||
ElementKind::Required => {
|
ElementKind::Required => {
|
||||||
quote!(
|
quote!(
|
||||||
|
#(#attrs)
|
||||||
|
*
|
||||||
#vis #ident: #ty
|
#vis #ident: #ty
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
ElementKind::Optional => {
|
ElementKind::Optional => {
|
||||||
quote!(
|
quote!(
|
||||||
|
#(#attrs)
|
||||||
|
*
|
||||||
#vis #ident: ::std::option::Option<#ty>
|
#vis #ident: ::std::option::Option<#ty>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -191,9 +222,18 @@ fn build_output(input: Input) -> TokenStream {
|
|||||||
let params = input.elements.iter().map(|element| {
|
let params = input.elements.iter().map(|element| {
|
||||||
let ident = &element.ident;
|
let ident = &element.ident;
|
||||||
let ty = &element.ty;
|
let ty = &element.ty;
|
||||||
|
let attrs = cfg_attrs(&element.attrs);
|
||||||
match element.kind {
|
match element.kind {
|
||||||
ElementKind::Required => quote!(#ident: #ty),
|
ElementKind::Required => quote! {
|
||||||
ElementKind::Optional => quote!(#ident: ::std::option::Option<#ty>),
|
#(#attrs)
|
||||||
|
*
|
||||||
|
#ident: #ty
|
||||||
|
},
|
||||||
|
ElementKind::Optional => quote! {
|
||||||
|
#(#attrs)
|
||||||
|
*
|
||||||
|
#ident: ::std::option::Option<#ty>
|
||||||
|
},
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -202,16 +242,22 @@ fn build_output(input: Input) -> TokenStream {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|element| {
|
.map(|element| {
|
||||||
let ident = &element.ident;
|
let ident = &element.ident;
|
||||||
quote! {#ident: #ident}
|
let attrs = cfg_attrs(&element.attrs);
|
||||||
|
quote! {
|
||||||
|
#(#attrs)
|
||||||
|
*
|
||||||
|
#ident: #ident
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let from_tags_fields: Vec<proc_macro2::TokenStream>= input.elements.iter().map(|element| {
|
let from_tags_fields: Vec<proc_macro2::TokenStream> = input.elements.iter().map(|element| {
|
||||||
let ident = &element.ident;
|
let ident = &element.ident;
|
||||||
let ty = &element.ty;
|
let ty = &element.ty;
|
||||||
let tag_name = &element.name;
|
let tag_name = &element.name;
|
||||||
|
let attrs = cfg_attrs(&element.attrs);
|
||||||
|
|
||||||
let try_convert = quote!{
|
let try_convert = quote! {
|
||||||
let value: ::std::result::Result<#ty, #root::tags::ParseTagsError> = <#ty as #root::tags::TagValue<#ty>>::from_raw_tag(value)
|
let value: ::std::result::Result<#ty, #root::tags::ParseTagsError> = <#ty as #root::tags::TagValue<#ty>>::from_raw_tag(value)
|
||||||
.map_err(
|
.map_err(
|
||||||
|e| #root::tags::ParseTagsError::ParseTag(#root::tags::ParseTagError::InvalidTagValue {
|
|e| #root::tags::ParseTagsError::ParseTag(#root::tags::ParseTagError::InvalidTagValue {
|
||||||
@@ -261,6 +307,8 @@ fn build_output(input: Input) -> TokenStream {
|
|||||||
};
|
};
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
|
#(#attrs)
|
||||||
|
*
|
||||||
#ident: {
|
#ident: {
|
||||||
let key: #root::tags::TagKey = #root::tags::TagKey::new(#tag_name.to_owned());
|
let key: #root::tags::TagKey = #root::tags::TagKey::new(#tag_name.to_owned());
|
||||||
|
|
||||||
@@ -286,9 +334,12 @@ fn build_output(input: Input) -> TokenStream {
|
|||||||
let ident = &element.ident;
|
let ident = &element.ident;
|
||||||
let ty= &element.ty;
|
let ty= &element.ty;
|
||||||
let tag_name = &element.name;
|
let tag_name = &element.name;
|
||||||
|
let attrs= &element.attrs;
|
||||||
match element.kind {
|
match element.kind {
|
||||||
ElementKind::Required => {
|
ElementKind::Required => {
|
||||||
quote! {
|
quote! {
|
||||||
|
#(#attrs)
|
||||||
|
*
|
||||||
{
|
{
|
||||||
let key = #root::tags::TagKey::new(#tag_name.to_owned());
|
let key = #root::tags::TagKey::new(#tag_name.to_owned());
|
||||||
let value: #root::tags::RawTagValue = <#ty as #root::tags::TagValue<#ty>>::into_raw_tag(self.#ident);
|
let value: #root::tags::RawTagValue = <#ty as #root::tags::TagValue<#ty>>::into_raw_tag(self.#ident);
|
||||||
@@ -298,6 +349,8 @@ fn build_output(input: Input) -> TokenStream {
|
|||||||
}
|
}
|
||||||
ElementKind::Optional => {
|
ElementKind::Optional => {
|
||||||
quote! {
|
quote! {
|
||||||
|
#(#attrs)
|
||||||
|
*
|
||||||
{
|
{
|
||||||
match self.#ident {
|
match self.#ident {
|
||||||
::std::option::Option::Some(value) => {
|
::std::option::Option::Some(value) => {
|
||||||
|
|||||||
85
src/lib.rs
85
src/lib.rs
@@ -1,6 +1,6 @@
|
|||||||
//! Provides an opinionated interface to the AWS API
|
//! Provides an opinionated interface to the AWS API
|
||||||
|
|
||||||
extern crate self as aws;
|
extern crate self as aws_lib;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fmt::{self, Debug},
|
fmt::{self, Debug},
|
||||||
@@ -11,6 +11,7 @@ use std::{
|
|||||||
use aws_config::retry::RetryConfig;
|
use aws_config::retry::RetryConfig;
|
||||||
use aws_sdk_ec2::client::Waiters;
|
use aws_sdk_ec2::client::Waiters;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
mod error;
|
mod error;
|
||||||
@@ -40,6 +41,7 @@ macro_rules! wrap_aws_enum {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
impl Serialize for $name {
|
impl Serialize for $name {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
where
|
where
|
||||||
@@ -49,6 +51,7 @@ macro_rules! wrap_aws_enum {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
impl<'de> Deserialize<'de> for $name {
|
impl<'de> Deserialize<'de> for $name {
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
where
|
where
|
||||||
@@ -196,11 +199,12 @@ impl Instance {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub enum Region {
|
pub enum Region {
|
||||||
#[serde(rename = "eu-central-1")]
|
#[cfg_attr(feature = "serde", serde(rename = "eu-central-1"))]
|
||||||
EuCentral1,
|
EuCentral1,
|
||||||
#[serde(rename = "us-east-1")]
|
#[cfg_attr(feature = "serde", serde(rename = "us-east-1"))]
|
||||||
UsEast1,
|
UsEast1,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,7 +273,8 @@ pub struct RegionClient {
|
|||||||
pub cdn: RegionClientCdn,
|
pub cdn: RegionClientCdn,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct InstanceProfileName(String);
|
pub struct InstanceProfileName(String);
|
||||||
|
|
||||||
impl InstanceProfileName {
|
impl InstanceProfileName {
|
||||||
@@ -278,7 +283,8 @@ impl InstanceProfileName {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct InstanceKeypairName(String);
|
pub struct InstanceKeypairName(String);
|
||||||
|
|
||||||
impl InstanceKeypairName {
|
impl InstanceKeypairName {
|
||||||
@@ -287,7 +293,8 @@ impl InstanceKeypairName {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct SecurityGroupId(String);
|
pub struct SecurityGroupId(String);
|
||||||
|
|
||||||
impl SecurityGroupId {
|
impl SecurityGroupId {
|
||||||
@@ -296,12 +303,14 @@ impl SecurityGroupId {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct SecurityGroup {
|
pub struct SecurityGroup {
|
||||||
id: SecurityGroupId,
|
id: SecurityGroupId,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct SubnetId(String);
|
pub struct SubnetId(String);
|
||||||
|
|
||||||
impl SubnetId {
|
impl SubnetId {
|
||||||
@@ -328,8 +337,9 @@ impl fmt::Display for SubnetId {
|
|||||||
|
|
||||||
macro_rules! string_newtype {
|
macro_rules! string_newtype {
|
||||||
($name:ident) => {
|
($name:ident) => {
|
||||||
#[Tag(translate = serde)]
|
#[Tag(translate = transparent)]
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub struct $name(String);
|
pub struct $name(String);
|
||||||
|
|
||||||
impl std::fmt::Display for $name {
|
impl std::fmt::Display for $name {
|
||||||
@@ -342,7 +352,8 @@ macro_rules! string_newtype {
|
|||||||
|
|
||||||
string_newtype!(AvailabilityZone);
|
string_newtype!(AvailabilityZone);
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct Subnet {
|
pub struct Subnet {
|
||||||
pub id: SubnetId,
|
pub id: SubnetId,
|
||||||
pub availability_zone: AvailabilityZone,
|
pub availability_zone: AvailabilityZone,
|
||||||
@@ -383,7 +394,8 @@ impl AmiId {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Ami {
|
pub struct Ami {
|
||||||
pub id: AmiId,
|
pub id: AmiId,
|
||||||
pub tags: TagList,
|
pub tags: TagList,
|
||||||
@@ -411,7 +423,8 @@ impl TryFrom<aws_sdk_ec2::types::Image> for Ami {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[Tag(translate = manual)]
|
#[Tag(translate = manual)]
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
|
||||||
pub struct Timestamp(DateTime<Utc>);
|
pub struct Timestamp(DateTime<Utc>);
|
||||||
|
|
||||||
impl Timestamp {
|
impl Timestamp {
|
||||||
@@ -468,7 +481,8 @@ impl TryFrom<RawImageCreationDate> for Timestamp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct Ip(net::IpAddr);
|
pub struct Ip(net::IpAddr);
|
||||||
|
|
||||||
impl Ip {
|
impl Ip {
|
||||||
@@ -499,7 +513,8 @@ impl EipAllocationId {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Eip {
|
pub struct Eip {
|
||||||
pub allocation_id: EipAllocationId,
|
pub allocation_id: EipAllocationId,
|
||||||
pub ip: Ip,
|
pub ip: Ip,
|
||||||
@@ -568,8 +583,9 @@ impl Eip {
|
|||||||
|
|
||||||
string_newtype!(CloudfrontDistributionId);
|
string_newtype!(CloudfrontDistributionId);
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[derive(Debug, Clone)]
|
||||||
|
#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
|
||||||
pub enum CloudfrontDistributionStatus {
|
pub enum CloudfrontDistributionStatus {
|
||||||
Deployed,
|
Deployed,
|
||||||
Other(String),
|
Other(String),
|
||||||
@@ -597,10 +613,12 @@ impl From<String> for CloudfrontDistributionStatus {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct EfsId(String);
|
pub struct EfsId(String);
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct Efs {
|
pub struct Efs {
|
||||||
id: EfsId,
|
id: EfsId,
|
||||||
region: Region,
|
region: Region,
|
||||||
@@ -633,7 +651,8 @@ impl From<String> for CloudfrontDistributionDomain {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct CloudfrontOrigin {
|
pub struct CloudfrontOrigin {
|
||||||
id: CloudfrontOriginId,
|
id: CloudfrontOriginId,
|
||||||
domain: CloudfrontOriginDomain,
|
domain: CloudfrontOriginDomain,
|
||||||
@@ -643,6 +662,10 @@ impl CloudfrontOrigin {
|
|||||||
pub const fn id(&self) -> &CloudfrontOriginId {
|
pub const fn id(&self) -> &CloudfrontOriginId {
|
||||||
&self.id
|
&self.id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const fn domain(&self) -> &CloudfrontOriginDomain {
|
||||||
|
&self.domain
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string_newtype!(CloudfrontOriginId);
|
string_newtype!(CloudfrontOriginId);
|
||||||
@@ -675,7 +698,8 @@ impl From<aws_sdk_cloudfront::types::Origin> for CloudfrontOrigin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct CloudfrontDistribution {
|
pub struct CloudfrontDistribution {
|
||||||
pub id: CloudfrontDistributionId,
|
pub id: CloudfrontDistributionId,
|
||||||
pub status: CloudfrontDistributionStatus,
|
pub status: CloudfrontDistributionStatus,
|
||||||
@@ -789,7 +813,8 @@ pub async fn load_sdk_clients<const C: usize>(
|
|||||||
region_clients
|
region_clients
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct Account {
|
pub struct Account {
|
||||||
id: String,
|
id: String,
|
||||||
}
|
}
|
||||||
@@ -804,7 +829,8 @@ impl Account {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct HostedZoneId(String);
|
pub struct HostedZoneId(String);
|
||||||
|
|
||||||
impl HostedZoneId {
|
impl HostedZoneId {
|
||||||
@@ -817,7 +843,8 @@ impl HostedZoneId {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct Route53Zone {
|
pub struct Route53Zone {
|
||||||
hosted_zone_id: HostedZoneId,
|
hosted_zone_id: HostedZoneId,
|
||||||
name: String,
|
name: String,
|
||||||
@@ -846,6 +873,14 @@ impl Route53Zone {
|
|||||||
name,
|
name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const fn hosted_zone_id(&self) -> &HostedZoneId {
|
||||||
|
&self.hosted_zone_id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name(&self) -> &str {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<aws_sdk_route53::types::HostedZone> for Route53Zone {
|
impl From<aws_sdk_route53::types::HostedZone> for Route53Zone {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ methods to create a struct instance for an instance [`TagList`], and turn a
|
|||||||
struct instance back into a [`TagList`]:
|
struct instance back into a [`TagList`]:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use aws::tags::{Tags, TagList, RawTag};
|
use aws_lib::tags::{Tags, TagList, RawTag};
|
||||||
|
|
||||||
#[Tags]
|
#[Tags]
|
||||||
struct MyTags {
|
struct MyTags {
|
||||||
@@ -55,15 +55,11 @@ There is a [`macro@Tag`] macro that selects the strategy, which can then be used
|
|||||||
in a struct that is using `#[Tags]`:
|
in a struct that is using `#[Tags]`:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use aws::tags::{Tag, Tags};
|
use aws_lib::tags::{Tag, Tags};
|
||||||
use serde::{Serialize, Deserialize};
|
|
||||||
|
|
||||||
#[Tag(translate = serde)]
|
#[Tag(translate = transparent)]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
struct MyTag {
|
struct MyTag(String);
|
||||||
foo: String,
|
|
||||||
bar: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[Tags]
|
#[Tags]
|
||||||
struct MyTags {
|
struct MyTags {
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
#![doc = include_str!("README.md")]
|
#![doc = include_str!("README.md")]
|
||||||
use std::fmt::{self, Debug};
|
use std::fmt::{self, Debug};
|
||||||
|
|
||||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
#[cfg(feature = "serde-tags")]
|
||||||
|
use serde::de::DeserializeOwned;
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
use serde::Deserialize;
|
||||||
|
#[cfg(any(feature = "serde-tags", feature = "serde"))]
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
mod error;
|
mod error;
|
||||||
mod helpers;
|
mod helpers;
|
||||||
@@ -43,10 +48,12 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
|
||||||
pub struct RawTagValue(String);
|
pub struct RawTagValue(String);
|
||||||
helpers::impl_string_wrapper!(RawTagValue);
|
helpers::impl_string_wrapper!(RawTagValue);
|
||||||
|
|
||||||
|
#[cfg(feature = "serde-tags")]
|
||||||
pub struct TranslateSerde;
|
pub struct TranslateSerde;
|
||||||
pub struct TranslateManual;
|
pub struct TranslateManual;
|
||||||
|
|
||||||
@@ -57,6 +64,7 @@ pub trait Translator<S: ?Sized, T> {
|
|||||||
fn into_raw_tag(value: T) -> RawTagValue;
|
fn into_raw_tag(value: T) -> RawTagValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde-tags")]
|
||||||
pub trait TranslatableSerde: Serialize + DeserializeOwned {}
|
pub trait TranslatableSerde: Serialize + DeserializeOwned {}
|
||||||
pub trait TranslatableManual:
|
pub trait TranslatableManual:
|
||||||
TryFrom<RawTagValue, Error: Into<ParseTagValueError>> + Into<RawTagValue>
|
TryFrom<RawTagValue, Error: Into<ParseTagValueError>> + Into<RawTagValue>
|
||||||
@@ -76,6 +84,7 @@ pub trait TagValue<V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde-tags")]
|
||||||
impl<S, T> Translator<S, T> for TranslateSerde
|
impl<S, T> Translator<S, T> for TranslateSerde
|
||||||
where
|
where
|
||||||
T: TranslatableSerde,
|
T: TranslatableSerde,
|
||||||
@@ -118,7 +127,8 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct RawTag {
|
pub struct RawTag {
|
||||||
key: TagKey,
|
key: TagKey,
|
||||||
value: RawTagValue,
|
value: RawTagValue,
|
||||||
@@ -155,7 +165,8 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
|
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
pub struct TagKey(String);
|
pub struct TagKey(String);
|
||||||
helpers::impl_string_wrapper!(TagKey);
|
helpers::impl_string_wrapper!(TagKey);
|
||||||
|
|
||||||
@@ -166,7 +177,7 @@ helpers::impl_string_wrapper!(TagKey);
|
|||||||
/// * You already have a `T`: Just use [`new()`](Self::new())
|
/// * You already have a `T`: Just use [`new()`](Self::new())
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use aws::tags::Tag;
|
/// # use aws_lib::tags::Tag;
|
||||||
/// let tag = Tag::<bool>::new("foo".to_owned(), true);
|
/// let tag = Tag::<bool>::new("foo".to_owned(), true);
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
@@ -176,7 +187,7 @@ helpers::impl_string_wrapper!(TagKey);
|
|||||||
/// for `String`:
|
/// for `String`:
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use aws::tags::Tag;
|
/// # use aws_lib::tags::Tag;
|
||||||
/// let tag = Tag::<bool>::parse("foo".to_owned(), "true".to_owned()).unwrap();
|
/// let tag = Tag::<bool>::parse("foo".to_owned(), "true".to_owned()).unwrap();
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
@@ -252,7 +263,8 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
|
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub struct TagList(Vec<RawTag>);
|
pub struct TagList(Vec<RawTag>);
|
||||||
|
|
||||||
impl TagList {
|
impl TagList {
|
||||||
@@ -292,6 +304,7 @@ impl TagList {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#[cfg(feature = "serde-tags")]
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -303,6 +316,7 @@ mod tests {
|
|||||||
B,
|
B,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde-tags")]
|
||||||
#[Tag(translate = serde)]
|
#[Tag(translate = serde)]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
struct MyStructTag {
|
struct MyStructTag {
|
||||||
@@ -358,7 +372,9 @@ mod tests {
|
|||||||
#[tag(key = "anothername")]
|
#[tag(key = "anothername")]
|
||||||
tag6: Option<MyTag>,
|
tag6: Option<MyTag>,
|
||||||
tag7: Option<MyTag>,
|
tag7: Option<MyTag>,
|
||||||
|
#[cfg(feature = "serde-tags")]
|
||||||
tag8: MyStructTag,
|
tag8: MyStructTag,
|
||||||
|
#[cfg(feature = "serde-tags")]
|
||||||
tag9: Option<MyStructTag>,
|
tag9: Option<MyStructTag>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -368,6 +384,7 @@ mod tests {
|
|||||||
RawTag::new("tag3".to_owned(), "false".to_owned()),
|
RawTag::new("tag3".to_owned(), "false".to_owned()),
|
||||||
RawTag::new("myname".to_owned(), "A".to_owned()),
|
RawTag::new("myname".to_owned(), "A".to_owned()),
|
||||||
RawTag::new("anothername".to_owned(), "B".to_owned()),
|
RawTag::new("anothername".to_owned(), "B".to_owned()),
|
||||||
|
#[cfg(feature = "serde-tags")]
|
||||||
RawTag::new("tag8".to_owned(), r#"{"foo":"hi","bar":false}"#.to_owned()),
|
RawTag::new("tag8".to_owned(), r#"{"foo":"hi","bar":false}"#.to_owned()),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -380,6 +397,7 @@ mod tests {
|
|||||||
assert!(tags.tag5 == MyTag::A);
|
assert!(tags.tag5 == MyTag::A);
|
||||||
assert!(tags.tag6 == Some(MyTag::B));
|
assert!(tags.tag6 == Some(MyTag::B));
|
||||||
assert!(tags.tag7.is_none());
|
assert!(tags.tag7.is_none());
|
||||||
|
#[cfg(feature = "serde-tags")]
|
||||||
assert!(
|
assert!(
|
||||||
tags.tag8
|
tags.tag8
|
||||||
== MyStructTag {
|
== MyStructTag {
|
||||||
@@ -387,6 +405,7 @@ mod tests {
|
|||||||
bar: false
|
bar: false
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
#[cfg(feature = "serde-tags")]
|
||||||
assert!(tags.tag9.is_none());
|
assert!(tags.tag9.is_none());
|
||||||
|
|
||||||
let into_tags = tags.into_tags();
|
let into_tags = tags.into_tags();
|
||||||
@@ -399,6 +418,7 @@ mod tests {
|
|||||||
RawTag::new("tag3".to_owned(), "false".to_owned()),
|
RawTag::new("tag3".to_owned(), "false".to_owned()),
|
||||||
RawTag::new("myname".to_owned(), "A".to_owned()),
|
RawTag::new("myname".to_owned(), "A".to_owned()),
|
||||||
RawTag::new("anothername".to_owned(), "B".to_owned()),
|
RawTag::new("anothername".to_owned(), "B".to_owned()),
|
||||||
|
#[cfg(feature = "serde-tags")]
|
||||||
RawTag::new("tag8".to_owned(), r#"{"foo":"hi","bar":false}"#.to_owned(),),
|
RawTag::new("tag8".to_owned(), r#"{"foo":"hi","bar":false}"#.to_owned(),),
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
@@ -407,7 +427,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_transparent_tag() {
|
fn test_transparent_tag() {
|
||||||
#[Tag(translate = transparent)]
|
#[Tag(translate = transparent)]
|
||||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
#[derive(PartialEq, Debug)]
|
||||||
struct MyTag(String);
|
struct MyTag(String);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
@@ -1,14 +1,35 @@
|
|||||||
use super::{
|
use super::{ParseTagValueError, RawTagValue, TagValue, TranslatableManual, TranslateManual};
|
||||||
ParseTagValueError, TagValue, TranslatableManual, TranslatableSerde, TranslateManual,
|
|
||||||
TranslateSerde,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Bools can just be handled by serde.
|
impl TranslatableManual for bool {}
|
||||||
impl TranslatableSerde for bool {}
|
|
||||||
|
const TRUE_STR: &str = "true";
|
||||||
|
const FALSE_STR: &str = "false";
|
||||||
|
|
||||||
impl TagValue<Self> for bool {
|
impl TagValue<Self> for bool {
|
||||||
type Error = ParseTagValueError;
|
type Error = ParseTagValueError;
|
||||||
type Translator = TranslateSerde;
|
type Translator = TranslateManual;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<RawTagValue> for bool {
|
||||||
|
type Error = ParseTagValueError;
|
||||||
|
|
||||||
|
fn try_from(value: RawTagValue) -> Result<Self, Self::Error> {
|
||||||
|
match value.as_str() {
|
||||||
|
TRUE_STR => Ok(true),
|
||||||
|
FALSE_STR => Ok(false),
|
||||||
|
_ => Err(ParseTagValueError::InvalidBoolValue { value }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<bool> for RawTagValue {
|
||||||
|
fn from(value: bool) -> Self {
|
||||||
|
if value {
|
||||||
|
Self::new(TRUE_STR.to_owned())
|
||||||
|
} else {
|
||||||
|
Self::new(FALSE_STR.to_owned())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Due to quoting, we cannot use serde here. It would produce quoted
|
// Due to quoting, we cannot use serde here. It would produce quoted
|
||||||
|
|||||||
Reference in New Issue
Block a user