Skip to content

Latest commit

 

History

History
469 lines (395 loc) · 21.8 KB

README.md

File metadata and controls

469 lines (395 loc) · 21.8 KB
rust-template Logo

rust-template

Build Status License-Apache License-MIT Discord

This template repository is Fission's opinionated Rust, Rust web framework, and Rust+WebAssembly (Wasm) project generator, which uses the cargo-generate tool.

These templates provide various features for getting-up and running with Rust or Rust and Wasm, including:

  • README standardization, code of conduct, contribuing guidelines, and a consistent project layout
  • GitHub issue and pull-request templates
  • A highly-opinionated axum middleware-extensive webserver, focused heavily on observability and flexibility
  • Some choice default Rust dependencies (particularly for Wasm) including wasm-bindgen
  • cargo-bench scaffolding (optional)
  • Release GitHub Action workflow(s) using the release-please-action and the release-please deploy strategy (optional)
    • For Wasm libraries, this includes publishing to npm with wasm-pack, reliant on the Cargo version for the Wasm package.
  • Test, lint, audit, and code coverage (via Codecov) GitHub Action workflows (optional)
  • Cross-compile-compatible (arm64, amd64), buildx-focused Dockerfiles—pick a musl or glibc build—for binary executables (right now), as well as an associated GitHub Action for building and pushing to GitHub Packages Registry and Docker Hub.
  • Pre-commit and rustfmt opinionated defaults
  • Dependabot support (optional)
  • Nix flake support (optional)
  • A choice of an Apache, MIT, or a dual Apache/MIT license

Outline

Project Templates

This repository contains two sub-templates:

  • rust: for generating a rust-only library, webserver, or binary/executable project.
  • rust+wasm for generating a cargo workspace with a rust-only crate of the project (library or binary) and another crate for wasm-bindings (library-only), meant for execution in Node.js or running in modern browsers and/or with bundlers like webpack.

Getting Started

First, install cargo-generate via cargo install cargo-generate. More installation options are available here.

The experience running through the experience should look something like this:

cargo-generate Rust Binary Application Screenshot

Generating a Rust-Only Project

The rust template is designed for generating a rust binary or application library.

  • Generate a binary project, for example an axum webserver:

    cargo generate --bin --git https://github.com/fission-codes/rust-template

Note on binary crate types: If using the --bin flag, this template will generate a Rust binary project scaffolding with both a src/main.rs and a src/lib.rs. This allows for better support for integration testing and helps with separation of concerns.

  • Generate an application library project:

    cargo generate --lib --git https://github.com/fission-codes/rust-template
  • Generate a project from src, locally:

    cargo generate --lib --path fission-codes/rust-template/

Note on SSH-Keys: When genearting a project/repository, please be aware that RSA keys used with SHA-1 signatures are no longer supported by GitHub. There is currently an issue in the cargo-generate repository involving an id_rsa default. If you run into an associated error using the template, please specify your private key when generating a project/repository like so:

cargo generate -i ~/.ssh/id_ed25519 https://github.com/fission-codes/rust-template

🔋 Batteries Included

  • anyhow as a ergonomic and idiomatic alternative for handling errors in applications, and thiserror for designing dedicated error type(s) in libraries so that on failures the caller gets exactly the information chosen.
  • proptest and criterion for generating inputs and running benchmarks (optional).
  • tracing for instrumenting Rust programs to collect structured, event-based diagnostic information, going beyond just logging-style diagnostics.
  • tracing-subscriber for Rust binary applications to collect trace data, such as by logging it to standard output, and consume messages emitted by log-instrumented libraries and modules.
  • An option to generate a highly opiniated axum web application stack.

🔋 Batteries Included Web Framework

