Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
4bea9d8
feat(router): Shut down gracefully on interrupt under Tokio
Fuwn Jun 13, 2026
44287cb
ci: Run fmt, clippy, and tests on both runtimes via just
Fuwn Jun 13, 2026
c39fe84
test(router): Extract and cover status-line formatting
Fuwn Jun 14, 2026
4f98615
refactor(router): Split unit tests into src/router/tests.rs
Fuwn Jun 14, 2026
95c069f
style: Keep consecutive imports in one group
Fuwn Sep 11, 2026
94814d5
test: Add compatibility coverage and move tests out of src
Fuwn Sep 11, 2026
e7910fc
refactor(rossweisse): Simplify router field generation and parsing
Fuwn Sep 11, 2026
dcb569c
refactor(router): Remove temporary footer and lookup allocations
Fuwn Sep 11, 2026
d52a17f
perf(response): Assemble responses in a single buffer
Fuwn Sep 11, 2026
2783208
refactor(examples): Simplify state and response handling
Fuwn Sep 11, 2026
b9f1e06
ci: Check formatting and test the supported feature combinations
Fuwn Sep 11, 2026
34cad5d
chore: Remove inactive formatter options and macro comments
Fuwn Sep 11, 2026
7fa2dad
style(router): Apply statement grouping consistently
Fuwn Sep 11, 2026
ea23c64
docs: Correct API guidance and explain examples and tests
Fuwn Sep 11, 2026
a016ffb
fix(router): Decode complete requests and terminate error responses
Fuwn Sep 11, 2026
0f82c42
refactor(router)!: Make async module attachment awaitable
Fuwn Sep 11, 2026
a191137
fix(router)!: Preserve parameters in case-insensitive routing
Fuwn Sep 11, 2026
9ce6984
fix(router): Retain registered partials when starting a server
Fuwn Sep 12, 2026
f4999f9
fix(router)!: Lock modules individually during request hooks
Fuwn Sep 12, 2026
ee8ee80
feat(router)!: Make concurrent module scheduling opt-in
Fuwn Sep 12, 2026
1dcddc1
fix(rossweisse): Preserve declared router visibility
Fuwn Sep 12, 2026
cff2183
fix(rossweisse): Report macro errors at the offending syntax
Fuwn Sep 12, 2026
79ae7cc
fix(rossweisse)!: Preserve index route method names
Fuwn Sep 12, 2026
60ccfe6
fix(macros): Repair context binding and MIME inference
Fuwn Sep 12, 2026
0def381
fix(rossweisse)!: Preserve router declarations and generics
Fuwn Sep 12, 2026
25e8c25
fix(response)!: Normalise text line endings and terminate footers
Fuwn Sep 12, 2026
b344bcf
fix(router)!: Load complete certificate chains
Fuwn Sep 12, 2026
b87a0b7
fix(logging): Preserve an existing application logger
Fuwn Sep 12, 2026
3193d42
fix(protocol)!: Validate Gemini requests and response headers
Fuwn Sep 12, 2026
5150a45
feat(router)!: Add connection limits and bounded shutdown
Fuwn Sep 12, 2026
08cb09c
refactor(response)!: Store one payload behind explicit accessors
Fuwn Sep 12, 2026
b93d9ed
refactor(module)!: Share request hooks without per-module locks
Fuwn Sep 12, 2026
4fdd365
refactor(logging)!: Let applications initialise their logger
Fuwn Sep 12, 2026
bae8ad8
feat(router): Separate bound servers from router configuration
Fuwn Sep 12, 2026
19f5f61
fix(just): Remove the obsolete logger feature flag
Fuwn Sep 12, 2026
aa9ae4d
fix(just): Put the nightly toolchain on the task PATH
Fuwn Sep 12, 2026
fc77340
refactor(response)!: Replace public response macros with closures
Fuwn Sep 12, 2026
415e149
chore(release): Prepare Windmark 0.8.0 and Rossweisse 0.0.4
Fuwn Sep 12, 2026
c4aef2c
fix(ci): Select the pinned nightly through the toolchain input
Fuwn Sep 12, 2026
bce4467
test(router): Allow scheduling headroom in deadline checks
Fuwn Sep 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 30 additions & 26 deletions .github/workflows/check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,44 @@ name: Check ✅
on:
workflow_dispatch:
push:
paths:
- "**"
pull_request:
paths:
- "**"

