diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 36e491c..a4afdfe 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -6,29 +6,38 @@ on:
- master
- dev
- boilerplate
+ - idiomatic
+ - rc1
+ - rc2
+ - rc3
pull_request:
+permissions:
+ contents: read
+
+defaults:
+ run:
+ shell: bash
+
env:
CARGO_TERM_COLOR: always
jobs:
check:
- name: Test, Clippy, Fmt, Checkers
+ name: Test, Clippy, Fmt, Docs, Checkers, Package
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- - uses: dtolnay/rust-toolchain@stable
+ - uses: dtolnay/rust-toolchain@master
with:
- components: clippy
-
- - name: Install nightly rustfmt
- run: rustup toolchain install nightly --profile minimal --component rustfmt
+ toolchain: nightly-2026-08-08
+ components: clippy, rustfmt
- uses: Swatinem/rust-cache@v2
- - name: cargo test
- run: cargo test --locked
+ - name: cargo test (all targets)
+ run: cargo test --all-targets --locked
- name: cargo test (no default features)
run: cargo test --no-default-features --locked
@@ -46,7 +55,7 @@ jobs:
run: cargo test --no-default-features --features full --locked
- name: cargo clippy
- run: cargo clippy --all-targets --locked -- -D warnings
+ run: cargo clippy --all-targets --all-features --locked -- -D warnings
- name: cargo build (examples)
run: cargo build --examples --locked
@@ -55,7 +64,7 @@ jobs:
run: cargo build --example character-play --features test-regex --locked
- name: cargo doc
- run: cargo doc --no-deps --locked
+ run: cargo doc --no-deps --all-features --locked
- name: rustfmt
run: ./scripts/fmt --check
@@ -69,6 +78,9 @@ jobs:
- name: DERIVE_LAYOUT checker
run: python3 scripts/check_derives.py
+ - name: cargo publish (dry run)
+ run: cargo publish --dry-run --locked
+
msrv:
name: MSRV (1.79)
runs-on: ubuntu-latest
diff --git a/CHANGES.md b/CHANGES.md
index 8c3d927..06e414e 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,4 +1,12 @@
-# shwild.Rust - CHANGES
+# shwild.Rust - Changes
+
+
+## 0.2.1 - 31st August 2026
+
+* completed CI coverage for feature combinations, examples, documentation, Clippy, formatting, custom checkers, and package validation;
+* pinned the nightly Rust toolchain used for project formatting;
+* completed Cargo package metadata, docs.rs configuration, and package exclusions;
+* updated **README.md** and example catalogues with current feature and source-link information;
## 0.2.0 - 10th July 2026
diff --git a/Cargo.lock b/Cargo.lock
index 6690ee4..36c879f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -354,7 +354,7 @@ checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
[[package]]
name = "shwild"
-version = "0.2.0"
+version = "0.2.1"
dependencies = [
"base-traits",
"collect-rs",
diff --git a/Cargo.toml b/Cargo.toml
index 8f97573..78b960b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,16 +10,22 @@ authors = [
]
categories = [
"command-line-interface",
- "parser-implementations",
"development-tools",
+ "parser-implementations",
"text-processing",
]
description = "Shell-compatible wildcard matching library"
documentation = "https://docs.rs/shwild"
edition = "2021"
exclude = [
+ ".cargo",
+ ".cursor",
+ ".github",
+ ".vimrc",
+ ".vscode",
+ "Cargo.toml.orig",
+ "scripts",
"target",
- ".github"
]
homepage = "https://github.com/synesissoftware/shwild.Rust"
keywords = [
@@ -32,7 +38,12 @@ name = "shwild"
readme = "README.md"
repository = "https://github.com/synesissoftware/shwild.Rust"
rust-version = "1.79"
-version = "0.2.0"
+version = "0.2.1"
+
+[package.metadata.docs.rs]
+features = [
+ "full",
+]
# ##########################################################
diff --git a/EXAMPLES.md b/EXAMPLES.md
index 6f9dd90..9ac7e7e 100644
--- a/EXAMPLES.md
+++ b/EXAMPLES.md
@@ -1,10 +1,10 @@
-# shwild.Rust Examples
+# shwild.Rust - Examples
-|Name|Source & Description|Summary|
-|---|---|---|
-|**list-matching-files**|[examples/list-matching-files/main.rs](/examples/list-matching-files/main.rs)
[examples/list-matching-files.md](/examples/list-matching-files.md)|Lists files in the current directory whose paths match one or more shell wildcard pattern(s), using `shwild::matches()`.|
-|**list-matching-files-compiled**|[examples/list-matching-files-compiled/main.rs](/examples/list-matching-files-compiled/main.rs)
[examples/list-matching-files-compiled.md](/examples/list-matching-files-compiled.md)|Same as **list-matching-files**, but patterns are compiled once into `CompiledMatcher` instances before matching.|
-|**character-play**|[test/scratch/character-play/main.rs](/test/scratch/character-play/main.rs)
[examples/character-play.md](/examples/character-play.md)|Scratch program exercising **regex** crate Unicode matching behaviour (requires feature `"test-regex"`).|
+| Name | Source & Description | Summary |
+| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
+| **list-matching-files** | [Source](./examples/list-matching-files/main.rs)
[Description](./examples/list-matching-files.md) | Lists matching files using `shwild::matches()`. |
+| **list-matching-files-compiled** | [Source](./examples/list-matching-files-compiled/main.rs)
[Description](./examples/list-matching-files-compiled.md) | Lists matching files using compiled `CompiledMatcher` instances. |
+| **character-play** | [Scratch source](./test/scratch/character-play/main.rs)
[Description](./examples/character-play.md) | Exercises **regex** Unicode matching (requires feature `"test-regex"`). |
diff --git a/NEWS.md b/NEWS.md
index 07e1a19..b156586 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,9 +1,10 @@
-# shwild.Rust - NEWS
+# shwild.Rust - News
| Date | News Item |
| --------------------- | ----------------------------------------- |
-| 10th July 2026 | shwild.Rust 0.2.0 released |
-| 9th July 2026 | shwild.Rust 0.1.6 released |
+| 31st August 2026 | [shwild.Rust 0.2.1](https://github.com/synesissoftware/shwild.Rust/releases/tag/0.2.1) released |
+| 10th July 2026 | [shwild.Rust 0.2.0](https://github.com/synesissoftware/shwild.Rust/releases/tag/0.2.0) released |
+| 9th July 2026 | [shwild.Rust 0.1.6](https://github.com/synesissoftware/shwild.Rust/releases/tag/0.1.6) released |
| 9th July 2026 | shwild.Rust 0.1.5 released |
| 7th July 2026 | shwild.Rust 0.1.4 released |
| 28th March 2025 | shwild.Rust 0.1.3 released |
diff --git a/README.md b/README.md
index 49bc488..1b9db57 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,9 @@
# shwild.Rust
+**SH**ell-compatible **WILD**cards, for **Rust** β part of the cross-language
+**shwild** family.
+
+

[](https://opensource.org/licenses/BSD-3-Clause)
[](https://crates.io/crates/shwild)
@@ -9,8 +13,6 @@
[](https://github.com/synesissoftware/shwild.Rust/commits/master)
[](https://docs.rs/shwild)
-**SH**ell-compatible **WILD**cards, for **Rust** β part of the cross-language **shwild** family.
-
## Table of Contents
@@ -37,9 +39,12 @@
## Introduction
-**shwild** is a small, standalone library, implemented in C++ with a C and a C++ API, that provides shell-compatible wildcard matching. **shwild.Rust** is a **Rust** port, with minimal API differences. The design emphasis is on simplicity-of-use, modularity, and performance.
+**shwild** is a small, standalone C++ library with C and C++ APIs that
+provides shell-compatible wildcard matching. **shwild.Rust** is a **Rust**
+port with minimal API differences. The design emphasis is on
+simplicity-of-use, modularity, and performance.
-```Rust
+```rust
let pattern = r"Where are the* [πΌπ»]s\?";
assert_eq!(Ok(false), shwild_matches!(pattern, ""));
@@ -84,20 +89,21 @@ The constant `IGNORE_CASE` causes matching to ignore case.
The `shwild::Error` enum is used to represent a parse result, defined as:
-```Rust
+```rust
pub enum Error {
/// Parse error encountered.
ParseError {
- line : usize,
- column : usize,
- message : String,
+ line: usize,
+ column: usize,
+ message: String,
},
}
```
-The `shwild::Result` enum is a specialized `std::result::Result` type for **shwild**, defined as:
+The `shwild::Result` type is a specialized `std::result::Result` type for
+**shwild**, defined as:
-```Rust
+```rust
pub type Result = std_result::Result;
```
@@ -106,26 +112,28 @@ pub type Result = std_result::Result;
The following crate features are defined:
-| Name | Effect | Is `"default"`? | Dependent feature(s) |
-| --------------------------- | ------------------------------------- | --------------- | ------------------------------------- |
-| `"assertions"` | Provides `assert_shwild_matches!()` and `assert_shwild_not_matches!()` test assertion macros (via **base-traits** `AsI64`) | Yes | |
-| `"lookup-ranges"` | Causes match/non-match ranges to be implemented in terms of `UnicodePointMap` (from **collect-rs** crate), resulting in significant performance improvements in parsing and matching | Yes | |
-| `"null-feature"` | A feature that has no effect (and, thus, is useful for simplifying driver scripts) | **No** | |
-| `"test-regex"` | Introduces a dependency to **regex** crate to support benchmark/example program(s) | **No** | |
+| Name | Effect | Is `"default"`? | Dependent feature(s) |
+| ----------------------- | -------------------------------------------------------------------------------- | --------------- | -------------------- |
+| `"assertions"` | Provides test assertion macros; enabled by default | Yes | |
+| `"flexible-flags-type"` | Allows macro flags to use types implementing `base_traits::AsI64` | **No** | **base-traits** |
+| `"full"` | Enables all user-facing runtime features | **No** | |
+| `"lookup-ranges"` | Uses **collect-rs** `UnicodePointMap` for more efficient range matching | Yes | **collect-rs** |
+| `"null-feature"` | Has no effect; useful for simplifying driver scripts | **No** | |
+| `"test-regex"` | Enables **regex** support for benchmark and scratch/example programs | **No** | **regex** |
### Functions
-The `shwild::matches()` function attempts to parse a `pattern` according to `flags` and then match against it the string `input`.
+The `shwild::matches()` function attempts to parse a `pattern` according to
+`flags` and then match the string `input` against it.
-```Rust
-pub mod shwild {
-
- pub fn matches(
- pattern : &str,
- input : &str,
- flags : i64,
- ) -> Result;
+```rust
+pub fn matches(
+ pattern: &str,
+ input: &str,
+ flags: i64,
+) -> Result {
+ // ...
}
```
@@ -134,9 +142,16 @@ pub mod shwild {
The `shwild::shwild_matches!()` macro is a shorthand for the `shwild::matches()` function, providing 2-parameter and 3-parameter forms. The 2-parameter form passes 0 for the `flags` parameter.
-The `shwild::assert_shwild_matches!()` and `shwild::assert_shwild_not_matches!()` macros are test-oriented counterparts that panic on failure. Each provides 2-parameter and 3-parameter forms; the 2-parameter form passes 0 for the `flags` parameter. A parse error in the pattern panics with a descriptive message rather than returning `Err`. They are provided only when the feature `"assertions"` is enabled, which it is by default.
+The `shwild::assert_shwild_matches!()` and
+`shwild::assert_shwild_not_matches!()` macros are test-oriented counterparts
+that panic on failure. Each provides 2-parameter and 3-parameter forms; the
+2-parameter form passes 0 for the `flags` parameter. A parse error in the
+pattern panics with a descriptive message rather than returning `Err`. They
+are provided only when the feature `"assertions"` is enabled, which it is by
+default. The optional `"flexible-flags-type"` feature also allows the
+3-parameter forms to accept types implementing `base_traits::AsI64`.
-```Rust
+```rust
use shwild::{
assert_shwild_matches,
assert_shwild_not_matches,
@@ -153,7 +168,7 @@ The `shwild::assert_shwild_matches!()` and `shwild::assert_shwild_not_matches!()
The `shwild::CompiledMatcher` structure is the data structure that is used to parse the pattern and then test the input string. Because there is a small, but non-zero, cost to parsing patterns - and complex patterns more so, of course - so if matching is to be repeated in a context where performance costs matter then you may prefer to create an instance of `CompiledMatcher` and then use it to test against, as in:
-```Rust
+```rust
let pattern = r"Where are the* [πΌπ»]s\?";
let flags = 0;
@@ -170,7 +185,7 @@ The `shwild::CompiledMatcher` structure is the data structure that is used to pa
If you are ever need to get an understanding about the parsed state you can use the `Debug` implementation for the `CompiledMatcher`, as in:
-```Rust
+```rust
// a pattern for rudimentary Windows path names
let pattern = r"[A-Z]\?*\?*.[ce][ox][em]";
@@ -205,11 +220,17 @@ Defect reports, feature requests, and pull requests are welcome on https://githu
### Dependencies
-**shwild.Rust** has three optional dependencies:
+**shwild.Rust** has three optional runtime dependencies:
-* [**base-traits**](https://github.com/synesissoftware/base-traits) - required if feature `"assertions"` is specified; supports the `flags` parameter type in `assert_shwild_matches!()` and `assert_shwild_not_matches!()` via `AsI64`;
-* [**collect-rs**](https://github.com/synesissoftware/collect-rs) - required if feature `"lookup-ranges"` is specified, for more efficient range matching;
-* [**regex**](https://github.com/rust-lang/regex) - required, by some benchmark/example programs only, if feature `"test-regex"` is specified;
+* [**base-traits**](https://github.com/synesissoftware/base-traits) -
+ required if feature `"flexible-flags-type"` is specified, for
+ `base_traits::AsI64`;
+* [**collect-rs**](https://github.com/synesissoftware/collect-rs) - required
+ if feature `"lookup-ranges"` is specified, for more efficient range
+ matching;
+* [**regex**](https://github.com/rust-lang/regex) - required by some
+ benchmark and scratch/example programs if feature `"test-regex"` is
+ specified;
#### Dev Dependencies
@@ -219,13 +240,16 @@ Crates upon which **shwild** has development dependencies:
* [**criterion**](https://github.com/bheisler/criterion.rs);
* [**test_help-rs**](https://github.com/synesissoftware/test_help-rs);
+The committed **Cargo.lock** is retained for reproducible development and CI
+builds; locked Cargo commands are used throughout the workflow.
+
### Related projects
-* [**shwild**](https://github.com/synesissoftware/shwild/);
-* [**shwild.Go**](https://github.com/synesissoftware/shwild.Go/);
* [**base-traits**](https://github.com/synesissoftware/base-traits/);
* [**collect-rs**](https://github.com/synesissoftware/collect-rs/);
+* [**shwild**](https://github.com/synesissoftware/shwild/);
+* [**shwild.Go**](https://github.com/synesissoftware/shwild.Go/);
### License
@@ -233,5 +257,6 @@ Crates upon which **shwild** has development dependencies:
**shwild** is released under the 3-clause BSD license. See [LICENSE](./LICENSE) for details.
+
diff --git a/TODO.md b/TODO.md
index ab1788e..2643512 100644
--- a/TODO.md
+++ b/TODO.md
@@ -3,62 +3,64 @@
## Table of Contents
-- [0.1.4 boilerplate checklist](#014-boilerplate-checklist)
+- [Historical 0.1.4 boilerplate checklist](#historical-014-boilerplate-checklist)
+ - [Cargo packaging](#cargo-packaging)
+ - [CI](#ci)
+ - [Documentation](#documentation)
+ - [Formatting and quality tooling](#formatting-and-quality-tooling)
+ - [Layout and structure](#layout-and-structure)
- [Source layout (deferred)](#source-layout-deferred)
- [Functional improvements](#functional-improvements)
- [Performance improvements](#performance-improvements)
-## 0.1.4 boilerplate checklist
+## Historical 0.1.4 boilerplate checklist
### Cargo packaging
-* [x] `default-features = false` on optional and dev dependencies;
-* [x] `rust-version` (MSRV 1.79);
-* [x] `version = "0.1.4"`;
-* [x] upgraded **criterion** from 0.5 => 0.8;
-* [x] ~~narrow `exclude` (drop `.github`)~~ β **won't fix** (keep `exclude = ["target", ".github"]` since 0.1.3);
+* [x] ~~~`default-features = false` on optional and dev dependencies~~~ - β
;
+* [x] ~~~`rust-version` (MSRV 1.79)~~~ - β
;
+* [x] ~~~`version = "0.1.4"`~~~ - β
;
+* [x] ~~~upgraded **criterion** from 0.5 => 0.8~~~ - β
;
+* [x] ~~~narrow `exclude` (drop `.github`)~~~ β **won't fix** (keep
+ `exclude = ["target", ".github"]` since 0.1.3) - β
;
### CI
-* [x] `.github/workflows/ci.yml` (test, clippy, examples, doc, fmt, checkers);
-* [x] Clippy, DOC_76, and test-naming fixes;
-* [x] MSRV job on 1.79 (`cargo check --lib --locked`);
-* [x] `check_test_names.py` allows `__CONSTRUCT__` padding around construct names;
+* [x] ~~~`.github/workflows/ci.yml` (test, clippy, examples, doc, fmt, checkers)~~~ - β
;
+* [x] ~~~Clippy, DOC_76, and test-naming fixes~~~ - β
;
+* [x] ~~~MSRV job on 1.79 (`cargo check --lib --locked`)~~~ - β
;
+* [x] ~~~`check_test_names.py` allows `__CONSTRUCT__` padding around construct names~~~ - β
;
### Documentation
-* [x] back-filled **CHANGES.md** (0.1.0β0.1.3) and **0.1.4** entry;
-* [x] crate-level `//!` documentation;
-* [x] **EXAMPLES.md** and per-example `.md` files;
-* [x] **NEWS.md** release table (through 0.1.4);
-* [x] **README.md** badges, dependency links, related projects, `null-feature`;
-* [x] shortened `description` in **Cargo.toml**;
+* [x] ~~~back-filled **CHANGES.md** (0.1.0β0.1.3) and **0.1.4** entry~~~ - β
;
+* [x] ~~~crate-level `//!` documentation~~~ - β
;
+* [x] ~~~**EXAMPLES.md** and per-example `.md` files~~~ - β
;
+* [x] ~~~**NEWS.md** release table (through 0.1.4)~~~ - β
;
+* [x] ~~~**README.md** badges, dependency links, related projects, `null-feature`~~~ - β
;
+* [x] ~~~shortened `description` in **Cargo.toml**~~~ - β
;
### Formatting and quality tooling
-* [x] `.cargo/config.toml`;
-* [x] `.cursor/rules/rust-standards.mdc`;
-* [x] `.gitattributes`;
-* [x] `.gitignore` (`/scripts/__pycache__/`);
-* [x] `scripts/check_derives.py`, `check_doc_76.py`, `check_test_names.py`, `fmt`;
-* [x] renamed `.rustfmt.toml` => **rustfmt.toml**;
+* [x] ~~~`.cargo/config.toml`~~~ - β
;
+* [x] ~~~`.cursor/rules/rust-standards.mdc`~~~ - β
;
+* [x] ~~~`.gitattributes`~~~ - β
;
+* [x] ~~~`.gitignore` (`/scripts/__pycache__/`)~~~ - β
;
+* [x] ~~~`scripts/check_derives.py`, `check_doc_76.py`, `check_test_names.py`, `fmt`~~~ - β
;
+* [x] ~~~renamed `.rustfmt.toml` => **rustfmt.toml**~~~ - β
;
### Layout and structure
-* [x] ~~flatten examples to `examples/*.rs`~~ β **won't fix** (keep `examples/*/main.rs`);
-* [x] ~~move **character-play** from `[[example]]` to `[[bin]]`~~ β **won't fix** (since 0.1.3);
-* [x] ~~normalise bench filenames to snake_case~~ β **won't fix** (keep current names);
-
-### Release
-
-* [ ] merge `boilerplate` branch and tag **0.1.4** on GitHub;
-* [ ] publish **0.1.4** to crates.io;
+* [x] ~~~flatten examples to `examples/*.rs`~~~ β **won't fix** (keep `examples/*/main.rs`) - β
;
+* [x] ~~~move **character-play** from `[[example]]` to `[[bin]]`~~~ β **won't fix** (since 0.1.3) - β
;
+* [x] ~~~normalise bench filenames to snake_case~~~ β **won't fix** (keep current names) - β
;
## Source layout (deferred)
-Split `src/lib.rs` (~3,450 lines) into multiple source files. **Defer past 0.1.4**
+Split `src/lib.rs` (~3,450 lines) into multiple source files. **Deferred after
+0.1.4**
β behaviour-neutral refactor; ship as a dedicated follow-up change.
**API unit tests remain in `src/lib.rs`** (`#[cfg(test)] mod tests { ... }` and
diff --git a/examples/character-play.md b/examples/character-play.md
index 3266700..98ea81c 100644
--- a/examples/character-play.md
+++ b/examples/character-play.md
@@ -1,4 +1,4 @@
-# shwild.Rust Example - **character-play**
+# shwild.Rust - Example - **character-play**
## Summary
@@ -7,7 +7,9 @@ A scratch program that exercises **regex** crate matching behaviour for Unicode
## Source
-```Rust
+[Scratch source](../test/scratch/character-play/main.rs)
+
+```rust
// test/scratch/character-play/main.rs : Unicode matching experiments with **regex**
use regex::Regex;
@@ -85,7 +87,7 @@ fn main() {
```
-## Running and output
+## Execution
When executed, as in:
diff --git a/examples/list-matching-files-compiled.md b/examples/list-matching-files-compiled.md
index e426bff..c64f551 100644
--- a/examples/list-matching-files-compiled.md
+++ b/examples/list-matching-files-compiled.md
@@ -1,4 +1,4 @@
-# shwild.Rust Example - **list-matching-files-compiled**
+# shwild.Rust - Example - **list-matching-files-compiled**
## Summary
@@ -7,7 +7,9 @@ An example using **shwild.Rust**'s `CompiledMatcher` to list files in the curren
## Source
-```Rust
+[Source](./list-matching-files-compiled/main.rs)
+
+```rust
// examples/list-matching-files-compiled/main.rs : filter files using `CompiledMatcher`
use std::{
@@ -76,7 +78,7 @@ fn main() {
```
-## Running and output
+## Execution
When executed, as in:
diff --git a/examples/list-matching-files.md b/examples/list-matching-files.md
index 93a84ca..02e1ce3 100644
--- a/examples/list-matching-files.md
+++ b/examples/list-matching-files.md
@@ -1,4 +1,4 @@
-# shwild.Rust Example - **list-matching-files**
+# shwild.Rust - Example - **list-matching-files**
## Summary
@@ -7,72 +7,10 @@ An example using **shwild.Rust**'s `matches()` function to list files in the cur
## Source
-```Rust
-// examples/list-matching-files/main.rs : filter files by name using `matches()`
-
-use std::{
- env as std_env,
- fs as std_fs,
-};
-
-
-fn main() {
- let directory = ".";
-
- let patterns = {
- let r = std_env::args().skip(1).collect::>();
-
- if r.is_empty() {
- vec!["*".into()]
- } else {
- r
- }
- };
-
- println!("searching in '{directory}' with pattern(s) {patterns:?}");
-
- match std_fs::read_dir(directory) {
- Ok(entries) => {
- // for each file in the directory ...
- for entry in entries {
- match entry {
- Ok(entry) => {
- let path = entry.path();
- let path_s = format!("{}", path.display());
-
- // ... check against ...
- for pattern in &patterns {
- // ... each pattern ...
- match shwild::matches(pattern, &path_s, 0) {
- Ok(is_matched) => {
- if is_matched {
- // ... and print when it matches any one.
- println!("\t{path_s}");
-
- break;
- }
- },
- Err(e) => {
- eprintln!("failed to match against '{path_s}': {e}");
- },
- };
- }
- },
- Err(e) => {
- eprintln!("failed to read file in '{directory}': {e}");
- },
- };
- }
- },
- Err(e) => {
- eprintln!("failed to read files in '{directory}': {e}");
- },
- };
-}
-```
+[Source](./list-matching-files/main.rs)
-## Running and output
+## Execution
When executed, as in:
diff --git a/rust-toolchain.toml b/rust-toolchain.toml
new file mode 100644
index 0000000..c4af738
--- /dev/null
+++ b/rust-toolchain.toml
@@ -0,0 +1,11 @@
+# Pinned Rust toolchain for local formatting and CI.
+#
+# Keep the nightly channel explicit: rustfmt.toml uses unstable options.
+# Update this channel deliberately and keep CI in sync.
+
+[toolchain]
+channel = "nightly-2026-08-08"
+components = [
+ "clippy",
+ "rustfmt",
+]
diff --git a/scripts/fmt b/scripts/fmt
index 13731a1..2620b2d 100755
--- a/scripts/fmt
+++ b/scripts/fmt
@@ -1,12 +1,4 @@
#! /usr/bin/env bash
set -euo pipefail
-RUSTFMT="$(rustup which --toolchain nightly rustfmt 2>/dev/null || true)"
-if [[ -z "${RUSTFMT}" ]]; then
- echo "error: nightly rustfmt is required (see rustfmt.toml)" >&2
- echo " rustup component add rustfmt --toolchain nightly" >&2
- exit 1
-fi
-
-export RUSTFMT
exec cargo fmt -- --unstable-features "$@"