If you choose to run a webserver with axum (choosing true at the prompt), you'll be given an extensive web framework to work from, heavily influenced by Composing an observable Rust application, among other sources.

  • anyhow as a ergonomic and idiomatic alternative for handling errors in applications.

  • axum as the fundamenal, already-🔋's included web framework that serves as our foundation, which includes high-level features like:

    • Routing requests to handlers with a macro free API
    • Declaratively parsing requests using extractors.
    • Subscribing to a simple and predictable error handling model, which we further simplify in our generated template.
    • Generating responses with minimal boilerplate.
    • Taking full advantage of the tower and tower-http ecosystem of middleware, services, and utilities.
  • Template-specific extensions to axum, including

    • An enhanced Json extractor for better handling of Unprocessable Entity responses.

    • A generic AppError type for encoding JSONAPI error object responses.

    • A macro for generating typed HTTP headers, for example

      header!(XDummyId, XDUMMY_ID, "x-dummy-id");
      
      fn test_dummy_header() {
          let s = "18312349-3139-498C-84B6-87326BF1F2A7";
          let dummy_id = test_decode::<XDummyId>(&[s]).unwrap();
          let headers = test_encode(dummy_id);
          assert_eq!(headers["x-dummy-id"], s);
      }
    • Built-in healthcheck, ping GET route handlers, as well as a fallback route handler and decorated layers for catching panics, handling server timeouts, setting ulid request-ids per request, and marking sensitive headers on both requests and responses.

    • Graceful shutdown of server when Ctrl-c'ed or terminated.

  • config-rs for layered configuration settings and using APP prefixed environment variables for (overrding) configuration.

    We provide a default for application settings (note: metrics and server ports are provided through cargo generate prompts):

    [monitoring]
    process_collector_interval = 10
    
    [otel]
    exporter_otlp_endpoint = "http://localhost:4317"
    
    [server]
    environment = "local"
    metrics_port = 4000
    port = 3000
    timeout_ms = 30000
  • metrics-rs for application instrumentation, including counters, gauges, histograms, and more.

  • opentelemetry-rust and axum-tracing-opentelemetry for integrating axum and tracing with opentelemetry, the well-known observability framework and specification for capturing telemetry data and supporting distributed tracing context propagation. Here's an example set of logs displaying OTEL spec fields, among other contextual information:

    level=INFO span_name="HTTP request" span=2251799813685249 span_event=new_span timestamp=2023-01-29T15:06:42.188395Z http.method=GET http.client_ip=127.0.0.1:59965 http.host=localhost:3000 trace_id=fa9754fa3142db2c100a8c47f6dd391d http.route=/ping
    level=INFO subject=request category=http.request msg="started processing request" request_path=/ping authorization=null target="project::middleware::logging" location="project/src/middleware/logging.rs:123" timestamp=2023-01-29T15:06:42.188933Z span=2251799813685249 otel.name="GET /ping" http.method=GET http.scheme=HTTP http.client_ip=127.0.0.1:59965 http.flavor=1.1 otel.kind=server http.user_agent=curl/7.85.0 http.host=localhost:3000 trace_id=fa9754fa3142db2c100a8c47f6dd391d http.target=/ping http.route=/ping
    level=INFO span_name="HTTP request" span=2251799813685249 span_event=close_span timestamp=2023-01-29T15:06:42.192221Z http.method=GET latency_ms=3 http.client_ip=127.0.0.1:59965 http.host=localhost:3000 trace_id=fa9754fa3142db2c100a8c47f6dd391d http.route=/ping
  • proptest and criterion for generating inputs and running benchmarks (optional).

  • reqwest as the default HTTP client library and reqwest-middleware as a wrapper around reqwest for client middleware chaining, giving us retries and tracing out of the box.

  • sysinfo for monitoring process information like memory, disk usage, etc. This information is automatically represented and tracked as gauges for Prometheus scraping/export for example.

  • tracing for instrumenting Rust programs to collect structured, event-based diagnostic information, going beyond just logging-style diagnostics.

  • tracing-subscriber for Rust binary applications to collect trace data, such as by logging it to standard output, and to consume messages emitted by log-instrumented libraries and modules.

    For axum projects, we include a compositon of tracing subscribers from smaller units of behavior, called layers, for collecting, augmenting, and logging (as structured logs) trace data. These layers tap into hooks triggered throughout a span’s lifecycle. You can find them here.

    Event logs are formatted in logfmt, as a series of key/value pairs. The implementation of the log generation is inspired by influxdata's (Influx DB's) version.

  • utoipa for compile-time, auto-generated OpenAPI documentation and serving it via Swagger UI. This works as a Procedural attribute macro, for example:

    #[utoipa::path(
        get,
        path = "/ping",
        responses(
            (status = 200, description = "Ping successful"),
            (status = 500, description = "Ping not successful", body=AppError)
        )
    )]
    pub async fn get() -> AppResult<StatusCode> {
        Ok(StatusCode::OK)
    }
  • wiremock-rs to provide HTTP mocking to perform black-box testing of Rust applications that interact with third-party client APIs. This is exemplified through the given integration test when choosing axum as a prompt.

