Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
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
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
- **Clarity over cleverness.** Be concise, but favour explicit over terse or
obscure idioms. Prefer code that's easy to follow.
- **Use functions and composition.** Avoid repetition by extracting reusable
logic. Prefer generators or comprehensions, and declarative code to imperative
repetition when readable.
logic. Prefer generators or comprehensions, and declarative code, over
imperative repetition when readable.
- **Small, meaningful functions.** Functions must be small, clear in purpose,
single responsibility, and obey command/query segregation.
- **Clear commit messages.** Commit messages should be descriptive, explaining
Expand Down
33 changes: 18 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Wireframe

**Wireframe** is an experimental Rust library that simplifies building servers
and clients for custom binary protocols. The design borrows heavily from
[Actix Web](https://actix.rs/) to provide a familiar, declarative API for
routing, extractors, and middleware.
and clients for custom binary protocols. The design borrows heavily from [Actix
Web](https://actix.rs/) to provide a familiar, declarative API for routing,
extractors, and middleware.

## Motivation

Expand Down Expand Up @@ -83,7 +83,8 @@ WireframeServer::new(|| {
```

This example showcases how derive macros and the framing abstraction simplify a
binary protocol server【F:docs/rust-binary-router-library-design.md†L1126-L1156】.
binary protocol
server【F:docs/rust-binary-router-library-design.md†L1126-L1156】.
Comment on lines +86 to +87
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Merge the split noun phrase

The hard line-break between “binary protocol” and “server” interrupts the noun phrase.
Re-flow the two words onto the same line; the width will still be well under 80 columns.

🤖 Prompt for AI Agents
In README.md around lines 86 to 87, the phrase "binary protocol" and "server"
are split by a hard line-break, breaking the noun phrase. Remove the line break
between these two words so that "binary protocol server" appears on the same
line, ensuring the line length remains under 80 columns.


## Custom Envelopes

Expand Down Expand Up @@ -187,10 +188,10 @@ let app = WireframeApp::new().with_protocol(MySqlProtocolImpl);

## Session Registry

The \[`SessionRegistry`\] stores weak references to \[`PushHandle`\]s for active
connections. Background tasks can look up a handle by \[`ConnectionId`\] to send
frames asynchronously without keeping the connection alive. Stale entries are
removed automatically when looked up and found to be dead. Call
The \[`SessionRegistry`\] stores weak references to \[`PushHandle`\]s for
active connections. Background tasks can look up a handle by \[`ConnectionId`\]
to send frames asynchronously without keeping the connection alive. Stale
entries are removed automatically when looked up and found to be dead. Call
`active_handles()` to iterate over live sessions for broadcast or diagnostics.

```rust
Expand Down Expand Up @@ -219,8 +220,8 @@ access app state or expose peer information.

Custom extractors let you centralize parsing and validation logic that would
otherwise be duplicated across handlers. A session token parser, for example,
can verify the token before any route-specific code executes
[Design Guide: Data Extraction and Type Safety][data-extraction-guide].
can verify the token before any route-specific code executes [Design Guide:
Data Extraction and Type Safety][data-extraction-guide].

```rust
use wireframe::extractor::{ConnectionInfo, FromMessageRequest, MessageRequest, Payload};
Expand Down Expand Up @@ -309,11 +310,13 @@ production use.

Development priorities are tracked in [docs/roadmap.md](docs/roadmap.md). Key
tasks include building the Actix‑inspired API, implementing middleware and
extractor traits, and providing example applications【F:docs/roadmap.md†L1-L24】.
extractor traits, and providing example
applications【F:docs/roadmap.md†L1-L24】.

## License
## Licence

Wireframe is distributed under the terms of the ISC license. See
[LICENSE](LICENSE) for details.
Wireframe is distributed under the terms of the ISC licence. See
[LICENCE](LICENSE) for details.

[data-extraction-guide]: docs/rust-binary-router-library-design.md#53-data-extraction-and-type-safety
[data-extraction-guide]:
docs/rust-binary-router-library-design.md#53-data-extraction-and-type-safety
62 changes: 31 additions & 31 deletions docs/asynchronous-outbound-messaging-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ worker task—to push frames to a live connection at any time.

Earlier releases spawned a short-lived worker per request. This approach made
persistent state awkward and required extra synchronisation when multiple tasks
needed to write to the same socket. The new design promotes each connection to a
**stateful actor** that owns its context for the lifetime of the session. Actor
state keeps sequencing rules and push queues local to one task, drastically
simplifying concurrency while enabling unsolicited frames.
needed to write to the same socket. The new design promotes each connection to
a **stateful actor** that owns its context for the lifetime of the session.
Actor state keeps sequencing rules and push queues local to one task,
drastically simplifying concurrency while enabling unsolicited frames.

This feature is a cornerstone of the "Road to Wireframe 1.0" and is designed to
be synergistic with the planned streaming and fragmentation capabilities,
Expand All @@ -31,21 +31,21 @@ protocols.

Only the queue management utilities in `src/push.rs` exist at present. The
connection actor and its write loop are still to be implemented. The remaining
sections describe how to build that actor from first principles using the biased
`select!` loop presented in
[Section 3](#3-core-architecture-the-connection-actor).
sections describe how to build that actor from first principles using the
biased `select!` loop presented in [Section
3](#3-core-architecture-the-connection-actor).

## 2. Design Goals & Requirements

The implementation must satisfy the following core requirements:

| ID | Requirement |
| ID | Requirement |
| --- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| G1 | Any async task must be able to push frames to a live connection. |
| G2 | Ordering-safety: Pushed frames must interleave correctly with normal request/response traffic and respect any per-message sequencing rules. |
| G3 | Back-pressure: Writers must block (or fail fast) when the peer cannot drain the socket, preventing unbounded memory consumption. |
| G4 | Generic—independent of any particular protocol; usable by both servers and clients built on wireframe. |
| G5 | Preserve the simple “return a reply” path for code that does not need pushes, ensuring backward compatibility and low friction for existing users. |
| G1 | Any async task must be able to push frames to a live connection. |
| G2 | Ordering-safety: Pushed frames must interleave correctly with normal request/response traffic and respect any per-message sequencing rules. |
| G3 | Back-pressure: Writers must block (or fail fast) when the peer cannot drain the socket, preventing unbounded memory consumption. |
| G4 | Generic—independent of any particular protocol; usable by both servers and clients built on wireframe. |
| G5 | Preserve the simple “return a reply” path for code that does not need pushes, ensuring backward compatibility and low friction for existing users. |

## 3. Core Architecture: The Connection Actor

Expand Down Expand Up @@ -74,16 +74,16 @@ manage two distinct, bounded `tokio::mpsc` channels for pushed frames:
background messages like log forwarding or secondary status updates.

The bounded nature of these channels provides an inherent and robust
back-pressure mechanism. When a channel's buffer is full, any task attempting to
push a new message will be asynchronously suspended until space becomes
back-pressure mechanism. When a channel's buffer is full, any task attempting
to push a new message will be asynchronously suspended until space becomes
available.

### 3.2 The Prioritised Write Loop

The connection actor's write logic will be implemented within a `tokio::select!`
loop. Crucially, this loop will use the `biased` keyword to ensure a strict,
deterministic polling order. This prevents high-volume yet critical control
messages from being starved by large data streams.
The connection actor's write logic will be implemented within a
`tokio::select!` loop. Crucially, this loop will use the `biased` keyword to
ensure a strict, deterministic polling order. This prevents high-volume yet
critical control messages from being starved by large data streams.
Comment on lines 81 to +86
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Use oxford-ised “Prioritized”

Replace “Prioritised” with the oxford-ised spelling “Prioritized” to comply with the en-GB-oxendict rule.

-### 3.2 The Prioritised Write Loop
+### 3.2 The Prioritized Write Loop
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
### 3.2 The Prioritised Write Loop
The connection actor's write logic will be implemented within a `tokio::select!`
loop. Crucially, this loop will use the `biased` keyword to ensure a strict,
deterministic polling order. This prevents high-volume yet critical control
messages from being starved by large data streams.
The connection actor's write logic will be implemented within a
`tokio::select!` loop. Crucially, this loop will use the `biased` keyword to
ensure a strict, deterministic polling order. This prevents high-volume yet
critical control messages from being starved by large data streams.
### 3.2 The Prioritized Write Loop
The connection actor's write logic will be implemented within a
`tokio::select!` loop. Crucially, this loop will use the `biased` keyword to
ensure a strict, deterministic polling order. This prevents high-volume yet
critical control messages from being starved by large data streams.
🧰 Tools
🪛 LanguageTool

[style] ~81-~81: Would you like to use the Oxford spelling “Prioritized”? The spelling ‘Prioritised’ is also correct.
Context: ...l space becomes available. ### 3.2 The Prioritised Write Loop The connection actor's writ...

(OXFORD_SPELLING_Z_NOT_S)


[grammar] ~83-~83: An apostrophe ‘s’ denotes possession. Did you mean to use the plural form of the noun (no apostrophe)?
Context: ... Prioritised Write Loop The connection actor's write logic will be implemented within ...

(NOUN_APOSTROPHE_S_VERB)

🤖 Prompt for AI Agents
In docs/asynchronous-outbound-messaging-design.md around lines 81 to 86, replace
the heading "Prioritised Write Loop" with the oxford-ised spelling "Prioritized
Write Loop" to comply with the en-GB-oxendict style guide.


The polling order will be:

Expand Down Expand Up @@ -143,8 +143,8 @@ checks `low_priority_push_rx.try_recv()` and, if a frame is present, processes
it and resets the counter.

An optional time slice (for example 100 µs) can also be configured. When the
elapsed time spent handling high-priority frames exceeds this slice, and the low
queue is not empty, the actor yields to a low-priority frame. Application
elapsed time spent handling high-priority frames exceeds this slice, and the
low queue is not empty, the actor yields to a low-priority frame. Application
builders expose `with_fairness(FairnessConfig)` where `FairnessConfig` groups
the counter threshold and an optional `time_slice`. The counter defaults to 8
while `time_slice` is disabled. Setting the counter to zero disables the
Expand Down Expand Up @@ -273,8 +273,8 @@ struct ActorState {
}
```

`total_sources` is calculated when the actor starts. Whenever a receiver returns
`None`, it is set to `None` and `closed_sources` increments. When
`total_sources` is calculated when the actor starts. Whenever a receiver
returns `None`, it is set to `None` and `closed_sources` increments. When
`closed_sources == total_sources` the loop exits. This consolidation clarifies
progress through the actor lifecycle and reduces manual flag management.

Expand Down Expand Up @@ -587,8 +587,8 @@ clearly signalling to the producer task that the connection is gone.
### 5.2 Optional Dead Letter Queue (DLQ) for Critical Messages

For applications where dropping a message is unacceptable (e.g., critical
notifications, audit events), the framework will support an optional Dead Letter
Queue.
notifications, audit events), the framework will support an optional Dead
Letter Queue.

**Implementation:** The `WireframeApp` builder will provide a method,
`with_push_dlq(mpsc::Sender<F>)`, to configure a DLQ. If provided, any frame
Expand Down Expand Up @@ -700,10 +700,10 @@ features of the 1.0 release.
that urgent pushes can interrupt a long-running data stream.

- **Message Fragmentation:** Pushes occur at the *logical frame* level. The
`FragmentAdapter` will operate at a lower layer in the `FrameProcessor` stack,
transparently splitting any large pushed frames before they are written to the
socket. The `PushHandle` and the application code that uses it remain
completely unaware of fragmentation.
`FragmentAdapter` will operate at a lower layer in the `FrameProcessor`
stack, transparently splitting any large pushed frames before they are
written to the socket. The `PushHandle` and the application code that uses it
remain completely unaware of fragmentation.

## 7. Use Cases

Expand Down Expand Up @@ -757,8 +757,8 @@ sequenceDiagram
### 7.3 Broker-Side MQTT `PUBLISH`

An MQTT broker can deliver retained messages or fan-out new `PUBLISH` frames to
all subscribed clients via their `PushHandle`s. The `try_push` method allows the
broker to drop non-critical messages when a subscriber falls behind.
all subscribed clients via their `PushHandle`s. The `try_push` method allows
the broker to drop non-critical messages when a subscriber falls behind.

```mermaid
sequenceDiagram
Expand Down
6 changes: 3 additions & 3 deletions docs/behavioural-testing-in-rust-with-cucumber.md
Original file line number Diff line number Diff line change
Expand Up @@ -1101,7 +1101,6 @@ aligned with what is needed.
[^18]: *Quickstart* — Cucumber Rust Book, accessed on 14 July 2025,
<https://cucumber-rs.github.io/cucumber/current/quickstart.html>

[^19]: *A Beginner’s Guide to Cucumber in Rust* — Florian Reinhard, accessed
on 14 July 2025,
<https://www.florianreinhard.de/cucumber-in-rust-beginners-tutorial/>

Expand All @@ -1116,7 +1115,7 @@ aligned with what is needed.
Stack Overflow, accessed on 14 July 2025,
<https://stackoverflow.com/questions/30505639/how-to-do-error-handling-in-rust-and-what-are-the-common-pitfalls>

[^23]: Data tables - Cucumber Rust Book, accessed on 14 July 2025,
[^23]: Data tables - Cucumber Rust Book, accessed on 14 July 2025,
<https://cucumber-rs.github.io/cucumber/main/writing/data_tables.html>

[^25]: Best practices for scenario writing | CucumberStudio Documentation
Expand All @@ -1133,7 +1132,8 @@ aligned with what is needed.
<https://medium.com/@realtalkdev/common-challenges-in-cucumber-testing-and-how-to-overcome-them-dc95fffb43c8>

[^31]: Cucumber in cucumber - Rust - [Docs.rs](http://Docs.rs), accessed on
14 July 2025, <https://docs.rs/cucumber/latest/cucumber/struct.Cucumber.html>
14 July 2025,
<https://docs.rs/cucumber/latest/cucumber/struct.Cucumber.html>

[^32]: CLI (command-line interface) - Cucumber Rust Book, accessed on
14 July 2025, <https://cucumber-rs.github.io/cucumber/main/cli.html>
Expand Down
Loading