[−][src]Crate atelier_core
This crate provides a Rust native implementaton of the the AWS Smithy semantic model and foundational capabilities..
The semantic model (a component of the Smithy framework) is the core representation used by tools in the Smithy build process. This crate provides an implementation of the semantic model the for the Atelier set of crates, and core traits for other crates. Specifically it provides:
- The semantic model itself that represents a Smithy model. This API is the in-memory representation shared by all Atelier crates and tools.
- A model builder API that allow for a more fluent and less repetative construction of a core model.
- The prelude module contains the set of shapes defined in the Smithy specification.
- Traits for model actions used to implement linters, validators, and transformations.
- Traits for reading/writing model files in different representations.
- A common error module to be used by all Atelier crates.
The Semantic Model API Example
The following example demonstrates the core model API to create a model for a simple service. The
service, MessageOfTheDay
has a single resource Message
. The resource has an identifier for the
date, but the read
operation does not make the date member required and so will return the message
for the current date.
This API acts as a set of generic data objects and as such has a tendency to be verbose in the
construction of models. The need to create a lot of Identifier
and ShapeID
instances, for example,
does impact the readability. It is important to note, that while there is a discussion in the Smithy
specification contains the
notion of both absolute and relative shape identifiers it is important to note that relative
identifiers are not supported in the semantic model. All names in the semantic model must
be resolved to an absolute name.
use atelier_core::model::shapes::{ AppliedTrait, MemberShape, Operation, Resource, Service, Shape, ShapeKind, Simple, StructureOrUnion, TopLevelShape, }; use atelier_core::model::values::Value; use atelier_core::model::{Model, NamespaceID}; use atelier_core::prelude::PRELUDE_NAMESPACE; use atelier_core::Version; let prelude: NamespaceID = PRELUDE_NAMESPACE.parse().unwrap(); let namespace: NamespaceID = "example.motd".parse().unwrap(); // ---------------------------------------------------------------------------------------- let mut date = TopLevelShape::new( namespace.make_shape("Date".parse().unwrap()), ShapeKind::Simple(Simple::String), ); let mut pattern_trait = AppliedTrait::new(prelude.make_shape("pattern".parse().unwrap())); pattern_trait.set_value(Value::String(r"^\d\d\d\d\-\d\d-\d\d$".to_string())); date.apply_trait(pattern_trait); // ---------------------------------------------------------------------------------------- let shape_name = namespace.make_shape("BadDateValue".parse().unwrap()); let mut body = StructureOrUnion::new(); body.add_member( shape_name.make_member("errorMessage".parse().unwrap()), prelude.make_shape("String".parse().unwrap()), ); let mut error = TopLevelShape::new(shape_name, ShapeKind::Structure(body)); let error_trait = AppliedTrait::with_value( prelude.make_shape("error".parse().unwrap()), "client".to_string().into(), ); error.apply_trait(error_trait); // ---------------------------------------------------------------------------------------- let shape_name = namespace.make_shape("GetMessageOutput".parse().unwrap()); let mut output = StructureOrUnion::new(); let mut message = MemberShape::new( shape_name.make_member("message".parse().unwrap()), prelude.make_shape("String".parse().unwrap()), ); let required = AppliedTrait::new(prelude.make_shape("required".parse().unwrap())); message.apply_trait(required); let _ = output.add_a_member(message); let output = TopLevelShape::new( namespace.make_shape("GetMessageOutput".parse().unwrap()), ShapeKind::Structure(output), ); // ---------------------------------------------------------------------------------------- let shape_name = namespace.make_shape("GetMessageInput".parse().unwrap()); let mut input = StructureOrUnion::new(); input.add_member( shape_name.make_member("date".parse().unwrap()), date.id().clone(), ); let input = TopLevelShape::new( namespace.make_shape("GetMessageInput".parse().unwrap()), ShapeKind::Structure(input), ); // ---------------------------------------------------------------------------------------- let mut get_message = Operation::default(); get_message.set_input_shape(&input); get_message.set_output_shape(&output); get_message.add_error_shape(&error); let mut get_message = TopLevelShape::new( namespace.make_shape("GetMessage".parse().unwrap()), ShapeKind::Operation(get_message), ); let required = AppliedTrait::new(prelude.make_shape("readonly".parse().unwrap())); get_message.apply_trait(required); // ---------------------------------------------------------------------------------------- let mut message = Resource::default(); message.add_identifier("date".to_string(), Value::String(date.id().to_string())); message.set_read_operation_shape(&get_message); let message = TopLevelShape::new( namespace.make_shape("Message".parse().unwrap()), ShapeKind::Resource(message), ); // ---------------------------------------------------------------------------------------- let mut service = Service::new("2020-06-21"); service.add_resource_shape(&message); let mut service = TopLevelShape::new( namespace.make_shape("MessageOfTheDay".parse().unwrap()), ShapeKind::Service(service), ); let documentation = AppliedTrait::with_value( prelude.make_shape("documentation".parse().unwrap()), Value::String("Provides a Message of the day.".to_string()), ); service.apply_trait(documentation); // ---------------------------------------------------------------------------------------- let mut model = Model::new(Version::V10); model.add_shape(message); model.add_shape(date); model.add_shape(get_message); model.add_shape(input); model.add_shape(output); model.add_shape(error); println!("{:#?}", model);
The Model Builder API Example
The following example demonstrates the builder interface to create the same service as the example
above. Hopefully this is more readable as it tends to be less repetative, uses &str
for
identifiers, and includes helper functions for common traits for example. It provides this better
construction experience (there are no read methods on builder objects) by compromising two aspects:
- The API itself is very repetative; this means the same method may be on multiple objects, but
makes it easier to use. For example, you want to add the documentation trait to a shape, so you can:
- construct a
Trait
entity using the core model and theBuilder::add_trait
method, - use the
TraitBuilder::documentation
method which also takes the string to use as the trait value and returns a newTraitBuilder
, or - use the
Builder::documentation
method that hides all the details of a trait and just takes a string.
- construct a
- It hides a lot of the
Identifier
andShapeID
construction and so any of those calls tofrom_str
may fail when the code unwraps the result. This means the builder can panic in ways the core model does not.
use atelier_core::error::ErrorSource; use atelier_core::builder::values::{ArrayBuilder, ObjectBuilder}; use atelier_core::builder::{ traits, ListBuilder, MemberBuilder, ModelBuilder, OperationBuilder, ResourceBuilder, ServiceBuilder, ShapeTraits, SimpleShapeBuilder, StructureBuilder, TraitBuilder, }; use atelier_core::model::{Identifier, Model, ShapeID}; use atelier_core::Version; let model: Model = ModelBuilder::new(Version::V10, "example.motd") .service( ServiceBuilder::new("MessageOfTheDay", "2020-06-21") .documentation("Provides a Message of the day.") .resource("Message") .into(), ) .resource( ResourceBuilder::new("Message") .identifier("date", "Date") .read("GetMessage") .into(), ) .simple_shape( SimpleShapeBuilder::string("Date") .apply_trait(traits::pattern(r"^\d\d\d\d\-\d\d-\d\d$")) .into(), ) .operation( OperationBuilder::new("GetMessage") .readonly() .input("GetMessageInput") .output("GetMessageOutput") .error("BadDateValue") .into(), ) .structure( StructureBuilder::new("GetMessageInput") .member("date", "Date") .into(), ) .structure( StructureBuilder::new("GetMessageOutput") .add_member(MemberBuilder::string("message").required().into()) .into(), ) .structure( StructureBuilder::new("BadDateValue") .error_source(ErrorSource::Client) .add_member(MemberBuilder::string("errorMessage").required().into()) .into(), ) .into();
Modules
action | This module provides a set of traits that describes actions that can operate on models. These actions take three major forms; linters, validators, and transformers. |
builder | Builders to construct models in a more fluent style. See the example in the library overview. |
error | Standard |
io | Traits for reading and writing models in different formats. Separate crates implement the ability to handle different representations, such as the original Smithy, JSON AST, and OpenAPI. |
model | The Smithy semantic model itself, consisting of shapes, members, values, and model statements. |
prelude | Provides an implementation of the prelude model described in the Smithy specification. |
syntax | String constants for elements of the model. |
Enums
Version | Versions of the Smithy specification. |