use crate::{ShapeId, Trait};
use std::any::Any;
macro_rules! annotation_trait {
($(#[$meta:meta])* $name:ident, $ns:literal, $trait_name:literal) => {
$(#[$meta])*
#[derive(Debug, Clone)]
#[allow(dead_code)] pub struct $name;
impl $name {
pub const TRAIT_ID: ShapeId = crate::shape_id!($ns, $trait_name);
}
impl Trait for $name {
fn trait_id(&self) -> &ShapeId { &Self::TRAIT_ID }
fn as_any(&self) -> &dyn Any { self }
}
};
}
macro_rules! string_trait {
($(#[$meta:meta])* $name:ident, $ns:literal, $trait_name:literal) => {
$(#[$meta])*
#[derive(Debug, Clone)]
#[allow(dead_code)] pub struct $name {
value: &'static str,
}
#[allow(dead_code)] impl $name {
pub const TRAIT_ID: ShapeId = crate::shape_id!($ns, $trait_name);
pub const fn new(value: &'static str) -> Self {
Self { value }
}
pub fn value(&self) -> &str {
self.value
}
}
impl Trait for $name {
fn trait_id(&self) -> &ShapeId { &Self::TRAIT_ID }
fn as_any(&self) -> &dyn Any { self }
}
};
}
string_trait!(
JsonNameTrait,
"smithy.api", "jsonName"
);
string_trait!(
XmlNameTrait,
"smithy.api", "xmlName"
);
string_trait!(
MediaTypeTrait,
"smithy.api", "mediaType"
);
annotation_trait!(
XmlAttributeTrait,
"smithy.api", "xmlAttribute"
);
annotation_trait!(
XmlFlattenedTrait,
"smithy.api", "xmlFlattened"
);
annotation_trait!(
XmlNamespaceTrait,
"smithy.api", "xmlNamespace"
);
#[derive(Debug, Clone, Copy)]
#[allow(dead_code)] pub struct TimestampFormatTrait {
format: TimestampFormat,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TimestampFormat {
EpochSeconds,
DateTime,
HttpDate,
}
#[allow(dead_code)] impl TimestampFormatTrait {
pub const TRAIT_ID: ShapeId = crate::shape_id!("smithy.api", "timestampFormat");
pub const fn new(format: TimestampFormat) -> Self {
Self { format }
}
pub fn format(&self) -> TimestampFormat {
self.format
}
}
impl Trait for TimestampFormatTrait {
fn trait_id(&self) -> &ShapeId {
&Self::TRAIT_ID
}
fn as_any(&self) -> &dyn Any {
self
}
}
string_trait!(
HttpHeaderTrait,
"smithy.api", "httpHeader"
);
string_trait!(
HttpQueryTrait,
"smithy.api", "httpQuery"
);
string_trait!(
HttpPrefixHeadersTrait,
"smithy.api", "httpPrefixHeaders"
);
annotation_trait!(
HttpLabelTrait,
"smithy.api", "httpLabel"
);
annotation_trait!(
HttpPayloadTrait,
"smithy.api", "httpPayload"
);
annotation_trait!(
HttpQueryParamsTrait,
"smithy.api", "httpQueryParams"
);
annotation_trait!(
HttpResponseCodeTrait,
"smithy.api", "httpResponseCode"
);
#[derive(Debug, Clone)]
pub struct HttpTrait {
method: &'static str,
uri: &'static str,
code: u16,
}
impl HttpTrait {
pub const fn new(method: &'static str, uri: &'static str, code: Option<u16>) -> Self {
Self {
method,
uri,
code: match code {
Some(c) => c,
None => 200,
},
}
}
pub fn method(&self) -> &str {
self.method
}
pub fn uri(&self) -> &str {
self.uri
}
pub fn code(&self) -> u16 {
self.code
}
}
annotation_trait!(
StreamingTrait,
"smithy.api", "streaming"
);
annotation_trait!(
EventHeaderTrait,
"smithy.api", "eventHeader"
);
annotation_trait!(
EventPayloadTrait,
"smithy.api", "eventPayload"
);
annotation_trait!(
SensitiveTrait,
"smithy.api", "sensitive"
);
annotation_trait!(
HostLabelTrait,
"smithy.api", "hostLabel"
);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn downcast_json_name() {
let t: Box<dyn Trait> = Box::new(JsonNameTrait::new("userName"));
assert_eq!(t.trait_id().as_str(), "smithy.api#jsonName");
let json_name = t.as_any().downcast_ref::<JsonNameTrait>().unwrap();
assert_eq!(json_name.value(), "userName");
}
#[test]
fn downcast_sensitive() {
let t: Box<dyn Trait> = Box::new(SensitiveTrait);
assert_eq!(t.trait_id().as_str(), "smithy.api#sensitive");
assert!(t.as_any().downcast_ref::<SensitiveTrait>().is_some());
}
#[test]
fn timestamp_format_parsing() {
let t = TimestampFormatTrait::new(TimestampFormat::EpochSeconds);
assert_eq!(t.format(), TimestampFormat::EpochSeconds);
assert_eq!(t.trait_id().as_str(), "smithy.api#timestampFormat");
}
}