Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
58 changes: 47 additions & 11 deletions docs/execplans/public-api-surface.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This ExecPlan is a living document. The sections `Constraints`, `Tolerances`,
`Risks`, `Progress`, `Surprises & Discoveries`, `Decision Log`, and
`Outcomes & Retrospective` must be kept up to date as work proceeds.

Status: DRAFT
Status: COMPLETE

`PLANS.md` is not present in this repository at the time this plan was drafted.

Expand Down Expand Up @@ -57,17 +57,26 @@ optional convenience prelude. Test-only helpers become private to tests (or to
## Progress

- [x] (2026-02-18) Drafted ExecPlan for public API surface cleanup.
- [ ] Define target root API map and progressive discovery tiers.
- [ ] Refactor `src/lib.rs` exports to the target map.
- [ ] Tighten module visibility (`pub` to `pub(crate)` where applicable).
- [ ] Remove public test-only reachability from production builds.
- [ ] Update migration guide and user docs.
- [ ] Run full quality gates.
- [x] Define target root API map and progressive discovery tiers.
- [x] Refactor `src/lib.rs` exports to the target map.
- [x] Tighten module visibility (`pub` to `pub(crate)` where applicable).
- [x] Remove public test-only reachability from production builds.
- [x] Update migration guide and user docs.
- [x] Run full quality gates.

## Surprises & Discoveries

- Observation: None yet.
Evidence: Plan-only phase. Impact: None yet.
- Observation: Root re-export pruning required broad import rewrites across
tests, examples, and client test modules. Evidence:
`git diff --name-only | wc -l` returned `60` touched files. Impact: At the
tolerance boundary, and required iterative compile repair loops before moving
to full quality gates.

- Observation: `connection::test_support` was publicly reachable in non-test
builds via `cfg(not(loom))`. Evidence: `src/connection/mod.rs` exported
`pub mod test_support` with only `not(loom)` gating. Impact: Tightened to
`cfg(all(not(loom), any(test, feature = "test-support")))` to keep it out of
normal production builds.

## Decision Log

Expand All @@ -79,9 +88,36 @@ optional convenience prelude. Test-only helpers become private to tests (or to
Rationale: Module paths communicate conceptual ownership and reduce root
clutter. Date/Author: 2026-02-18 / Codex.

- Decision: Keep crate-root exports to canonical error/result aliases and move
high-frequency ergonomics to `wireframe::prelude`. Rationale: This preserves
a stable minimal root while still offering optional convenience imports.
Date/Author: 2026-02-19 / Codex.

- Decision: Preserve the `test-support` feature for integration-test workflows
while removing default-build exposure of connection test helpers. Rationale:
Internal and companion tests continue to work without exposing test-only
helpers in standard library builds. Date/Author: 2026-02-19 / Codex.

## Outcomes & Retrospective

Not started. Populate after implementation milestones complete.
Completed implementation outcomes:

- Root API was reduced to canonical error/result aliases with detailed APIs
now discovered through module namespaces.
- `wireframe::prelude` was introduced as an optional ergonomics layer for
common imports.
- `connection::test_support` is no longer reachable in normal builds.
- Migration and user documentation now describe module-based imports and
include explicit before/after mappings for removed root re-exports.

Validation outcomes:

- `make fmt` passed.
- `make check-fmt` passed.
- `make lint` passed.
- `make test` passed.
- `make markdownlint` passed.
- `make nixie` passed.

## Context and orientation

Expand Down Expand Up @@ -185,7 +221,7 @@ No new external dependencies are planned.

Expected interface shape:

- `wireframe::` root exposes only canonical high-level entry points.
- `wireframe::` root exposes only canonical error/result aliases.
- `wireframe::<module>::...` is the default path for specialized APIs.
- `wireframe::prelude::*` is optional convenience, not mandatory coupling.

Expand Down
29 changes: 21 additions & 8 deletions docs/users-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ with pluggable routing, middleware, and connection utilities.[^1] The guide
below walks through the components that exist today and explains how they work
together when assembling an application.

## API discovery and imports

Wireframe uses progressive discovery for public APIs:

- `wireframe::` root is intentionally small and stable, exposing canonical
`Result` and `WireframeError`.
- `wireframe::<module>::...` is the default path for specialized APIs.
- `wireframe::prelude::*` is an optional convenience import for common
workflows.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

## Quick start: building an application

A `WireframeApp` collects route handlers and middleware. Each handler is stored
Expand Down Expand Up @@ -290,7 +300,10 @@ The `WireframeProtocol` trait includes an `on_eof` hook for handling EOF
conditions during frame decoding:

```rust,ignore
use wireframe::{ConnectionContext, EofError, WireframeProtocol};
use wireframe::{
codec::EofError,
hooks::{ConnectionContext, WireframeProtocol},
};

impl WireframeProtocol for MyProtocol {
type Frame = Vec<u8>;
Expand Down Expand Up @@ -1081,7 +1094,7 @@ system, enabling consistent instrumentation across both client and server.[^48]
use std::{net::SocketAddr, sync::Arc};
use std::sync::atomic::{AtomicUsize, Ordering};

use wireframe::WireframeClient;
use wireframe::client::WireframeClient;

struct SessionState {
request_count: AtomicUsize,
Expand Down Expand Up @@ -1144,10 +1157,10 @@ use std::net::SocketAddr;

use wireframe::{
app::{Envelope, Packet},
WireframeClient,
client::{ClientError, WireframeClient},
};

# async fn example() -> Result<(), wireframe::ClientError> {
# async fn example() -> Result<(), ClientError> {
let addr: SocketAddr = "127.0.0.1:7878".parse().expect("valid socket address");
let mut client = WireframeClient::builder()
.connect(addr)
Expand All @@ -1171,8 +1184,8 @@ separately:

```rust,no_run
use wireframe::app::{Envelope, Packet};
# use wireframe::WireframeClient;
# async fn example(client: &mut WireframeClient) -> Result<(), wireframe::ClientError> {
# use wireframe::client::{ClientError, WireframeClient};
# async fn example(client: &mut WireframeClient) -> Result<(), ClientError> {

// Auto-generate correlation ID when sending.
let envelope = Envelope::new(1, None, b"payload".to_vec());
Expand Down Expand Up @@ -1429,7 +1442,7 @@ also prevents other client I/O until the stream is dropped or drained.
```rust,no_run
use futures::StreamExt;
use std::net::SocketAddr;
use wireframe::{ClientError, WireframeClient, app::Envelope};
use wireframe::{app::Envelope, client::{ClientError, WireframeClient}};

# #[tokio::main]
# async fn main() -> Result<(), ClientError> {
Expand Down Expand Up @@ -1459,7 +1472,7 @@ consumed:
```rust,no_run
use futures::StreamExt;
use std::net::SocketAddr;
use wireframe::{ClientError, WireframeClient, app::Envelope};
use wireframe::{app::Envelope, client::{ClientError, WireframeClient}};

# #[tokio::main]
# async fn main() -> Result<(), ClientError> {
Expand Down
97 changes: 97 additions & 0 deletions docs/v0-1-0-to-v0-2-0-migration-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,100 @@ use wireframe::response::WireframeError as StreamError;
use wireframe::WireframeError;
use wireframe::Result;
```

## Public API surface reorganization

The crate root is now intentionally minimal. Detailed APIs moved to their
owning modules, and root re-exports were removed.

- Root now exposes canonical `wireframe::Result<T>` and
`wireframe::WireframeError`.
- Use `wireframe::<module>::...` for specialized APIs.
- `wireframe::prelude::*` is available as an optional convenience import for
high-frequency types.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

### Removed root re-exports and their new module paths

- `wireframe::AppDataStore` ->
`wireframe::app_data_store::AppDataStore`
- `wireframe::BincodeSerializer`, `wireframe::Serializer` ->
`wireframe::serializer::{BincodeSerializer, Serializer}`
- `wireframe::ConnectionActor` ->
`wireframe::connection::ConnectionActor`
- `wireframe::CorrelatableFrame` ->
`wireframe::correlation::CorrelatableFrame`
- `wireframe::ConnectionContext`, `wireframe::ProtocolHooks`,
`wireframe::WireframeProtocol` ->
`wireframe::hooks::{ConnectionContext, ProtocolHooks, WireframeProtocol}`
- `wireframe::ClientCodecConfig`, `wireframe::ClientError`,
`wireframe::ClientProtocolError` -> `wireframe::client::{...}`
- `wireframe::ClientWireframeError`, `wireframe::SocketOptions`,
`wireframe::WireframeClient` -> `wireframe::client::{...}`
- `wireframe::CodecError`, `wireframe::CodecErrorContext`,
`wireframe::DefaultRecoveryPolicy`, `wireframe::EofError` ->
`wireframe::codec::{...}`
- `wireframe::FrameCodec`, `wireframe::FramingError`,
`wireframe::LengthDelimitedFrameCodec`, `wireframe::MAX_FRAME_LENGTH` ->
`wireframe::codec::{...}`
- `wireframe::MIN_FRAME_LENGTH`, `wireframe::ProtocolError`,
`wireframe::RecoveryConfig`, `wireframe::RecoveryPolicy`,
`wireframe::RecoveryPolicyHook` -> `wireframe::codec::{...}`
- `wireframe::DefaultFragmentAdapter`, `wireframe::FRAGMENT_MAGIC`,
`wireframe::FragmentAdapter`, `wireframe::FragmentAdapterError` ->
`wireframe::fragment::{...}`
- `wireframe::FragmentBatch`, `wireframe::FragmentError`,
`wireframe::FragmentFrame`, `wireframe::FragmentHeader`,
`wireframe::FragmentIndex`, `wireframe::FragmentSeries` ->
`wireframe::fragment::{...}`
- `wireframe::FragmentStatus`, `wireframe::FragmentationConfig`,
`wireframe::FragmentationError`, `wireframe::Fragmenter`,
`wireframe::MessageId`, `wireframe::ReassembledMessage` ->
`wireframe::fragment::{...}`
- `wireframe::Reassembler`, `wireframe::ReassemblyError`,
`wireframe::decode_fragment_payload`, `wireframe::encode_fragment_payload`,
`wireframe::fragment_overhead` -> `wireframe::fragment::{...}`
- `wireframe::AssembledMessage`, `wireframe::ContinuationFrameHeader`,
`wireframe::FirstFrameHeader`, `wireframe::FirstFrameInput` ->
`wireframe::message_assembler::{...}`
- `wireframe::FirstFrameInputError`, `wireframe::FrameHeader`,
`wireframe::FrameSequence`, `wireframe::MessageAssembler`,
`wireframe::MessageAssemblyError` -> `wireframe::message_assembler::{...}`
- `wireframe::MessageAssemblyState`, `wireframe::MessageKey`,
`wireframe::MessageSeries`, `wireframe::MessageSeriesError`,
`wireframe::MessageSeriesStatus`, `wireframe::ParsedFrameHeader` ->
`wireframe::message_assembler::{...}`
- `wireframe::CODEC_ERRORS`, `wireframe::CONNECTIONS_ACTIVE`,
`wireframe::Direction`, `wireframe::ERRORS_TOTAL`,
`wireframe::FRAMES_PROCESSED` -> `wireframe::metrics::{...}`
- `wireframe::DEFAULT_BODY_CHANNEL_CAPACITY`,
`wireframe::RequestBodyReader`, `wireframe::RequestBodyStream`,
`wireframe::RequestParts`, `wireframe::body_channel` ->
`wireframe::request::{...}`
- `wireframe::FrameStream`, `wireframe::Response` ->
`wireframe::response::{FrameStream, Response}`
- `wireframe::ConnectionId`, `wireframe::SessionRegistry` ->
`wireframe::session::{ConnectionId, SessionRegistry}`

Example migration:

```rust
// Before
use wireframe::{Response, Serializer, WireframeClient};

// After
use wireframe::{
client::WireframeClient,
response::Response,
serializer::Serializer,
};
```

## Test support visibility changes

Connection actor test helpers are no longer reachable in normal builds.

- `wireframe::connection::test_support` now requires
`cfg(any(test, feature = "test-support"))` in addition to `cfg(not(loom))`.
- Production consumers should not depend on `connection::test_support`.
- Internal and integration test suites can continue to use the `test-support`
feature where required.
Comment thread
leynos marked this conversation as resolved.
5 changes: 4 additions & 1 deletion docs/wireframe-client-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ A `WireframeClient::builder()` method configures the client:
```rust
use std::net::SocketAddr;

use wireframe::{BincodeSerializer, WireframeClient};
use wireframe::{
client::WireframeClient,
serializer::BincodeSerializer,
};

let addr: SocketAddr = "127.0.0.1:7878".parse()?;
let client = WireframeClient::builder()
Expand Down
2 changes: 1 addition & 1 deletion examples/hotline_codec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
use std::io;

use wireframe::{
BincodeSerializer,
app::{Envelope, WireframeApp},
codec::examples::HotlineFrameCodec,
serializer::BincodeSerializer,
};

fn main() -> io::Result<()> {
Expand Down
2 changes: 1 addition & 1 deletion examples/multi_packet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use std::time::Duration;
use futures::TryStreamExt;
use tokio::time::sleep;
use tracing::info;
use wireframe::{Response, WireframeError};
use wireframe::{WireframeError, response::Response};

const TRANSCRIPT: &[&str] = &[
"Client: HELLO",
Expand Down
2 changes: 1 addition & 1 deletion examples/mysql_codec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
use std::io;

use wireframe::{
BincodeSerializer,
app::{Envelope, WireframeApp},
codec::examples::MysqlFrameCodec,
serializer::BincodeSerializer,
};

fn main() -> io::Result<()> {
Expand Down
2 changes: 1 addition & 1 deletion examples/resp_codec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ mod resp_codec_impl;

pub use resp_codec_impl::{codec::RespFrameCodec, frame::RespFrame};
use wireframe::{
BincodeSerializer,
app::{Envelope, WireframeApp},
serializer::BincodeSerializer,
};

fn main() -> io::Result<()> {
Expand Down
3 changes: 2 additions & 1 deletion src/app/envelope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ pub trait Packet: CorrelatableFrame + Message + Send + Sync + 'static {
/// The default implementation returns `false`. Protocol implementations
/// should override this to detect the protocol-specific terminator format
/// emitted by the server's
/// [`stream_end_frame`](crate::WireframeProtocol::stream_end_frame) hook.
/// [`stream_end_frame`](crate::hooks::WireframeProtocol::stream_end_frame)
/// hook.
///
/// # Examples
///
Expand Down
2 changes: 1 addition & 1 deletion src/client/builder/connect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ where
/// ```no_run
/// use std::net::SocketAddr;
///
/// use wireframe::{ClientError, WireframeClient};
/// use wireframe::client::{ClientError, WireframeClient};
///
/// # #[tokio::main]
/// # async fn main() -> Result<(), ClientError> {
Expand Down
2 changes: 1 addition & 1 deletion src/client/builder/serializer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ where
/// # Examples
///
/// ```
/// use wireframe::{BincodeSerializer, client::WireframeClientBuilder};
/// use wireframe::{client::WireframeClientBuilder, serializer::BincodeSerializer};
///
/// let builder = WireframeClientBuilder::new().serializer(BincodeSerializer);
/// let _ = builder;
Expand Down
5 changes: 3 additions & 2 deletions src/client/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub enum ClientProtocolError {
/// [`ClientProtocolError`].
pub type ClientWireframeError = WireframeError<ClientProtocolError>;

/// Errors emitted by [`crate::WireframeClient`].
/// Errors emitted by [`crate::client::WireframeClient`].
#[derive(Debug, thiserror::Error)]
pub enum ClientError {
/// Request/response transport or protocol failure.
Expand All @@ -45,7 +45,8 @@ pub enum ClientError {
PreambleTimeout,
/// Response correlation ID does not match the request.
///
/// This error is returned by [`crate::WireframeClient::call_correlated`]
/// This error is returned by
/// [`crate::client::WireframeClient::call_correlated`]
/// when the response envelope's correlation ID differs from the request's.
#[error("correlation ID mismatch: expected {expected:?}, received {received:?}")]
CorrelationMismatch {
Expand Down
Loading
Loading