env:
CARGO_TERM_COLOR: always

jobs:
check:
format:
runs-on: ubuntu-latest
steps:
- name: Checkout 🛒
uses: actions/checkout@v3

- name: Toolchain 🧰
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
components: rustfmt, clippy
override: true

- name: Check ✅
uses: actions-rs/cargo@v1
continue-on-error: false
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
with:
command: check
args: --verbose
toolchain: nightly-2026-05-25
components: rustfmt
- name: Check Formatting
run: |
cargo +nightly-2026-05-25 fmt --all -- --check
cargo +nightly-2026-05-25 fmt --manifest-path tests/consumer/Cargo.toml -- --check

- name: Test 🧪
uses: actions-rs/cargo@v1
continue-on-error: false
check:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- name: Default
flags: --features windmark/tokio
- name: Tokio
flags: --no-default-features --features windmark/tokio,windmark/auto-deduce-mime
- name: Async-Std
flags: --no-default-features --features windmark/async-std,windmark/auto-deduce-mime
name: Check ${{ matrix.name }}
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@1.95.0
with:
command: test
args: --verbose --features auto-deduce-mime,response-macros
components: clippy
- name: Test the Workspace and Consumers
run: cargo +1.95.0 test --workspace ${{ matrix.flags }}
- name: Lint All Targets
run: cargo +1.95.0 clippy --workspace --all-targets ${{ matrix.flags }} -- -D warnings
76 changes: 23 additions & 53 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
members = ["rossweisse"]

[package]
autotests = false
name = "windmark"
version = "0.7.0"
version = "0.8.0"
authors = ["Fuwn <contact@fuwn.me>"]
edition = "2021"
description = "An elegant and highly performant async Gemini server framework"
Expand All @@ -19,9 +20,7 @@ categories = ["web-programming"]

[features]
default = ["tokio"]
logger = ["pretty_env_logger"]
auto-deduce-mime = ["tree_magic_mini"]
response-macros = []
tokio = ["dep:tokio", "tokio-openssl"]
async-std = ["dep:async-std", "async-std-openssl"]

Expand All @@ -38,79 +37,50 @@ tokio = { version = "1.52.3", default-features = false, features = [
"net",
"io-util",
"macros",
"signal",
"time",
], optional = true }
async-trait = "0.1.68"
futures-util = "0.3.31"
async-std = { version = "1.12.0", features = ["attributes"], optional = true }

# Logging
pretty_env_logger = { version = "0.5.0", optional = true }
log = "0.4.30"

# URL
url = "2.2.2"
matchit = "0.7.3"
mime = "0.3.17"
language-tags = "0.3.2"

tree_magic_mini = { version = "3.2.2", optional = true } # MIME

paste = "1.0.12" # Token Pasting

[dev-dependencies]
rossweisse = { version = "0.0.3", path = "./rossweisse" }
pretty_env_logger = "0.5.0"
rossweisse = { version = "0.0.4", path = "./rossweisse" }

[package.metadata.docs.rs]
# `tokio` and `async-std` are mutually exclusive, so document the default
# (`tokio`) runtime alongside the optional feature surface.
all-features = false
features = ["response-macros", "logger", "auto-deduce-mime"]

[[example]]
name = "async"
required-features = ["response-macros"]

[[example]]
name = "async_stateful_module"
required-features = ["response-macros"]

[[example]]
name = "binary"
required-features = ["response-macros"]

