Please read this document before contributing!
Thank you for contributing! We welcome your contributions in whichever form they may come.
This document provides guidelines and resources to help you successfully contribute to the project. Rocket is a tool designed to push the envelope of usability, security, and performance in web frameworks, and accordingly, our quality standards are high. To make the best use of everyone's time and avoid wasted efforts, take a moment to understand our expectations and conventions outlined here.
Before creating a new pull request:
- Read and understand Code Style Conventions, Commit Message Guidelines, and Testing.
- If you're resolving an open issue, follow Resolving an Open Issue.
- If you're implementing new functionality, check whether the functionality you're implementing has been proposed before, either as an issue or pull request. Ensure your PR resolves any previously raised concerns. Then, follow Implementing an Unproposed Feature.
- For everything else, see Other Common Contributions.
We aim to keep Rocket's code quality at the highest level. This means that any code you contribute must be:
- Commented: Complex or subtle functionality must be properly commented.
- Documented: Public items must have doc comments with examples.
- Styled: Your code must folow the Code Style Conventions.
- Simple: Your code should accomplish its task as simply and idiomatically as possible.
- Tested: You must write (and pass) convincing tests for all new or changed functionality.
- Focused: Your code should do what it's supposed to and nothing more.
If you spot an open issue that you'd like to resolve:
-
First identify if there's a proposed solution to the problem.
If there is, proceed to step 2. If there isn't, your first course of action, before writing any code, is to propose a solution. To do so, leave a comment describing your solution in the relevant issue. It's especially useful to see test cases and hypothetical examples. This step is critical: it allows us to identify and resolve concerns with a proposed solution before anyone spends time writing code. It may also allow us to point you in more efficient implementation directions.
-
Write a failing test case you expect to pass after resolving the issue.
If you can write proper tests cases that fail, do so (see Testing). If you cannot, for instance because you're introducing new APIs which can't be used until they exist, write a test case that mocks usage of those APIs. In either case, allow the tests and mock examples to guide your progress.
-
Write basic functionality, pass tests, and submit a PR.
Think about edge cases to the problem and ensure you have tests for those edge cases. Once your implementation is functionally complete, submit a PR. Don't spend time writing or changing a bunch of documentation just yet.
-
Wait for a review, iterate, and polish.
If a review doesn't come in a few days, feel free to ping a maintainer. Once somene reviews your PR, integrate their feedback. If the PR solves the issue (which it should because you have passing tests) and fits the project (which it should since you sought feedback before submitting), it will be conditionally approved pending final polish: documentation (rustdocs, guide docs), style improvements, and testing. Your PR will then be merged.
First and foremost, please do not submit a PR that implements a new feature without first proposing a design and seeking feedback. We take the addition of new features very seriously because they directly impact usability.
To propose a new feature, create a new feature request issue and follow the template. Note that certain classes of features require particularly compelling justification to be taken into consideration. These include features that:
- Can be implemented outside of Rocket.
- Introduce new dependencies, especially heavier ones.
- Only exist to add support for an external crate.
- Are too specific to one use-case.
- Are overtly complex and have "simple" workarounds.
- Only partially solve a bigger deficiency.
Once your feature request is accepted, follow Resolving an Open Issue.
-
Doc fixes, typos, wording improvements.
We encourage any of these! Just a submit a PR with your changes. Please preserve the surrounding markdown formatting as much as possible. This typically means keeping lines under 80 characters, keeping table delimiters aligned, and preserving indentation accordingly.
The guide's source files are at docs/guide. Note the following special syntax available in guide markdown:
- Cross-linking pages is accomplished via relative links. Outside
of the index, this is:
../{page}#anchor
. For instance, to link to Quickstart > Running Examples, use../quickstart#running-examples
. - Aliases are shorthand URLs that start with
@
(e.g,@api
). They are used throughout the guide to simplify creating versioned URLs. They are replaced at build time with the appropriate versioned instance.
- Cross-linking pages is accomplished via relative links. Outside
of the index, this is:
-
New examples or changes to existing ones.
Please follow the Implementing an Unproposed Feature process.
-
Formatting or other purely cosmetic changes.
We generally do not accept purely cosmetic changes to the codebase such as style or formatting changes. All PRs must add something substantial to Rocket's functionality, coherence, testing, performance, usability, bug fixes, security, documentation, or overall maintainability.
-
Advertisements of any nature.
We do not accept any contributions that resemble advertisements or promotional content. If you are interested in supporting Rocket, we encourage you to sponsor the project.
All testing happens through test.sh. Before submitting a PR, run the script
and fix any issues. The default mode (passing no arguments or --default
) will
usually suffice, but you may also wish to execute additional tests. In
particular:
- If you make changes to
contrib
:test.sh --contrib
- If you make user-facing API changes or update deps:
test.sh --examples
- If you add or modify feature flags:
test.sh --core
- If you modify codegen: see UI Tests.
Run test.sh --help
to get an overview of how to execute the script:
USAGE:
./scripts/test.sh [+<TOOLCHAIN>] [--help|-h] [--<TEST>]
OPTIONS:
+<TOOLCHAIN> Forwarded to Cargo to select toolchain.
--help, -h Print this help message and exit.
--<TEST> Run the specified test suite.
(Run without --<TEST> to run default tests.)
AVAILABLE <TEST> OPTIONS:
default
all
core
contrib
examples
benchmarks
testbench
ui
EXAMPLES:
./scripts/test.sh # Run default tests on current toolchain.
./scripts/test.sh +stable --all # Run all tests on stable toolchain.
./scripts/test.sh --ui # Run UI tests on current toolchain.
Rocket is tested in a variety of ways. This includes via Rust's regular testing facilities such as doctests, unit tests, and integration tests, as well Rocket's examples, testbench, and UI Tests:
-
Examples: The
examples
directory contains applications that make use of many of Rocket's features. Each example is integration tested using Rocket's built-in local testing. This both ensures that typical Rocket code continues to work as expected and serves as a way to detect and resolve user-facing breaking changes. -
Testbench: Rocket's testbench tests end-to-end server or protocol properties by starting up full Rocket servers to which it dispatches real HTTP requests. Each server is independently written in testbench/src/servers/. You're unlikely to need to write a testbench test unless you're modifying low-level details.
-
UI Tests: UI tests ensure Rocket's codegen produces meaningful compiler diagnostics. They compile Rocket applications and compare the compiler's output to expected results. If you're changing codegen, you'll need to update or create UI tests. See UI Tests for details.
For any change that affects functionality, we ask that you write a test that verifies that functionality. Minimally, this means a unit test, doctest, integration test, or some combination of these. For small changes, unit tests will likely suffice. If the change affects the user in any way, then doctests should be added or modified. And if the change requires using unrelated APIs to test, then an integration test should be added.
Additionally, the following scenarios require special attention:
-
Improved Features
Modifying an existing example is a great place to write tests for improved features. If you do modify an example, make sure you modify the README in the example directory, too.
-
New Features
For major features, introducing a new example that showcases idiomatic use of the feature can be useful. Make sure you modify the README in the
examples
directory if you do. In addition, all newly introduced public APIs should be fully documented and include doctests as well as unit and integration tests. -
Fixing a Bug
To avoid regressions, always introduce or modify an integration or testbench test for a bugfix. Integration tests should live in the usual
tests/
directory and be namedshort-issue-description-NNNN.rs
, whereNNNN
is the GitHub issue number for the bug. For example,forward-includes-status-1560.rs
.
Changes to codegen (i.e, rocket_codegen
and other _codegen
crates)
necessitate adding and running UI tests, which capture compiler output and
compare it against some expected output. UI tests use trybuild
.
Tests can be found in the codegen/tests/ui-fail
directories of respective
codegen
crates. Each test is symlinked into sibling ui-fail-stable
and
ui-fail-nightly
directories, which also contain the expected error output for
stable and nightly compilers, respectively. For example:
./core/codegen/tests
├── ui-fail
│ ├── async-entry.rs
│ ├── ...
│ └── uri_display_type_errors.rs
├── ui-fail-nightly
│ ├── async-entry.rs -> ../ui-fail/async-entry.rs
│ ├── async-entry.stderr
│ ├── ...
│ ├── uri_display_type_errors.rs -> ../ui-fail/uri_display_type_errors.rs
│ └── uri_display_type_errors.stderr
└── ui-fail-stable
├── async-entry.rs -> ../ui-fail/async-entry.rs
├── async-entry.stderr
├── ...
├── uri_display_type_errors.rs -> ../ui-fail/uri_display_type_errors.rs
└── uri_display_type_errors.stderr
If you make changes to codegen, run the UI tests for stable and nightly with
test.sh +stable --ui
and test.sh +nightly --ui
. If there are failures,
update the outputs with TRYBUILD=overwrite test.sh +nightly --ui
and
TRYBUILD=overwrite test.sh +stable --ui
. Look at the diff to see what's
changed. Ensure that error messages properly attribute (i.e., visually underline
or point to) the source of the error. For example, if a type need to implement a
trait, then that type should be underlined. We strive to emit the most helpful
and descriptive error messages possible.
If you make changes to documentation, you should build the API docs and verify
that your changes look as you expect. API documentation is built with
mk-docs.sh and output to the usual target/docs
directory. By default, the
script will clean
any existing docs to avoid potential caching issues. To
override this behavior, use mk-docs.sh -d
.
We do not use rustfmt
or cargo fmt
due to bugs and missing functionality.
Instead, we ask that you follow the Rust Style Guide with the following
changes:
Always separate items with one blank line.
✅ Yes | No 🚫 |
---|---|
fn foo() {
// ..
}
fn bar() {
// ..
} |
fn foo() {
// ..
}
fn bar() {
// ..
} |
Prefer a where-clause over block-indented generics.
✅ Yes | No 🚫 |
---|---|
fn foo<T, U>(x: Vec<T>, y: Vec<U>)
where T: Display, U: Debug
{
// ..
} |
fn foo<
T: Display,
U: Debug,
>(x: Vec<T>, y: Vec<U>) {
// ..
} |
For "short" where-clauses, follow Rust guidelines. For "long" where-clauses,
block-indent where
, place the first bound on the same line as where
, and
block-align the remaining bounds.
✅ Yes | No 🚫 |
---|---|
fn foo<T, F, Item, G>(v: Foo<T, F, Item>) -> G
where T: for<'x> SomeTrait<'x>
F: Fn(Item) -> G,
Item: Display + Debug,
G: Error,
{
// ..
} |
fn foo<T, F, Item, G>(v: Foo<T, F, Item>) -> G
where
T: for<'x> SomeTrait<'x>
F: Fn(Item) -> G,
Item: Display + Debug,
G: Error,
{
// ..
} |
Do not use multi-line imports. Use multiple lines grouped by import kind if possible.
✅ Yes | No 🚫 |
---|---|
use foo::{Long, List, Of, Type, Imports};
use foo::{some_macro, imports}; |
use foo::{
Long, List, Of, Type, Imports,
some_macro, imports,
}; |
Order imports in order of decreasing "distance" to the current module: std
,
core
, and alloc
, external crates, then current crate. Prefer using crate
relative imports to super
. Separate each category with one blank line.
✅ Yes | No 🚫 |
---|---|
use std::{foo, bar};
use alloc::{bar, baz};
use either::Either;
use futures::{SomeItem, OtherItem};
use crate::{item1, item2};
use crate::module::item3;
use crate::module2::item4; |
use crate::{item1, item2};
use std::{foo, bar};
use either::Either;
use alloc::{bar, baz};
use futures::{SomeItem, OtherItem};
use super::{item3, item4};
use super::item4; |
Git commit messages should start with a single-line header of at most 50 characters followed by a body with any number of descriptive paragraphs, with lines not to exceed 72 characters, and a footer.
The header must be an imperative statement that precisely describes the primary change made by the commit. The goal is to give the reader a good understanding of what the commit does via only the header. It should not require context to understand. It should not include references to git commits or issues. Avoid using Markdown in the header if possible.
Typically, the first word in the header will be one of the following:
- Fix - to fix a functional or doc bug
- Example:
Fix 'TcpListener': allow 'udp://' prefix.
- Example:
- Improve - for minor feature or doc improvements
- Example:
Improve 'FromParam' derive error messages.
- Example:
- Introduce - for major feature introductions
- Example:
Introduce WebSocket support.
- Example:
- Add, Remove - for changes
- Example:
Add 'Foo::new()' constructor.
- Example:
Remove 'Foo::new()'; add 'Foo::build()'.
- Example:
- Update - for crate updates
- Example:
Update 'base64' to 0.12.
- Example:
- Impl or Implement - for trait implementations
- Example:
Implement 'FromForm' for 'ThisNewType'.
- Example:
Note how generic words like "change" are avoided, and how the headers are
specific about the changes they made. You need not limit yourself to this
vocabulary. When in doubt, consult the git log
for examples.
✅ Yes | No 🚫 |
---|---|
Fix 'FromForm' derive docs typo: 'yis' -> 'yes'. | |
Default 'MsgPack' to named variant. | |
Fix 'Compact' advice in 'MsgPack' docs. | |
Improve 'Sentinel' docs: explain 'Sentry'. | |
Fix CI: pin macOS CI 'mysql-client' to '8.4'. | |
Fix link to 'rocket::build()' in config guide. |
The body should describe what the commit does. For example, if the commit introduces a new feature it should describe what the feature enables and how it enables it. A body may be unnecessary if the header sufficiently describes the commit. Avoid referencing issues in the body as well: we'll do that in the footer. If you reference a commit, reference it by shorthash only. Feel free to use markdown including lists and code.
Finally, the footer is where references to issues should be made. See the GitHub's linked issues documentation.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in Rocket by you shall be dual licensed under the MIT License and Apache License, Version 2.0, without any additional terms or conditions.
The Rocket website docs are licensed under separate terms. Any contribution intentionally submitted for inclusion in the Rocket website docs by you shall be licensed under those terms.