ci-cargo

CI for Rust code.
git clone https://git.philomathiclife.com/repos/ci-cargo
Log | Files | Refs | README

README.md (21789B)


      1 CI app for Rust code
      2 ====================
      3 
      4 [<img alt="git" src="https://git.philomathiclife.com/badges/ci-cargo.svg" height="20">](https://git.philomathiclife.com/ci-cargo/log.html)
      5 [<img alt="crates.io" src="https://img.shields.io/crates/v/ci-cargo.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/ci-cargo)
      6 
      7 `ci-cargo` is a CLI application that runs [`cargo`](https://doc.rust-lang.org/cargo/index.html) with `check`,
      8 `clippy`, `test --tests`, and `test --doc` for all possible combinations of features defined in `Cargo.toml`.
      9 
     10 The toolchain(s) used depend on platform support for [`rustup`](https://rust-lang.github.io/rustup/), the existence
     11 of `rust-toolchain.toml`, the defined MSRV (if there is one), and if `--default-toolchain`, `--skip-msrv`, or
     12 `--rustup-home` were passed. Specifically `cargo +stable` will be used if all of the following conditions are met:
     13 
     14 * `--default-toolchain` was not passed.
     15 * `rust-toolchain.toml` does not exist in the package directory nor its ancestor directories.
     16 * `--rustup-home` was not passed for platforms that don't support `rustup`.
     17 
     18 If the above are not met, `cargo` will be used instead. `cargo +<MSRV>` will also be used if all of the following
     19 conditions are met:
     20 
     21 * `--skip-msrv` was not passed.
     22 * Package has an MSRV defined via `rust-version` that is less than the `stable` or not equivalent to the default
     23   toolchain used.
     24 * `--rustup-home` was passed or the platform supports `rustup`.
     25 
     26 `ci-cargo` avoids superfluous combinations of features. For example if feature `foo` depends on feature `bar` and
     27 `bar` depends on feature `fizz`; then no combination of features that contain `foo` and `bar`, `foo` and `fizz`, or
     28 `bar` and `fizz` will be tested.
     29 
     30 When a command errors, `ci-cargo` will terminate; upon termination (successful or not), `ci-cargo` will write all
     31 _unique_ messages that were written to `stderr` to `stderr` followed by the offending command in case of an error.
     32 
     33 ## Why is this useful?
     34 
     35 The number of possible configurations grows exponentially based on the number of features in `Cargo.toml`. This can
     36 easily cause a crate to not be tested with certain combinations of features. Instead of manually invoking `cargo`
     37 with each possible combination of features, this handles it automatically. Additionally it automatically ensures the
     38 build works on both the stable or default toolchain _and_ the stated MSRV (if one is defined).
     39 
     40 ## Commands
     41 
     42 * `<none>`: `cargo clippy` and `cargo test` are invoked for each combination of features.
     43 * `help`: Prints help message.
     44 * `version`: Prints version info.
     45 * `check`: `cargo check` is invoked for each combination of features.
     46 * `clippy`: `cargo clippy` is invoked for each combination of features.
     47 * `tests`: `cargo test --tests` is invoked for each combination of features.
     48 * `doc-tests`: `cargo test --doc` is invoked for each combination of features.
     49 
     50 ## Options
     51 
     52 * `--all-targets`: `cargo clippy --all-targets` or `cargo check --all-targets` is invoked for each combination of
     53   features.
     54 * `--allow-implied-features`: Features implied by optional dependencies are allowed; by default features must be
     55   explicitly defined or an error will occur (e.g., `foo = ["dep:bar"]`).
     56 * `--cargo-home <PATH>`: Sets the storage directory used by `cargo`.
     57 * `--cargo-path <PATH>`: Sets the directory to search for `cargo`. Defaults to `cargo`.
     58 * `--color`: `--color always` is passed to the above commands; otherwise without this option, `--color never` is
     59   passed.
     60 * `--default-toolchain`: `cargo` is used instead of `cargo +stable`.
     61 * `--deny-warnings`: `cargo clippy -- --Dwarnings` is invoked for each combination of features.
     62 * `--dir <PATH>`: Changes the working directory to the passed path before executing. Without this, the current
     63   directory and all ancestor directories are searched for `Cargo.toml` before changing the working directory to its
     64   location.
     65 * `--ignore-compile-errors`: [`compile_error`](https://doc.rust-lang.org/core/macro.compile_error.html)s are ignored
     66    and don't lead to termination.
     67 * `--ignore-features <FEATURES>`: Any combination of features that depend on any of the features in the
     68   comma-separated list will be ignored. The features must be unique and represent valid features in the package. The
     69   features can contain implied features so long as `--allow-implied-features` is also passed. An empty value
     70   represents the empty set of features (i.e., `--no-default-features`). For example `--ignore-features`, will error
     71   since no features were passed. `--ignore-features ''` will ignore the empty set of features. `--ignore-features a,`
     72   will ignore the empty set of features and any combination of features that depend on feature `a`.
     73 * `--ignore-msrv`: `--ignore-rust-version` is passed to the commands for the default toolchain.
     74 * `--ignored`: `cargo test -- --ignored` is invoked for each combination of features.
     75 * `--include-ignored`: `cargo test -- --include-ignored` is invoked for each combination of features.
     76 * `--progress`: Writes the current progress to `stdout`.
     77 * `--rustup-home <PATH>`: Sets the storage directory used by `rustup`.
     78 * `--skip-msrv`: `cargo +<MSRV>` is not used.
     79 * `--summary`: Writes the toolchain(s) used and the combinations of features run on to `stdout` on success.
     80 
     81 Any unique sequence of the above options are allowed to be passed after the command so long as the following
     82 conditions are met:
     83 
     84 * No options are allowed for the `help` or `version` commands.
     85 * `--all-targets` is allowed iff `check`, `clippy`, or no command is passed.
     86 * `--deny-warnings` is allowed iff `clippy` or no command is passed.
     87 * `--ignored` is allowed iff `tests` or no command is passed and `--include-ignored` is not passed.
     88 * `--include-ignored` is allowed iff `tests` or no command is passed and `--ignored` is not passed.
     89 * `--ignore-msrv` is allowed iff the `stable` toolchain is used.
     90 
     91 ## `ci-cargo` in action
     92 
     93 ```bash
     94 [zack@laptop example]$ cat Cargo.toml
     95 [package]
     96 authors = ["Johann Carl Friedrich Gauß <gauss@invalid.com>"]
     97 categories = ["mathematics"]
     98 description = "Example."
     99 documentation = "https://example.com/"
    100 edition = "2024"
    101 keywords = ["example"]
    102 license = "MIT OR Apache-2.0"
    103 name = "example"
    104 readme = "README.md"
    105 repository = "https://example.com/"
    106 rust-version = "1.89.0"
    107 version = "0.1.0"
    108 
    109 [lints.rust]
    110 ambiguous_negative_literals = { level = "deny", priority = -1 }
    111 closure_returning_async_block = { level = "deny", priority = -1 }
    112 deprecated_safe = { level = "deny", priority = -1 }
    113 deref_into_dyn_supertrait = { level = "deny", priority = -1 }
    114 ffi_unwind_calls = { level = "deny", priority = -1 }
    115 future_incompatible = { level = "deny", priority = -1 }
    116 impl_trait_redundant_captures = { level = "deny", priority = -1 }
    117 keyword_idents = { level = "deny", priority = -1 }
    118 let_underscore = { level = "deny", priority = -1 }
    119 linker_messages = { level = "deny", priority = -1 }
    120 macro_use_extern_crate = { level = "deny", priority = -1 }
    121 meta_variable_misuse = { level = "deny", priority = -1 }
    122 missing_copy_implementations = { level = "deny", priority = -1 }
    123 missing_debug_implementations = { level = "deny", priority = -1 }
    124 missing_docs = { level = "deny", priority = -1 }
    125 non_ascii_idents = { level = "deny", priority = -1 }
    126 nonstandard_style = { level = "deny", priority = -1 }
    127 redundant_imports = { level = "deny", priority = -1 }
    128 redundant_lifetimes = { level = "deny", priority = -1 }
    129 refining_impl_trait = { level = "deny", priority = -1 }
    130 rust_2018_compatibility = { level = "deny", priority = -1 }
    131 rust_2018_idioms = { level = "deny", priority = -1 }
    132 rust_2021_compatibility = { level = "deny", priority = -1 }
    133 rust_2024_compatibility = { level = "deny", priority = -1 }
    134 single_use_lifetimes = { level = "deny", priority = -1 }
    135 trivial_casts = { level = "deny", priority = -1 }
    136 trivial_numeric_casts = { level = "deny", priority = -1 }
    137 unit_bindings = { level = "deny", priority = -1 }
    138 unknown-or-malformed-diagnostic-attributes = { level = "deny", priority = -1 }
    139 unnameable_types = { level = "deny", priority = -1 }
    140 unreachable_pub = { level = "deny", priority = -1 }
    141 unsafe_code = { level = "deny", priority = -1 }
    142 unstable_features = { level = "deny", priority = -1 }
    143 unused = { level = "deny", priority = -1 }
    144 unused_crate_dependencies = { level = "deny", priority = -1 }
    145 unused_import_braces = { level = "deny", priority = -1 }
    146 unused_lifetimes = { level = "deny", priority = -1 }
    147 unused_qualifications = { level = "deny", priority = -1 }
    148 unused_results = { level = "deny", priority = -1 }
    149 variant_size_differences = { level = "deny", priority = -1 }
    150 warnings = { level = "deny", priority = -1 }
    151 
    152 [lints.clippy]
    153 cargo = { level = "deny", priority = -1 }
    154 complexity = { level = "deny", priority = -1 }
    155 correctness = { level = "deny", priority = -1 }
    156 nursery = { level = "deny", priority = -1 }
    157 pedantic = { level = "deny", priority = -1 }
    158 perf = { level = "deny", priority = -1 }
    159 restriction = { level = "deny", priority = -1 }
    160 style = { level = "deny", priority = -1 }
    161 suspicious = { level = "deny", priority = -1 }
    162 blanket_clippy_restriction_lints = "allow"
    163 implicit_return = "allow"
    164 
    165 [dependencies]
    166 buzz = { version = "0.1.0", default-features = false, optional = true }
    167 
    168 [features]
    169 buzz = ["dep:buzz"]
    170 default = ["foo"]
    171 foo = []
    172 bar = ["fizz"]
    173 fizz = []
    174 [zack@laptop example]$ ci-cargo --all-targets --include-ignored --progress --summary
    175 Package: example. Toolchain (1/2): cargo +stable. Features (1/32, 5 skipped): buzz,fizz,foo. Command (1/2): clippy. Time running: 0 s.
    176 Package: example. Toolchain (1/2): cargo +stable. Features (1/32, 5 skipped): buzz,fizz,foo. Command (2/2): test. Time running: 0 s.
    177 Package: example. Toolchain (1/2): cargo +stable. Features (2/32, 6 skipped): fizz,foo. Command (1/2): clippy. Time running: 0 s.
    178 Package: example. Toolchain (1/2): cargo +stable. Features (2/32, 6 skipped): fizz,foo. Command (2/2): test. Time running: 0 s.
    179 Package: example. Toolchain (1/2): cargo +stable. Features (3/32, 10 skipped): bar,buzz,foo. Command (1/2): clippy. Time running: 0 s.
    180 Package: example. Toolchain (1/2): cargo +stable. Features (3/32, 10 skipped): bar,buzz,foo. Command (2/2): test. Time running: 0 s.
    181 Package: example. Toolchain (1/2): cargo +stable. Features (4/32, 10 skipped): buzz,foo. Command (1/2): clippy. Time running: 0 s.
    182 Package: example. Toolchain (1/2): cargo +stable. Features (4/32, 10 skipped): buzz,foo. Command (2/2): test. Time running: 0 s.
    183 Package: example. Toolchain (1/2): cargo +stable. Features (5/32, 10 skipped): bar,foo. Command (1/2): clippy. Time running: 1 s.
    184 Package: example. Toolchain (1/2): cargo +stable. Features (5/32, 10 skipped): bar,foo. Command (2/2): test. Time running: 1 s.
    185 Package: example. Toolchain (1/2): cargo +stable. Features (6/32, 10 skipped): foo. Command (1/2): clippy. Time running: 1 s.
    186 Package: example. Toolchain (1/2): cargo +stable. Features (6/32, 10 skipped): foo. Command (2/2): test. Time running: 1 s.
    187 Package: example. Toolchain (1/2): cargo +stable. Features (7/32, 11 skipped): buzz,default,fizz. Command (1/2): clippy. Time running: 1 s.
    188 Package: example. Toolchain (1/2): cargo +stable. Features (7/32, 11 skipped): buzz,default,fizz. Command (2/2): test. Time running: 1 s.
    189 Package: example. Toolchain (1/2): cargo +stable. Features (8/32, 12 skipped): default,fizz. Command (1/2): clippy. Time running: 1 s.
    190 Package: example. Toolchain (1/2): cargo +stable. Features (8/32, 12 skipped): default,fizz. Command (2/2): test. Time running: 1 s.
    191 Package: example. Toolchain (1/2): cargo +stable. Features (9/32, 13 skipped): buzz,fizz. Command (1/2): clippy. Time running: 2 s.
    192 Package: example. Toolchain (1/2): cargo +stable. Features (9/32, 13 skipped): buzz,fizz. Command (2/2): test. Time running: 2 s.
    193 Package: example. Toolchain (1/2): cargo +stable. Features (10/32, 14 skipped): fizz. Command (1/2): clippy. Time running: 2 s.
    194 Package: example. Toolchain (1/2): cargo +stable. Features (10/32, 14 skipped): fizz. Command (2/2): test. Time running: 2 s.
    195 Package: example. Toolchain (1/2): cargo +stable. Features (11/32, 14 skipped): bar,buzz,default. Command (1/2): clippy. Time running: 2 s.
    196 Package: example. Toolchain (1/2): cargo +stable. Features (11/32, 14 skipped): bar,buzz,default. Command (2/2): test. Time running: 2 s.
    197 Package: example. Toolchain (1/2): cargo +stable. Features (12/32, 14 skipped): buzz,default. Command (1/2): clippy. Time running: 2 s.
    198 Package: example. Toolchain (1/2): cargo +stable. Features (12/32, 14 skipped): buzz,default. Command (2/2): test. Time running: 2 s.
    199 Package: example. Toolchain (1/2): cargo +stable. Features (13/32, 14 skipped): bar,default. Command (1/2): clippy. Time running: 3 s.
    200 Package: example. Toolchain (1/2): cargo +stable. Features (13/32, 14 skipped): bar,default. Command (2/2): test. Time running: 3 s.
    201 Package: example. Toolchain (1/2): cargo +stable. Features (14/32, 14 skipped): default. Command (1/2): clippy. Time running: 3 s.
    202 Package: example. Toolchain (1/2): cargo +stable. Features (14/32, 14 skipped): default. Command (2/2): test. Time running: 3 s.
    203 Package: example. Toolchain (1/2): cargo +stable. Features (15/32, 14 skipped): bar,buzz. Command (1/2): clippy. Time running: 3 s.
    204 Package: example. Toolchain (1/2): cargo +stable. Features (15/32, 14 skipped): bar,buzz. Command (2/2): test. Time running: 3 s.
    205 Package: example. Toolchain (1/2): cargo +stable. Features (16/32, 14 skipped): buzz. Command (1/2): clippy. Time running: 3 s.
    206 Package: example. Toolchain (1/2): cargo +stable. Features (16/32, 14 skipped): buzz. Command (2/2): test. Time running: 3 s.
    207 Package: example. Toolchain (1/2): cargo +stable. Features (17/32, 14 skipped): bar. Command (1/2): clippy. Time running: 4 s.
    208 Package: example. Toolchain (1/2): cargo +stable. Features (17/32, 14 skipped): bar. Command (2/2): test. Time running: 4 s.
    209 Package: example. Toolchain (1/2): cargo +stable. Features (18/32, 14 skipped): <none>. Command (1/2): clippy. Time running: 4 s.
    210 Package: example. Toolchain (1/2): cargo +stable. Features (18/32, 14 skipped): <none>. Command (2/2): test. Time running: 4 s.
    211 Package: example. Toolchain (2/2): cargo +1.89.0. Features (1/32, 5 skipped): buzz,fizz,foo. Command (1/2): clippy. Time running: 4 s.
    212 Package: example. Toolchain (2/2): cargo +1.89.0. Features (1/32, 5 skipped): buzz,fizz,foo. Command (2/2): test. Time running: 4 s.
    213 Package: example. Toolchain (2/2): cargo +1.89.0. Features (2/32, 6 skipped): fizz,foo. Command (1/2): clippy. Time running: 4 s.
    214 Package: example. Toolchain (2/2): cargo +1.89.0. Features (2/32, 6 skipped): fizz,foo. Command (2/2): test. Time running: 4 s.
    215 Package: example. Toolchain (2/2): cargo +1.89.0. Features (3/32, 10 skipped): bar,buzz,foo. Command (1/2): clippy. Time running: 5 s.
    216 Package: example. Toolchain (2/2): cargo +1.89.0. Features (3/32, 10 skipped): bar,buzz,foo. Command (2/2): test. Time running: 5 s.
    217 Package: example. Toolchain (2/2): cargo +1.89.0. Features (4/32, 10 skipped): buzz,foo. Command (1/2): clippy. Time running: 5 s.
    218 Package: example. Toolchain (2/2): cargo +1.89.0. Features (4/32, 10 skipped): buzz,foo. Command (2/2): test. Time running: 5 s.
    219 Package: example. Toolchain (2/2): cargo +1.89.0. Features (5/32, 10 skipped): bar,foo. Command (1/2): clippy. Time running: 5 s.
    220 Package: example. Toolchain (2/2): cargo +1.89.0. Features (5/32, 10 skipped): bar,foo. Command (2/2): test. Time running: 5 s.
    221 Package: example. Toolchain (2/2): cargo +1.89.0. Features (6/32, 10 skipped): foo. Command (1/2): clippy. Time running: 6 s.
    222 Package: example. Toolchain (2/2): cargo +1.89.0. Features (6/32, 10 skipped): foo. Command (2/2): test. Time running: 6 s.
    223 Package: example. Toolchain (2/2): cargo +1.89.0. Features (7/32, 11 skipped): buzz,default,fizz. Command (1/2): clippy. Time running: 6 s.
    224 Package: example. Toolchain (2/2): cargo +1.89.0. Features (7/32, 11 skipped): buzz,default,fizz. Command (2/2): test. Time running: 6 s.
    225 Package: example. Toolchain (2/2): cargo +1.89.0. Features (8/32, 12 skipped): default,fizz. Command (1/2): clippy. Time running: 6 s.
    226 Package: example. Toolchain (2/2): cargo +1.89.0. Features (8/32, 12 skipped): default,fizz. Command (2/2): test. Time running: 6 s.
    227 Package: example. Toolchain (2/2): cargo +1.89.0. Features (9/32, 13 skipped): buzz,fizz. Command (1/2): clippy. Time running: 6 s.
    228 Package: example. Toolchain (2/2): cargo +1.89.0. Features (9/32, 13 skipped): buzz,fizz. Command (2/2): test. Time running: 7 s.
    229 Package: example. Toolchain (2/2): cargo +1.89.0. Features (10/32, 14 skipped): fizz. Command (1/2): clippy. Time running: 7 s.
    230 Package: example. Toolchain (2/2): cargo +1.89.0. Features (10/32, 14 skipped): fizz. Command (2/2): test. Time running: 7 s.
    231 Package: example. Toolchain (2/2): cargo +1.89.0. Features (11/32, 14 skipped): bar,buzz,default. Command (1/2): clippy. Time running: 7 s.
    232 Package: example. Toolchain (2/2): cargo +1.89.0. Features (11/32, 14 skipped): bar,buzz,default. Command (2/2): test. Time running: 7 s.
    233 Package: example. Toolchain (2/2): cargo +1.89.0. Features (12/32, 14 skipped): buzz,default. Command (1/2): clippy. Time running: 7 s.
    234 Package: example. Toolchain (2/2): cargo +1.89.0. Features (12/32, 14 skipped): buzz,default. Command (2/2): test. Time running: 8 s.
    235 Package: example. Toolchain (2/2): cargo +1.89.0. Features (13/32, 14 skipped): bar,default. Command (1/2): clippy. Time running: 8 s.
    236 Package: example. Toolchain (2/2): cargo +1.89.0. Features (13/32, 14 skipped): bar,default. Command (2/2): test. Time running: 8 s.
    237 Package: example. Toolchain (2/2): cargo +1.89.0. Features (14/32, 14 skipped): default. Command (1/2): clippy. Time running: 8 s.
    238 Package: example. Toolchain (2/2): cargo +1.89.0. Features (14/32, 14 skipped): default. Command (2/2): test. Time running: 8 s.
    239 Package: example. Toolchain (2/2): cargo +1.89.0. Features (15/32, 14 skipped): bar,buzz. Command (1/2): clippy. Time running: 8 s.
    240 Package: example. Toolchain (2/2): cargo +1.89.0. Features (15/32, 14 skipped): bar,buzz. Command (2/2): test. Time running: 8 s.
    241 Package: example. Toolchain (2/2): cargo +1.89.0. Features (16/32, 14 skipped): buzz. Command (1/2): clippy. Time running: 9 s.
    242 Package: example. Toolchain (2/2): cargo +1.89.0. Features (16/32, 14 skipped): buzz. Command (2/2): test. Time running: 9 s.
    243 Package: example. Toolchain (2/2): cargo +1.89.0. Features (17/32, 14 skipped): bar. Command (1/2): clippy. Time running: 9 s.
    244 Package: example. Toolchain (2/2): cargo +1.89.0. Features (17/32, 14 skipped): bar. Command (2/2): test. Time running: 9 s.
    245 Package: example. Toolchain (2/2): cargo +1.89.0. Features (18/32, 14 skipped): <none>. Command (1/2): clippy. Time running: 9 s.
    246 Package: example. Toolchain (2/2): cargo +1.89.0. Features (18/32, 14 skipped): <none>. Command (2/2): test. Time running: 9 s.
    247 Toolchains used: cargo +stable and cargo +1.89.0
    248 Features used:
    249 buzz,fizz,foo
    250 fizz,foo
    251 bar,buzz,foo
    252 buzz,foo
    253 bar,foo
    254 foo
    255 buzz,default,fizz
    256 default,fizz
    257 buzz,fizz
    258 fizz
    259 bar,buzz,default
    260 buzz,default
    261 bar,default
    262 default
    263 bar,buzz
    264 buzz
    265 bar
    266 <none>
    267 [zack@laptop example]$ ci-cargo clippy --deny-warnings --ignore-compile-errors --ignore-features buzz, --ignore-msrv --skip-msrv
    268 [zack@laptop ~]$ ci-cargo tests --allow-implied-features --cargo-home ~/.cargo/ --cargo-path ~/.cargo/bin --default-toolchain --dir ~/example/ --ignored --rustup-home ~/.rustup/
    269 [zack@laptop ~]$ ci-cargo version
    270 ci-cargo 0.1.0
    271 [zack@laptop example]$ ci-cargo --summary doc-tests
    272 doc-tests is an unknown argument. See ci-cargo help for more information.
    273 ```
    274 
    275 ## Limitations
    276 
    277 Functionality when using old versions of `cargo` may not exist (e.g., `cargo check` wasn't available until 1.16.0).
    278 
    279 There is a hard limit on the number of features allowed. Specifically the number of features can't exceed the
    280 number of bits that make up a pointer; however practical limits will almost always be a factor long before hitting
    281 such a hard limit due to the exponential effect features have.
    282 
    283 Cyclic and redundant features are forbidden. For example the below snippets from `Cargo.toml` files will cause an
    284 error:
    285 
    286 ```toml
    287 [features]
    288 # Loops are forbidden by `cargo`, so this is not an additional limitation.
    289 a = ["a"]
    290 [features]
    291 # Cyclic features are disallowed even though `cargo` allows them.
    292 a = ["b"]
    293 b = ["a"]
    294 [features]
    295 # `a` should more concisely be assigned `["b"]` since `b` already depends on `c`.
    296 a = ["b", "c"]
    297 b = ["c"]
    298 c = []
    299 ```
    300 
    301 ## Minimum Supported Rust Version (MSRV)
    302 
    303 This will frequently be updated to be the same as stable. Specifically, any time stable is updated and that
    304 update has "useful" features or compilation no longer succeeds (e.g., due to new compiler lints), then MSRV
    305 will be updated.
    306 
    307 MSRV changes will correspond to a SemVer patch version bump pre-`1.0.0`; otherwise a minor version bump.
    308 
    309 ## SemVer Policy
    310 
    311 * All on-by-default features of this library are covered by SemVer
    312 * MSRV is considered exempt from SemVer as noted above
    313 
    314 ## License
    315 
    316 Licensed under either of
    317 
    318 * Apache License, Version 2.0 ([LICENSE-APACHE](https://www.apache.org/licenses/LICENSE-2.0))
    319 * MIT license ([LICENSE-MIT](https://opensource.org/licenses/MIT))
    320 
    321 at your option.
    322 
    323 ## Contribution
    324 
    325 Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you,
    326 as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
    327 
    328 Before any PR is sent, `ci-cargo --all-targets --include-ignored` should be run on itself. Additionally
    329 `cargo +nightly doc` should be run to ensure documentation can be built.
    330 
    331 ### Status
    332 
    333 The crate is only tested on the `x86_64-unknown-linux-gnu`, `x86_64-unknown-openbsd`, and `aarch64-apple-darwin`
    334 targets; but it should work on most platforms.