smithy4rs
Smithy code generators for Rust
smithy4rs provides Smithy code generators for clients, server,
and shapes for the Rust programming language.
Generated code supports protocol-agnostic clients, servers, and event systems via schema-guided (de)serialization where schema metadata is made available at runtime.
smithy4rs is an unofficial, community-supported Smithy code generator for Rust.
If you are looking for the official code generators used to create the
the AWS Rust SDK, head over to smithy-rs.
Core components
smithy4rs contains three core types of components:
smithy-buildplugins - used to integrate with the smithy toolchain and generate code from a Smithy model.- Core libraries - provide the core functionality for generated Rust code.
- Protocol implementations - libraries that implement (de)serialization and binding logic that can be used to interface between clients and servers.
All interfaces of code generators, core libraries, and protocol implementations are unstable and may be subject to change without notice or guaranteed of backwards-compatibility.
Getting Started
For a guided introduction to this project, see the Quick Start guide.
For a more in-depth exploration, check out our Tutorials.
FAQ
What is different between smithy4rs and the official code generators?
The official AWS rust code generators create "model ignorant" code. No schema metadata is available at runtime and (de)serialization code is generated on a per-protocol basis. This approach can lead to fast (de)serialization at the expense of extensibility/flexibility.
In contrast, smithy4rs generates "schema-aware" code. Schema metadata is made available to
(de)serializers and schemas can even be created and used dynamically at runtime. The flexibility
of this approach allows smithy4rs to support a number of features that the AWS Rust code generators
do not:
- serde compatibility -
smithy4rsallows users to optionally (de)serialize shapes using the rustserdeecosystem. See serde-compatibility for more info on how to enable this feature. - Type codegen -
smithy4rsallows users to generate standalone types without need for a client or service. This makes the development of more flexible middleware possible for clients and servers and even opens up the possibility of generating event types. - Protocol-agnosticism - The shapes, clients, and servers generated by
smithy4rsare fully protocol-agnostic. In contrast,smithy-rsgenerates protocol-specific (de)serialization code. - Dynamic schemas -
smithy4rsis designed to allow users to dynamically load and use schemas for (de)serialization. This can be used to support fully dynamic clients and server-mocks. - "Any"-ish Document support -
smithy-javaintroduced a concept of documents as anAnytype for the Smithy data model.smithy4rsprovides first class support for this new Document model.
What does it mean to be "protocol agnostic"?
A "protocol" is composed of both the transport (i.e. HTTP or MQTT) and serialization format (i.e. JSON or XML) used to communicate data between clients and servers.
Data representations in the Smithy IDL are not tied to any specific wire format or transport.
In contrast to IDL's like protobuf (gRPC), this allows Smithy servers and clients to
support any protocol they would like, even swapping out client protocols at runtime.
This ability to be "agnostic" to the final wire format and transport is what is meant when we say Smithy is "protocol-agnostic".
Quick Start
This guide introduces [smithy4rs] with a simple working example of generating (de)serializable types .
If you are new to Smithy, we recommend going through the Smithy Quickstart
guide before using smithy4rs. The guide will walk you through the basics
of creating a simple Smithy model.
Prerequisites
- Ensure you have installed rust and cargo
- Ensure you have the Smithy CLI installed.
- To check if you have the CLI installed, Run
smithy --versionin your terminal. - If you need to install the CLI, see the Smithy CLI installation guide.
- To check if you have the CLI installed, Run
Setup
Create a new rust project
First, create a new Rust project by running cargo init in a new directory.
This will set up the basic boilerplate for our crate.
Next, add smithy-cargo as a build dependency:
cargo add --build smithy-cargo
This tool will integrate the Smithy build tooling with Cargo.
Also add smithy4rs-core as a dependency:
cargo add smithy4rs-core
This package defines the Smithy data model in rust and adds all the core functionality (such as serialization) for generate shapes.
Set up Smithy build tooling
We will now set up smithy-cargo to execute the Smithy CLI as
part of the cargo build process.
First, create a build.rs file at the root of your project
and add the following build script:
use smithy_cargo::SmithyBuild; fn main() { /// Executes the `smithy` CLI `build` command from cargo /// and configures some environment variables to point to the /// generated output folder. SmithyBuild::new().execute().expect("Smithy Build failed"); }
Then, add a smithy-build.json file to the root of your project
to configure the Smithy build:
{
"version": "1.0",
"maven": {
"dependencies": [
"dev.hmellema.smithy4rs:type-codegen:1.0.0"
]
},
"plugins": {
"rust-types": {
"TODO": "ADD REAL CONFIG"
}
}
}
Now, we are ready to create our model!
Event Model
For this quickstart we are going to generate a few event shapes.
TODO: Come up with a bit more interesting framing
namespace com.quickstart.example
/// Doc comment
structure EventA {
int: Integer
}
Using the generated shapes
To use our generated shapes, simply create a module add import
them using the generated_shapes! macro.
#![allow(unused)] fn main() { mod shapes { use smithy4rs_core::generated_shapes; generated_shapes![]; } }
We can now use our generated shapes elsewhere in our rust code:
fn main() { // Create a new, validated event instance let event = EventA::builder() .int(42) .build() .expect("Should Build"); // pretty-print the event println!("{:#?}", event) }
Tutorials
Type Codegen User Guide
Client User Guide
This guide provides a detailed walkthrough of how to use and
configure smithy4rs code
generators to generate Rust clients (SDKs) from a Smithy model.
SDK code generation is not supported at this time, but is planned.
To provide input on the development of SDK codegen please create an issue or discussion on the main repo
Server User Guide
This guide provides a detailed walkthrough of how to use and
configure smithy4rs code
generators to generate Rust server stubs from a Smithy model.
Server-stub code generation is not supported at this time.
To provide input on the development of server codegen please create an issue or discussion on the main repo
Schemas
This page provides an overview of the Smithy data model and Schema implementation
in smithy4rs.
Documents
This page provides an overview of the Document types in smithy4rs.
Serde Compatibility
This page walks through how to enable serde (de)serialization for shapes
generated by smithy4rs.
Validation
This page provides an overview of the Validation in smithy4rs.
Fuzz testing
Fuzz testing provides pseudo-random data as the input to code, running until a fatal error is encountered or a time or iteration limit is reached. Fuzz testing can detect potential security and/or stability issues in your Rust code.
Shapes generated by smithy4rs support fuzzing with the cargo-fuzz
rust fuzzing framework.
Structure-Aware fuzzing with smithy4rs
To use generated shapes for structure-aware fuzzing,
enable the arbitrary feature for smithy4rs-core and add the arbitrary
crate as a dependency.
# cargo.toml
[dependencies]
# other deps...
smithy4rs-core = { version = "0.0.1", features = ["arbitrary"] }
arbitrary = { version = "1.4.2", default-features = false }
Now, use a generated shape as the input to your fuzz target:
#![allow(unused)] fn main() { fuzz_target!(|data: MyGeneratedStruct| { // Code to fuzz... }); }
And that's it! Run cargo fuzz to execute any fuzz tests.
Unvalidated inputs
Using a generated Smithy structure for structure-aware fuzzing will always
provide your fuzzed code with a structure validated by the DefaultValidator.
If you would like to generate invalid structures or use a custom validator for your
fuzzed structures, the ShapeBuilder for a generated structure shape can also be used
as an input to a fuzz_target closure. Such builders have not been validated and so,
can be used to create invalid/custom-validated structures.