Generating a Rust+Wasm Workspace Project

The rust+wasm template is designed for generating a workspace containing both rust-native library or binary code, as well a library for compilation to Wasm and leveraging wasm-pack. We don't currently support any Javascript examples or frameworks that can use Wasm npm package explicitly, but this is on our radar. Additionally, when using the --bin flag you have the option to generate our axum template with all the 🔋's mentioned above.

Generate a project just like before and choose the rust+wasm template:

cargo generate --lib --git https://github.com/fission-codes/rust-template

Note: Currently, wasm-pack does not support building binary crates, so even with the --bin flag specified, a library will still be generated.

🔋 Batteries Included

  • wasm-bindgen for communicating between WebAssembly and JavaScript.
  • wasm-bindgen-futures for converting between Javascript Promises and Rust futures.
  • console_error_panic_hook for logging panic messages to the developer console.
  • js-sys for bindings to Javascript's standard, built-in objects.
  • web-sys for bindings to Web APIs like window.fetch, WebGL, WebAudio, etc. (optional, via feature-flag).

Generation in an existing project

The generator is also designed for templating within an existing project and prompts with this in mind. To generate in an existing project, run this command in the project root:

cargo generate --git https://github.com/fission-codes/rust-template --init

When taking this approach, please be aware that some of the generated code, e.g. benches, READMEs, etc., rely on dependencies or contain text that may not be set in or follow the layout of your existing Rust codebase, so please make the appropriate changes where needed.

If the generator detects a conflict, it will not alter your project in any way, failing with an error. We can't cover all the cases when extending an existing project. If you run into problems, open an issue.

Notes for Post-Project Generation

  • If using nix via Nix flake, please install nix and direnv to get started. Then, make sure to run direnv allow and add your files via git add.

  • If Codecov upload is enabled through GitHub Actions make sure to sync your project and gather tokens/badges. Read more here.

  • There are stock integration tests available for all templates, including a wasm-bindgen decorated test, #[wasm_bindgen_test], that can be tested with wasm-pack.

  • For CI/CD purposes, be aware there's some secrets you'll need to configure in Github, including:

    • CODECOV_TOKEN if you choose to use coverage via Codecov
    • CARGO_REGISTRY_TOKEN for publshing Rust packages to crates.io
    • NPM_TOKEN for publishing a Wasm project to npm
    • DOCKERHUB_USERNAME and DOCKERHUB_TOKEN for pushing containers to Docker Hub.

Contributing

🎈 We're thankful for any feedback and help in improving our template generator! We have a contributing guide to help you get involved. We also adhere to Fission's Code of Conduct.

Pre-commit Hook

This repository recommends using pre-commit for running pre-commit hooks. Please run this before every commit and/or push.

  • Once installed, Run pre-commit install and pre-commit install --hook-type commit-msg to setup the pre-commit hooks locally. This will reduce failed CI builds.
  • If you are doing interim commits locally, and for some reason if you don't want pre-commit hooks to fire, you can run git commit -a -m "Your message here" --no-verify.

Template References

Thanks

Major shout-outs to the various contributors of this work, including:

License

This project is licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.