use crate::builder::id::ShapeName;
use crate::error::{Error, ErrorKind};
use crate::model::shapes::{
ListOrSet, Map, MemberShape, Operation, Resource, Service, ShapeKind, StructureOrUnion,
TopLevelShape,
};
use crate::model::values::{Value, ValueMap};
use crate::model::{Identifier, Model, NamespaceID, ShapeID};
use crate::prelude::{
defined_prelude_shapes, defined_prelude_traits, prelude_namespace_id, PRELUDE_NAMESPACE,
};
use crate::syntax::{MEMBER_KEY, MEMBER_MEMBER, MEMBER_VALUE};
use crate::Version;
use std::collections::{HashMap, HashSet};
use std::convert::TryFrom;
use std::iter::FromIterator;
use std::str::FromStr;
pub use values::{ArrayBuilder, ObjectBuilder, ValueBuilder};
#[derive(Clone, Debug)]
pub struct ModelBuilder {
make_references: bool,
default_namespace: NamespaceID,
prelude_namespace: NamespaceID,
smithy_version: Version,
metadata: ValueMap,
shapes: HashMap<ShapeName, TopLevelShapeBuilder>,
}
#[allow(clippy::large_enum_variant)]
#[derive(Clone, Debug)]
enum TopLevelShapeBuilder {
SimpleShape(SimpleShapeBuilder),
List(ListBuilder),
Set(ListBuilder),
Map(MapBuilder),
Structure(StructureBuilder),
Union(StructureBuilder),
Service(ServiceBuilder),
Operation(OperationBuilder),
Resource(ResourceBuilder),
Reference(ReferenceBuilder),
}
impl TryFrom<ModelBuilder> for Model {
type Error = Error;
fn try_from(builder: ModelBuilder) -> Result<Self, Self::Error> {
let mut builder = builder;
Self::try_from(&mut builder)
}
}
impl TryFrom<&mut ModelBuilder> for Model {
type Error = Error;
fn try_from(builder: &mut ModelBuilder) -> Result<Self, Self::Error> {
let mut model = Model::new(builder.smithy_version);
for (k, v) in builder.metadata.iter() {
let _ = model.add_metadata(k.clone(), v.clone());
}
let mut unresolved: HashSet<ShapeID> = Default::default();
for shape in builder.shapes.values() {
let _ = model.add_shape(builder.make_shape(&shape, &mut unresolved)?);
}
for shape_id in unresolved {
let _ = model.add_shape(TopLevelShape::new(shape_id, ShapeKind::Unresolved));
}
Ok(model)
}
}
impl ModelBuilder {
pub fn new(smithy_version: Version, default_namespace: &str) -> Self {
Self {
make_references: false,
default_namespace: default_namespace.parse().unwrap(),
prelude_namespace: PRELUDE_NAMESPACE.parse().unwrap(),
smithy_version,
metadata: Default::default(),
shapes: Default::default(),
}
}
pub fn shape_name(&self, shape_name: &str) -> ShapeID {
self.default_namespace
.make_shape(shape_name.parse().unwrap())
}
pub fn simple_shape(&mut self, builder: SimpleShapeBuilder) -> &mut Self {
self.insert(builder.shape_name.clone(), builder.into())
}
pub fn list(&mut self, builder: ListBuilder) -> &mut Self {
self.insert(
builder.shape_name.clone(),
TopLevelShapeBuilder::List(builder),
)
}
pub fn set(&mut self, builder: ListBuilder) -> &mut Self {
self.insert(
builder.shape_name.clone(),
TopLevelShapeBuilder::Set(builder),
)
}
pub fn map(&mut self, builder: MapBuilder) -> &mut Self {
self.insert(builder.shape_name.clone(), builder.into())
}
pub fn structure(&mut self, builder: StructureBuilder) -> &mut Self {
self.insert(
builder.shape_name.clone(),
TopLevelShapeBuilder::Structure(builder),
)
}
pub fn union(&mut self, builder: StructureBuilder) -> &mut Self {
self.insert(
builder.shape_name.clone(),
TopLevelShapeBuilder::Union(builder),
)
}
pub fn service(&mut self, builder: ServiceBuilder) -> &mut Self {
self.insert(builder.shape_name.clone(), builder.into())
}
pub fn operation(&mut self, builder: OperationBuilder) -> &mut Self {
self.insert(builder.shape_name.clone(), builder.into())
}
pub fn resource(&mut self, builder: ResourceBuilder) -> &mut Self {
self.insert(builder.shape_name.clone(), builder.into())
}
pub fn uses(&mut self, shape: &str) -> &mut Self {
self.reference(ReferenceBuilder::new(shape))
}
pub fn apply(&mut self, shape: &str, a_trait: TraitBuilder) -> &mut Self {
let shape_name = ShapeName::from_str(shape).unwrap();
self.apply_to(&shape_name, a_trait)
}
pub fn reference(&mut self, builder: ReferenceBuilder) -> &mut Self {
let shape_id = &builder.shape_id;
self.insert(shape_id.clone(), builder.into())
}
pub fn meta_data(&mut self, key: String, value: Value) -> &mut Self {
let _ = self.metadata.insert(key, value);
self
}
pub fn meta_data_from(&mut self, value_map: ValueMap) -> &mut Self {
for (key, value) in value_map {
let _ = self.metadata.insert(key, value);
}
self
}
fn insert(&mut self, id: ShapeName, shape: TopLevelShapeBuilder) -> &mut Self {
let previous = self.shapes.insert(id, shape);
if previous.is_some() {
info!("Interestingly the shape seems to have been added more than once?",);
}
self
}
fn reference_names(&self) -> impl Iterator<Item = &ShapeName> {
self.shapes
.iter()
.filter(|(_, builder)| matches!(builder, TopLevelShapeBuilder::Reference(_)))
.map(|(id, _)| id)
}
fn resolve_shape_name(
&self,
shape_name: &ShapeName,
is_trait: bool,
) -> Result<(ShapeID, bool), Error> {
info!(
"resolve_shape_name(shape_name: {:?}, is_trait: {})",
&shape_name, is_trait
);
match shape_name {
ShapeName::Qualified(qualified) => {
trace!("Qualified ShapeID exists, use as is");
if !qualified.is_member() {
Ok((qualified.clone(), false))
} else {
error!("expected a qualified, non-member, ShapeID");
Err(ErrorKind::ShapeIDExpected(qualified.clone()).into())
}
}
ShapeName::Local(local) => {
trace!("Local Identifier: proceeding with resolution...");
let references: Vec<&ShapeName> = self
.reference_names()
.filter(|shape_name| shape_name.shape_name() == local)
.collect();
debug!(
"Found {} references to check, references => {:#?}",
references.len(),
&references
);
match references.len() {
1 => {
trace!("SUCCESS: a use statement imports this shape explicitly");
Ok((
references.first().unwrap().as_qualified().unwrap().clone(),
false,
))
}
0 => {
if self.shapes.contains_key(shape_name) {
trace!(
"SUCCESS: shape found in same namespace as shape with same name"
);
Ok((self.default_namespace.make_shape(local.clone()), false))
} else if (!is_trait
&& defined_prelude_shapes().contains(&*local.to_string()))
|| (is_trait && defined_prelude_traits().contains(&*local.to_string()))
{
trace!("SUCCESS: shape found in prelude with the same name");
Ok((prelude_namespace_id().make_shape(local.clone()), false))
} else {
trace!("SUCCESS-ish: shape not found, so assume it's local");
let qualified = self.default_namespace.make_shape(local.clone());
Ok((qualified, true))
}
}
_ => {
error!("shape resolution failed because more than one reference matches");
Err(ErrorKind::AmbiguousShape(local.to_string()).into())
}
}
}
}
}
fn apply_to(&mut self, shape_name: &ShapeName, a_trait: TraitBuilder) -> &mut Self {
match shape_name {
ShapeName::Qualified(shape_id) => {
if let Some(member_name) = shape_id.member_name() {
let parent_shape = ShapeName::from(shape_id.shape_only());
if let Some(shape) = self.shapes.get_mut(&parent_shape) {
apply_to_member(shape, member_name, a_trait);
} else {
panic!("No shape named {} for member {}", parent_shape, member_name);
}
} else if let Some(shape) = self.shapes.get_mut(&shape_name) {
apply_to_shape(shape, a_trait);
} else {
let mut builder = ReferenceBuilder::from(shape_id.clone());
let _ = builder.apply_trait(a_trait);
let _ = self.reference(builder);
}
self
}
ShapeName::Local(local) => {
let (shape_id, new_reference) = self
.resolve_shape_name(&local.clone().into(), false)
.unwrap();
if new_reference {
let _ = self.reference(ReferenceBuilder::from(shape_id.clone()));
}
self.apply_to(&shape_id.into(), a_trait)
}
}
}
fn make_shape(
&self,
builder: &TopLevelShapeBuilder,
references: &mut HashSet<ShapeID>,
) -> Result<TopLevelShape, Error> {
Ok(match builder {
TopLevelShapeBuilder::SimpleShape(builder) => {
self.make_simple_shape(builder, references)?
}
TopLevelShapeBuilder::List(builder) => self.make_list(builder, references)?,
TopLevelShapeBuilder::Set(builder) => self.make_set(builder, references)?,
TopLevelShapeBuilder::Map(builder) => self.make_map(builder, references)?,
TopLevelShapeBuilder::Structure(builder) => self.make_structure(builder, references)?,
TopLevelShapeBuilder::Union(builder) => self.make_union(builder, references)?,
TopLevelShapeBuilder::Service(builder) => self.make_service(builder, references)?,
TopLevelShapeBuilder::Operation(builder) => self.make_operation(builder, references)?,
TopLevelShapeBuilder::Resource(builder) => self.make_resource(builder, references)?,
TopLevelShapeBuilder::Reference(builder) => self.make_reference(builder, references)?,
})
}
fn make_simple_shape(
&self,
builder: &SimpleShapeBuilder,
references: &mut HashSet<ShapeID>,
) -> Result<TopLevelShape, Error> {
let (shape_id, new_reference) = self.resolve_shape_name(&builder.shape_name, false)?;
if new_reference {
let _ = references.insert(shape_id.clone());
}
Ok(TopLevelShape::with_traits(
shape_id,
ShapeKind::Simple(builder.simple_shape.clone()),
self.make_traits(&builder.applied_traits, references)?,
))
}
fn make_list(
&self,
builder: &ListBuilder,
references: &mut HashSet<ShapeID>,
) -> Result<TopLevelShape, Error> {
let (shape_id, new_reference) = self.resolve_shape_name(&builder.shape_name, false)?;
if new_reference {
let _ = references.insert(shape_id.clone());
}
let (target_id, new_reference) = self.resolve_shape_name(&builder.member.target, false)?;
if new_reference {
let _ = references.insert(target_id.clone());
}
Ok(TopLevelShape::with_traits(
shape_id,
ShapeKind::List(ListOrSet::from(MemberShape::with_traits(
builder.member.member_name.clone(),
target_id,
self.make_traits(&builder.member.applied_traits, references)?,
))),
self.make_traits(&builder.applied_traits, references)?,
))
}
fn make_set(
&self,
builder: &ListBuilder,
references: &mut HashSet<ShapeID>,
) -> Result<TopLevelShape, Error> {
let (shape_id, new_reference) = self.resolve_shape_name(&builder.shape_name, false)?;
if new_reference {
let _ = references.insert(shape_id.clone());
}
let (target_id, new_reference) = self.resolve_shape_name(&builder.member.target, false)?;
if new_reference {
let _ = references.insert(target_id.clone());
}
Ok(TopLevelShape::with_traits(
shape_id,
ShapeKind::List(ListOrSet::from(MemberShape::with_traits(
builder.member.member_name.clone(),
target_id,
self.make_traits(&builder.member.applied_traits, references)?,
))),
self.make_traits(&builder.applied_traits, references)?,
))
}
fn make_map(
&self,
builder: &MapBuilder,
references: &mut HashSet<ShapeID>,
) -> Result<TopLevelShape, Error> {
let (shape_id, new_reference) = self.resolve_shape_name(&builder.shape_name, false)?;
if new_reference {
let _ = references.insert(shape_id.clone());
}
let (key_target_id, new_reference) = self.resolve_shape_name(&builder.key.target, false)?;
if new_reference {
let _ = references.insert(key_target_id.clone());
}
let (value_target_id, new_reference) =
self.resolve_shape_name(&builder.value.target, false)?;
if new_reference {
let _ = references.insert(value_target_id.clone());
}
Ok(TopLevelShape::with_traits(
shape_id,
ShapeKind::Map(Map::from(
MemberShape::with_traits(
builder.key.member_name.clone(),
key_target_id,
self.make_traits(&builder.key.applied_traits, references)?,
),
MemberShape::with_traits(
builder.value.member_name.clone(),
value_target_id,
self.make_traits(&builder.value.applied_traits, references)?,
),
)),
self.make_traits(&builder.applied_traits, references)?,
))
}
fn make_structure_inner(
&self,
builder: &StructureBuilder,
references: &mut HashSet<ShapeID>,
) -> Result<StructureOrUnion, Error> {
let members: Result<Vec<MemberShape>, Error> = builder
.members
.iter()
.map(|mb| {
let (member_target_id, new_reference) =
self.resolve_shape_name(&mb.target, false)?;
if new_reference {
let _ = references.insert(member_target_id.clone());
}
Ok(MemberShape::with_traits(
mb.member_name.clone(),
member_target_id,
self.make_traits(&mb.applied_traits, references)?,
))
})
.collect();
members.map(|members| StructureOrUnion::with_members(&members))
}
fn make_structure(
&self,
builder: &StructureBuilder,
references: &mut HashSet<ShapeID>,
) -> Result<TopLevelShape, Error> {
let (shape_id, new_reference) = self.resolve_shape_name(&builder.shape_name, false)?;
if new_reference {
let _ = references.insert(shape_id.clone());
}
Ok(TopLevelShape::with_traits(
shape_id,
ShapeKind::Structure(self.make_structure_inner(builder, references)?),
self.make_traits(&builder.applied_traits, references)?,
))
}
fn make_union(
&self,
builder: &StructureBuilder,
references: &mut HashSet<ShapeID>,
) -> Result<TopLevelShape, Error> {
let (shape_id, new_reference) = self.resolve_shape_name(&builder.shape_name, false)?;
if new_reference {
let _ = references.insert(shape_id.clone());
}
Ok(TopLevelShape::with_traits(
shape_id,
ShapeKind::Union(self.make_structure_inner(builder, references)?),
self.make_traits(&builder.applied_traits, references)?,
))
}
fn make_service(
&self,
builder: &ServiceBuilder,
references: &mut HashSet<ShapeID>,
) -> Result<TopLevelShape, Error> {
let (shape_id, new_reference) = self.resolve_shape_name(&builder.shape_name, false)?;
if new_reference {
let _ = references.insert(shape_id.clone());
}
let mut service = Service::new(&builder.version);
for member_shape_id in &builder.operations {
let (shape_id, new_reference) = self.resolve_shape_name(&member_shape_id, false)?;
if new_reference {
let _ = references.insert(shape_id.clone());
}
service.add_operation(shape_id);
}
for member_shape_id in &builder.resources {
let (shape_id, new_reference) = self.resolve_shape_name(&member_shape_id, false)?;
if new_reference {
let _ = references.insert(shape_id.clone());
}
service.add_resource(shape_id);
}
for (shape_id, local_name) in &builder.rename_shapes {
let _ = service.insert_rename_shape(shape_id.clone(), local_name.clone());
}
Ok(TopLevelShape::with_traits(
shape_id,
ShapeKind::Service(service),
self.make_traits(&builder.applied_traits, references)?,
))
}
fn make_operation(
&self,
builder: &OperationBuilder,
references: &mut HashSet<ShapeID>,
) -> Result<TopLevelShape, Error> {
let (shape_id, new_reference) = self.resolve_shape_name(&builder.shape_name, false)?;
if new_reference {
let _ = references.insert(shape_id.clone());
}
let mut operation = Operation::default();
if let Some(shape_id) = &builder.input {
let (shape_id, new_reference) = self.resolve_shape_name(&shape_id, false)?;
if new_reference {
let _ = references.insert(shape_id.clone());
}
operation.set_input(shape_id);
}
if let Some(shape_id) = &builder.output {
let (shape_id, new_reference) = self.resolve_shape_name(&shape_id, false)?;
if new_reference {
let _ = references.insert(shape_id.clone());
}
operation.set_output(shape_id);
}
for shape_id in &builder.errors {
let (shape_id, new_reference) = self.resolve_shape_name(&shape_id, false)?;
if new_reference {
let _ = references.insert(shape_id.clone());
}
operation.add_error(shape_id);
}
Ok(TopLevelShape::with_traits(
shape_id,
ShapeKind::Operation(operation),
self.make_traits(&builder.applied_traits, references)?,
))
}
fn make_resource(
&self,
builder: &ResourceBuilder,
references: &mut HashSet<ShapeID>,
) -> Result<TopLevelShape, Error> {
let (shape_id, new_reference) = self.resolve_shape_name(&builder.shape_name, false)?;
if new_reference {
let _ = references.insert(shape_id.clone());
}
let mut resource = Resource::default();
for (name, shape_ref) in &builder.identifiers {
let (shape_id, new_reference) = self.resolve_shape_name(shape_ref, false)?;
if new_reference {
let _ = references.insert(shape_id.clone());
}
let _ = resource.add_identifier(name.clone(), shape_id);
}
if let Some(shape_id) = &builder.create {
let (shape_id, new_reference) = self.resolve_shape_name(&shape_id, false)?;
if new_reference {
let _ = references.insert(shape_id.clone());
}
resource.set_create(shape_id);
}
if let Some(shape_id) = &builder.put {
let (shape_id, new_reference) = self.resolve_shape_name(&shape_id, false)?;
if new_reference {
let _ = references.insert(shape_id.clone());
}
resource.set_put(shape_id);
}
if let Some(shape_id) = &builder.read {
let (shape_id, new_reference) = self.resolve_shape_name(&shape_id, false)?;
if new_reference {
let _ = references.insert(shape_id.clone());
}
resource.set_read(shape_id);
}
if let Some(shape_id) = &builder.update {
let (shape_id, new_reference) = self.resolve_shape_name(&shape_id, false)?;
if new_reference {
let _ = references.insert(shape_id.clone());
}
resource.set_update(shape_id);
}
if let Some(shape_id) = &builder.delete {
let (shape_id, new_reference) = self.resolve_shape_name(&shape_id, false)?;
if new_reference {
let _ = references.insert(shape_id.clone());
}
resource.set_delete(shape_id);
}
if let Some(shape_id) = &builder.list {
let (shape_id, new_reference) = self.resolve_shape_name(&shape_id, false)?;
if new_reference {
let _ = references.insert(shape_id.clone());
}
resource.set_list(shape_id);
}
for shape_id in &builder.operations {
let (shape_id, new_reference) = self.resolve_shape_name(&shape_id, false)?;
if new_reference {
let _ = references.insert(shape_id.clone());
}
resource.add_operation(shape_id);
}
for shape_id in &builder.collection_operations {
let (shape_id, new_reference) = self.resolve_shape_name(&shape_id, false)?;
if new_reference {
let _ = references.insert(shape_id.clone());
}
resource.add_collection_operation(shape_id);
}
for shape_id in &builder.resources {
let (shape_id, new_reference) = self.resolve_shape_name(&shape_id, false)?;
if new_reference {
let _ = references.insert(shape_id.clone());
}
resource.add_resource(shape_id);
}
Ok(TopLevelShape::with_traits(
shape_id,
ShapeKind::Resource(resource),
self.make_traits(&builder.applied_traits, references)?,
))
}
fn make_reference(
&self,
builder: &ReferenceBuilder,
references: &mut HashSet<ShapeID>,
) -> Result<TopLevelShape, Error> {
Ok(TopLevelShape::with_traits(
builder.shape_id.as_qualified().unwrap().clone(),
ShapeKind::Unresolved,
self.make_traits(&builder.applied_traits, references)?,
))
}
fn make_traits(
&self,
builders: &[TraitBuilder],
references: &mut HashSet<ShapeID>,
) -> Result<HashMap<ShapeID, Option<Value>>, Error> {
let pairs: Result<Vec<(ShapeID, Option<Value>)>, Error> = builders
.iter()
.cloned()
.map(|builder| {
let (shape_id, new_reference) = self.resolve_shape_name(&builder.shape_id, true)?;
if new_reference {
let _ = references.insert(shape_id.clone());
}
Ok((shape_id, builder.value))
})
.collect();
match pairs {
Ok(pairs) => Ok(HashMap::from_iter(pairs)),
Err(err) => Err(err),
}
}
}
fn apply_to_member(
shape: &mut TopLevelShapeBuilder,
member_name: &Identifier,
a_trait: TraitBuilder,
) {
let ok = match shape {
TopLevelShapeBuilder::List(shape) => {
if member_name.to_string() == MEMBER_MEMBER {
let _ = shape.member.apply_trait(a_trait);
true
} else {
false
}
}
TopLevelShapeBuilder::Set(shape) => {
if member_name.to_string() == MEMBER_MEMBER {
let _ = shape.member.apply_trait(a_trait);
true
} else {
false
}
}
TopLevelShapeBuilder::Map(shape) => {
if member_name.to_string() == MEMBER_KEY {
let _ = shape.key.apply_trait(a_trait);
true
} else if member_name.to_string() == MEMBER_VALUE {
let _ = shape.value.apply_trait(a_trait);
true
} else {
false
}
}
TopLevelShapeBuilder::Structure(shape) => {
if let Some(member) = shape
.members
.iter_mut()
.find(|m| &m.member_name == member_name)
{
let _ = member.apply_trait(a_trait);
true
} else {
false
}
}
TopLevelShapeBuilder::Union(shape) => {
if let Some(member) = shape
.members
.iter_mut()
.find(|m| &m.member_name == member_name)
{
let _ = member.apply_trait(a_trait);
true
} else {
false
}
}
_ => false,
};
if !ok {
panic!("Shape does not have a traitable member {}", member_name);
}
}
fn apply_to_shape(shape: &mut TopLevelShapeBuilder, a_trait: TraitBuilder) {
match shape {
TopLevelShapeBuilder::SimpleShape(shape) => {
let _ = shape.apply_trait(a_trait);
}
TopLevelShapeBuilder::List(shape) => {
let _ = shape.apply_trait(a_trait);
}
TopLevelShapeBuilder::Set(shape) => {
let _ = shape.apply_trait(a_trait);
}
TopLevelShapeBuilder::Map(shape) => {
let _ = shape.apply_trait(a_trait);
}
TopLevelShapeBuilder::Structure(shape) => {
let _ = shape.apply_trait(a_trait);
}
TopLevelShapeBuilder::Union(shape) => {
let _ = shape.apply_trait(a_trait);
}
TopLevelShapeBuilder::Service(shape) => {
let _ = shape.apply_trait(a_trait);
}
TopLevelShapeBuilder::Operation(shape) => {
let _ = shape.apply_trait(a_trait);
}
TopLevelShapeBuilder::Resource(shape) => {
let _ = shape.apply_trait(a_trait);
}
TopLevelShapeBuilder::Reference(shape) => {
let _ = shape.apply_trait(a_trait);
}
}
}
#[doc(hidden)]
pub mod id;
pub mod selector;
#[doc(hidden)]
pub mod shapes;
pub use shapes::{
ListBuilder, MapBuilder, MemberBuilder, OperationBuilder, ReferenceBuilder, ResourceBuilder,
ServiceBuilder, ShapeTraits, SimpleShapeBuilder, StructureBuilder,
};
#[doc(hidden)]
pub mod traits;
pub use traits::TraitBuilder;
#[doc(hidden)]
pub mod values;