use crate::error::{ErrorKind, Result};
use crate::model::identity::HasIdentity;
use crate::model::{values::Value, Identifier, ShapeID};
use crate::prelude::{
PRELUDE_NAMESPACE, TRAIT_BOX, TRAIT_DEPRECATED, TRAIT_DOCUMENTATION, TRAIT_ERROR,
TRAIT_EXTERNALDOCUMENTATION, TRAIT_IDEMPOTENT, TRAIT_LENGTH, TRAIT_NOREPLACE, TRAIT_PAGINATED,
TRAIT_PATTERN, TRAIT_PRIVATE, TRAIT_READONLY, TRAIT_REFERENCES, TRAIT_REQUIRED,
TRAIT_REQUIRESLENGTH, TRAIT_SENSITIVE, TRAIT_SINCE, TRAIT_STREAMING, TRAIT_TAGS, TRAIT_TITLE,
TRAIT_TRAIT, TRAIT_UNIQUEITEMS, TRAIT_UNSTABLE,
};
use std::collections::HashMap;
use std::fmt::Debug;
use std::ops::Deref;
use std::str::FromStr;
pub trait NonTraitEq {
fn equal_without_traits(&self, other: &Self) -> bool;
}
pub type TraitValue = Option<Value>;
pub type AppliedTraits = HashMap<ShapeID, TraitValue>;
pub trait HasTraits {
fn has_traits(&self) -> bool {
!self.traits().is_empty()
}
fn has_trait(&self, id: &ShapeID) -> bool {
self.traits().contains_key(id)
}
fn traits(&self) -> &AppliedTraits;
fn traits_mut(&mut self) -> &mut AppliedTraits;
fn trait_named(&self, id: &ShapeID) -> Option<&TraitValue> {
self.traits().get(id)
}
fn apply(&mut self, id: ShapeID) -> Result<()> {
self.apply_with_value(id, None)
}
fn apply_with_value(&mut self, a_trait: ShapeID, value: TraitValue) -> Result<()>;
fn append_traits(&mut self, traits: &AppliedTraits) -> Result<()> {
for (id, value) in traits {
self.apply_with_value(id.clone(), value.clone())?;
}
Ok(())
}
fn remove_trait(&mut self, id: &ShapeID) {
let _ = self.traits_mut().remove(id);
}
fn has_documentation(&self) -> bool {
self.has_trait(&prelude_name(TRAIT_DOCUMENTATION))
}
fn has_external_documentation(&self) -> bool {
self.has_trait(&prelude_name(TRAIT_EXTERNALDOCUMENTATION))
}
fn has_length(&self) -> bool {
self.has_trait(&prelude_name(TRAIT_LENGTH))
}
fn has_pattern(&self) -> bool {
self.has_trait(&prelude_name(TRAIT_PATTERN))
}
fn has_required_length(&self) -> bool {
self.has_trait(&prelude_name(TRAIT_REQUIRESLENGTH))
}
fn is_boxed(&self) -> bool {
self.has_trait(&prelude_name(TRAIT_BOX))
}
fn is_deprecated(&self) -> bool {
self.has_trait(&prelude_name(TRAIT_DEPRECATED))
}
fn is_error(&self) -> bool {
self.has_trait(&prelude_name(TRAIT_ERROR))
}
fn is_idempotent(&self) -> bool {
self.has_trait(&prelude_name(TRAIT_IDEMPOTENT))
}
fn is_no_replace(&self) -> bool {
self.has_trait(&prelude_name(TRAIT_NOREPLACE))
}
fn is_paginated(&self) -> bool {
self.has_trait(&prelude_name(TRAIT_PAGINATED))
}
fn is_private(&self) -> bool {
self.has_trait(&prelude_name(TRAIT_PRIVATE))
}
fn is_readonly(&self) -> bool {
self.has_trait(&prelude_name(TRAIT_READONLY))
}
fn is_references(&self) -> bool {
self.has_trait(&prelude_name(TRAIT_REFERENCES))
}
fn is_required(&self) -> bool {
self.has_trait(&prelude_name(TRAIT_REQUIRED))
}
fn is_sensitive(&self) -> bool {
self.has_trait(&prelude_name(TRAIT_SENSITIVE))
}
fn is_streaming(&self) -> bool {
self.has_trait(&prelude_name(TRAIT_STREAMING))
}
fn is_since(&self) -> bool {
self.has_trait(&prelude_name(TRAIT_SINCE))
}
fn is_tagged(&self) -> bool {
self.has_trait(&prelude_name(TRAIT_TAGS))
}
fn is_titled(&self) -> bool {
self.has_trait(&prelude_name(TRAIT_TITLE))
}
fn is_trait(&self) -> bool {
self.has_trait(&prelude_name(TRAIT_TRAIT))
}
fn has_unique_items(&self) -> bool {
self.has_trait(&prelude_name(TRAIT_UNIQUEITEMS))
}
fn is_unstable(&self) -> bool {
self.has_trait(&prelude_name(TRAIT_UNSTABLE))
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct TopLevelShape {
id: ShapeID,
traits: HashMap<ShapeID, Option<Value>>,
body: ShapeKind,
}
#[allow(clippy::large_enum_variant)]
#[derive(Clone, Debug, PartialEq)]
pub enum ShapeKind {
Simple(Simple),
List(ListOrSet),
Set(ListOrSet),
Map(Map),
Structure(StructureOrUnion),
Union(StructureOrUnion),
Service(Service),
Operation(Operation),
Resource(Resource),
Unresolved,
}
macro_rules! has_traits_impl {
($struct_name:ident . $field_name:ident) => {
impl HasTraits for $struct_name {
fn traits(&self) -> &AppliedTraits {
&self.$field_name
}
fn traits_mut(&mut self) -> &mut AppliedTraits {
&mut self.$field_name
}
fn apply_with_value(
&mut self,
id: ShapeID,
value: Option<Value>,
) -> $crate::error::Result<()> {
if id.is_member() {
return Err(crate::error::ErrorKind::ShapeIDExpected(id).into());
} else if let Some(trait_value) = self.trait_named(&id) {
let new_value = $crate::model::shapes::merge_traits(&id, &trait_value, &value)?;
let _ = self.$field_name.insert(id, new_value);
} else {
let _ = self.$field_name.insert(id, value);
}
Ok(())
}
}
};
}
impl From<Simple> for ShapeKind {
fn from(body: Simple) -> Self {
Self::Simple(body)
}
}
impl From<Service> for ShapeKind {
fn from(body: Service) -> Self {
Self::Service(body)
}
}
impl From<Operation> for ShapeKind {
fn from(body: Operation) -> Self {
Self::Operation(body)
}
}
impl From<Resource> for ShapeKind {
fn from(body: Resource) -> Self {
Self::Resource(body)
}
}
impl ShapeKind {
is_as! { simple, Simple, Simple }
is_as! { list, List, ListOrSet }
is_as! { set, Set, ListOrSet }
is_as! { map, Map, Map}
is_as! { structure, Structure, StructureOrUnion}
is_as! { union, Union, StructureOrUnion}
is_as! { service, Service, Service }
is_as! { operation, Operation, Operation }
is_as! { resource, Resource, Resource }
is_only! { unresolved, Unresolved }
}
impl NonTraitEq for TopLevelShape {
fn equal_without_traits(&self, other: &Self) -> bool {
self.id() == other.id()
&& match (self.body(), other.body()) {
(ShapeKind::Simple(l), ShapeKind::Simple(r)) => l == r,
(ShapeKind::List(l), ShapeKind::List(r)) => {
l.member.equal_without_traits(&r.member)
}
(ShapeKind::Set(l), ShapeKind::Set(r)) => l.member.equal_without_traits(&r.member),
(ShapeKind::Map(l), ShapeKind::Map(r)) => {
l.key.equal_without_traits(&r.key) && l.value.equal_without_traits(&r.value)
}
(ShapeKind::Structure(l), ShapeKind::Structure(r)) => {
l.members.keys().count() == r.members.keys().count()
&& l.members.keys().all(|k| {
l.member(k)
.unwrap()
.equal_without_traits(r.member(k).unwrap())
})
}
(ShapeKind::Union(l), ShapeKind::Union(r)) => {
l.members.keys().count() == r.members.keys().count()
&& l.members.keys().all(|k| {
l.member(k)
.unwrap()
.equal_without_traits(r.member(k).unwrap())
})
}
(ShapeKind::Service(l), ShapeKind::Service(r)) => l == r,
(ShapeKind::Operation(l), ShapeKind::Operation(r)) => l == r,
(ShapeKind::Resource(l), ShapeKind::Resource(r)) => l == r,
(ShapeKind::Unresolved, ShapeKind::Unresolved) => true,
(_, _) => false,
}
}
}
impl HasIdentity<ShapeID> for TopLevelShape {
fn id(&self) -> &ShapeID {
&self.id
}
fn set_id(&mut self, id: ShapeID) {
self.id = id
}
}
has_traits_impl! { TopLevelShape . traits }
lazy_static! {
static ref MEMBER_MEMBER: Identifier = Identifier::from_str("member").unwrap();
static ref MEMBER_KEY: Identifier = Identifier::from_str("key").unwrap();
static ref MEMBER_VALUE: Identifier = Identifier::from_str("value").unwrap();
}
impl TopLevelShape {
pub fn new(id: ShapeID, body: ShapeKind) -> Self {
Self {
id,
traits: Default::default(),
body,
}
}
pub fn with_traits(
id: ShapeID,
body: ShapeKind,
traits: HashMap<ShapeID, Option<Value>>,
) -> Self {
Self { id, traits, body }
}
pub fn body(&self) -> &ShapeKind {
&self.body
}
pub fn body_mut(&mut self) -> &mut ShapeKind {
&mut self.body
}
pub fn set_body(&mut self, body: ShapeKind) {
self.body = body
}
delegate! { is_simple, inner = body }
delegate! { is_list, inner = body }
delegate! { is_set, inner = body }
delegate! { is_map, inner = body }
delegate! { is_structure, inner = body }
delegate! { is_union, inner = body }
delegate! { is_service, inner = body }
delegate! { is_operation, inner = body }
delegate! { is_resource, inner = body }
delegate! { is_unresolved, inner = body }
pub fn is_prelude_shape(&self) -> bool {
self.id().namespace().to_string() == *PRELUDE_NAMESPACE
}
pub fn has_members(&self) -> bool {
!matches!(self.body(), ShapeKind::Simple(_) | ShapeKind::Unresolved)
}
pub fn has_member(&self, member: &Identifier) -> bool {
self.member(member).is_some()
}
pub fn member(&self, member: &Identifier) -> Option<&MemberShape> {
match self.body() {
ShapeKind::Simple(_) => None,
ShapeKind::List(v) => {
if member == MEMBER_MEMBER.deref() {
Some(v.member())
} else {
None
}
}
ShapeKind::Set(v) => {
if member == MEMBER_MEMBER.deref() {
Some(v.member())
} else {
None
}
}
ShapeKind::Map(v) => {
if member == MEMBER_KEY.deref() {
Some(v.key())
} else if member == MEMBER_VALUE.deref() {
Some(v.value())
} else {
None
}
}
ShapeKind::Structure(v) => v.member(member),
ShapeKind::Union(v) => v.member(member),
ShapeKind::Service(_) => None,
ShapeKind::Operation(_) => None,
ShapeKind::Resource(_) => None,
ShapeKind::Unresolved => None,
}
}
}
#[inline]
fn prelude_name(name: &str) -> ShapeID {
ShapeID::new_unchecked(PRELUDE_NAMESPACE, name, None)
}
pub(crate) fn merge_traits(
id: &ShapeID,
left: &TraitValue,
right: &TraitValue,
) -> Result<TraitValue> {
match (left, right) {
(Some(Value::Array(left)), Some(Value::Array(right))) => {
if left.is_empty() {
Ok(Some(Value::Array(right.clone())))
} else if right.is_empty() {
Ok(Some(Value::Array(left.clone())))
} else {
let mut result = left.clone();
result.extend(right.iter().cloned());
Ok(Some(Value::Array(result)))
}
}
(left, right) => {
if left == right {
Ok(left.clone())
} else {
Err(ErrorKind::MergeTraitConflict(id.clone()).into())
}
}
}
}
#[doc(hidden)]
pub mod simple;
pub use simple::Simple;
#[doc(hidden)]
pub mod aggregate;
pub use aggregate::{ListOrSet, Map, MemberShape, StructureOrUnion};
#[doc(hidden)]
pub mod service;
pub use service::{Operation, Resource, Service};