[[example]]
name = "callbacks"
required-features = ["response-macros"]

[[example]]
name = "certificate"
required-features = ["response-macros"]

[[example]]
name = "default_logger"
required-features = ["logger", "response-macros"]

[[example]]
name = "fix_path"
required-features = ["response-macros"]
features = ["auto-deduce-mime"]

[[example]]
name = "parameters"
required-features = ["response-macros"]
name = "simple_async_std"
required-features = ["async-std"]

[[example]]
name = "partial"
required-features = ["response-macros"]
# Private unit-test modules also live in tests, so integration targets are explicit.
[[test]]
name = "binary_response"

[[example]]
name = "query"
required-features = ["response-macros"]
[[test]]
name = "consumer"

[[example]]
name = "responses"
required-features = ["response-macros"]
[[test]]
name = "router_runtime"

[[example]]
name = "stateful_module"
required-features = ["response-macros"]
[[test]]
name = "module"

[[example]]
name = "simple_async_std"
required-features = ["async-std"]
[[test]]
name = "response"
26 changes: 10 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,15 @@ Now supporting both [Tokio](https://tokio.rs/) and [`async-std`](https://async.r
## Usage

> [!NOTE]
> A macro-based "`struct`-router" is in active development as a simplified
> alternative to the standard server creation approach. Check out
> [Rossweisse](./rossweisse/) for more information!
> Rossweisse lets you define a Windmark router using a struct and route
> attributes. See the [Rossweisse guide](./rossweisse/) for an example.

### Features

| Feature | Description |
| ------------------ | ------------------------------------------------------------------------------------------------------- |
| `default` | Base Windmark framework using [Tokio](https://tokio.rs/) |
| `logger` | Enables the default [`pretty_env_logger`](https://github.com/seanmonstar/pretty-env-logger) integration |
| `auto-deduce-mime` | Exposes `Response`s and macros that automatically fill MIMEs for non-Gemini responses |
| `response-macros` | Simple macros for all `Response`s |
| `auto-deduce-mime` | Enables `Response::binary_success_auto` to infer the MIME type of binary content |
| `tokio` | Marks [Tokio](https://tokio.rs/) as the asynchronous runtime |
| `async-std` | Marks [`async-std`](https://async.rs/) as the asynchronous runtime |

Expand All @@ -33,18 +30,12 @@ Now supporting both [Tokio](https://tokio.rs/) and [`async-std`](https://async.r
# Cargo.toml

[dependencies]
windmark = "0.7.0"
windmark = "0.8.0"
tokio = { version = "1.26.0", features = ["full"] }

# If you would like to use the built-in logger (recommended)
# windmark = { version = "0.7.0", features = ["logger"] }

# If you would like to use the built-in MIME deduction when `Success`-ing a file
# (recommended)
# windmark = { version = "0.7.0", features = ["auto-deduce-mime"] }

# If you would like to use macro-based responses (as seen below)
# windmark = { version = "0.7.0", features = ["response-macros"] }
# windmark = { version = "0.8.0", features = ["auto-deduce-mime"] }
```

### Implementing a Windmark Server
Expand Down Expand Up @@ -73,14 +64,15 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
```rust
// src/main.rs

use rossweisse::route;
use windmark::response::Response;

#[rossweisse::router]
struct Router;

#[rossweisse::router]
impl Router {
#[rossweisse::route(index)]
#[route(index)]
pub fn index(
_context: windmark::context::RouteContext,
) -> Response {
Expand All @@ -97,7 +89,9 @@ Examples can be found within the
[`examples/`](https://github.com/gemrest/windmark/tree/main/examples) directory
along with a rundown of each of their purposes and useful facts.

Run an example by cloning this repository and running `cargo run --example example_name`.
Each entry in [the examples guide](./examples/README.md) lists the required
features. Generate local credentials with `just gen-key`, then use the listed
command or `just example example_name` (optionally followed by `async-std`).

## Modules

Expand Down
40 changes: 21 additions & 19 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## [Async Stateful Module](./async_stateful_module.rs)

`cargo run --example async_stateful_module --features response-macros`
`cargo run --example async_stateful_module`

Demonstrates use of the `AsyncModule` trait by implementing the module
`Clicker` which tracks the global number of visits to the capsule.
Expand All @@ -12,37 +12,36 @@ tracked for clicks.

## [Async](./async.rs)

`cargo run --example async --features response-macros`
`cargo run --example async`

Demonstrates use of async routes through an async response macro and
implementing a click tracker using a shared variable through an thread-safe,
async mutex.
This example shows how to define async route handlers and implement a click
tracker using a shared variable protected by an async mutex.

## [Binary](./binary.rs)

`cargo run --example binary --features response-macros`
`cargo run --example binary`

Demonstrates the binary response functionality by using both manual
and automatic mime resolution (`--features auto-deduce-mime`).

## [Callbacks](./callbacks.rs)

`cargo run --example callbacks --features response-macros`
`cargo run --example callbacks`

Demonstrates use of the pre and post-route callback handlers.
This example shows how to register pre-route and post-route callbacks.

## [Certificate](./certificate.rs)

`cargo run --example certificate --features response-macros`
`cargo run --example certificate`

Demonstrate the various certificate related responses as well as
reading the client certificate to give conditional access.

## [Default Logger](./default_logger.rs)

`cargo run --example default_logger --features logger,response-macros`
`cargo run --example default_logger`

A simple example showing the use of the default default logger implementation.
A simple example showing how an application initialises a logger for Windmark.

## [Empty](./empty.rs)

Expand All @@ -54,11 +53,13 @@ An empty example which starts up a server but has no mounted routes.

`cargo run --example error_handler`

Creates an intentional error within a route, invoking the error handler.
This example shows how to handle requests that do not match a route. It also
includes an `/error` route that deliberately panics; this panic does not invoke
the error handler.

## [Fix Path](./fix_path.rs)

`cargo run --example fix_path --features response-macros`
`cargo run --example fix_path`

A simple example which demonstrates use of the path fixer that attempts to resolve the closest match of a route when an invalid route is visited.

Expand All @@ -79,27 +80,27 @@ Demonstrate how to modify the MIME of a response before use.

## [Parameters](./parameters.rs)

`cargo run --example parameters --features response-macros`
`cargo run --example parameters`

Demonstrate the use of route parameters (not URL queries).

## [Partial](./partial.rs)

`cargo run --example partial --features response-macros`
`cargo run --example partial`

Demonstrates use of appending headers and footers to routes, globally.

If you would like to conditionally append headers and footers based on route, please look into using a templating framework.

## [Query](./query.rs)

`cargo run --example query --features response-macros`
`cargo run --example query`

Demonstrates the inspection of URL queries parameters.

## [Responses](./responses.rs)

`cargo run --example responses --features response-macros`
`cargo run --example responses`

Demonstrates the use of a wide variety of responses, additionally exposing the flexibility of response bodies types.

Expand All @@ -109,7 +110,8 @@ Demonstrates the use of a wide variety of responses, additionally exposing the f

Demonstrates how to explicitly specify Windmark to use the [`async-std`](https://github.com/async-rs/async-std) runtime.

If the `async-std` feature is NOT enabled, Windmark will default to using Tokio as the async runtime.
Windmark uses Tokio when its default features are enabled. If you disable
default features, you must explicitly enable either `tokio` or `async-std`.

## [Simple Tokio](./simple_tokio.rs)

Expand All @@ -119,7 +121,7 @@ Demonstrates how to explicitly specify Windmark to use the [Tokio](https://githu

## [Stateful Module](./stateful_module.rs)

`cargo run --example stateful_module --features response-macros`
`cargo run --example stateful_module`

Demonstrates use of `Module`s by implementing a click tracker

Expand Down
Loading
Loading