Skip to content

Refactor: modularize into submodules; rebase onto origin/main#448

Merged
leynos merged 10 commits intomainfrom
refactor-modularize-large-files-ixca6n
Feb 9, 2026
Merged

Refactor: modularize into submodules; rebase onto origin/main#448
leynos merged 10 commits intomainfrom
refactor-modularize-large-files-ixca6n

Conversation

@leynos
Copy link
Copy Markdown
Owner

@leynos leynos commented Feb 5, 2026

Summary

  • Rebased branch onto origin/main; conflicts resolved.
  • Refactor large monolithic files into modular submodules to improve readability and maintainability. This change also updates tests to align with the new modular structure, and refactors server backoff tests to use parameterised functions for broader coverage.

Note: Public API surface remains compatible; internal module paths changed to improve maintainability and testability.

Changes

  • Application builder refactor
    • Split src/app/builder.rs into modular submodules under src/app/builder/: core.rs, config.rs, lifecycle.rs, protocol.rs, routing.rs, state.rs, and codec.rs.
    • Added src/app/builder/mod.rs to re-export the WireframeApp type and present a cleaner module structure.
  • Frame handling modularization
    • Replaced large frame_handling.rs with a modular layout: src/app/frame_handling/mod.rs, core.rs, reassembly.rs, and response.rs.
    • Added tests for frame handling functionality in src/app/frame_handling/tests.rs.
  • Client builder restructuring
    • Reorganized the client builder into a modular structure under src/client/builder/ with submodules: core, codec, connect, lifecycle, preamble, serializer, etc., replacing the monolithic client/builder.rs.
    • Introduced a macro-based field_update helper to ease type-transitional builder methods.
  • Extractor modularization
    • Reworked extractors into a modular layout under src/extractor/: mod.rs plus connection_info.rs, error.rs, message.rs, request.rs, streaming.rs, shared_state.rs, trait_def.rs.
    • Replaced monolithic extractor modules with finer-grained components and public exports.
  • Codec recovery module refactor
    • Added a new modular recovery stack under src/codec/recovery/: mod.rs, config.rs, context.rs, hook.rs, policy.rs, and recovery-related re-exports.
  • Tests
    • Added comprehensive tests for frame handling in src/app/frame_handling/tests.rs.
    • Refactored server backoff tests to use parameterised functions (rstest) to cover multiple configurations.

Note: Public API surface remains compatible; internal module paths changed to improve maintainability and testability.

Why

  • Large, monolithic files are hard to navigate and maintain.
  • A modular structure simplifies future changes, testing, and feature growth.
  • Clear separation of concerns between app wiring, frame handling, extraction, and codec recovery improves developer experience.

How to test

  • Build and run tests: cargo test
  • Specifically:
    • Ensure WireframeApp construction and usage remain unchanged from a consumer perspective.
    • Run frame handling tests added under src/app/frame_handling/tests.rs.
    • Run the whole test suite to verify no regressions across modules.
    • Run server backoff tests to exercise the new parameterised scenarios.

Notes for reviewers

  • Focus on module boundaries and export surface; ensure public API usage remains idiomatic.
  • Review new modularizers for consistency with existing crate architecture (e.g., existing traits and types).
  • Validate that the new tests exercise the intended modular behavior without relying on previous monolithic structure.
  • Backoff tests are now parameterised; review coverage across different initial/max delay combinations and edge cases.

📎 Task: https://www.devboxer.com/task/56865d80-c8c0-4c00-9f2b-fa7dfeb7809d

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 5, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Summarise the refactor that splits several large modules into focused submodules, re-exports the original public API surfaces, and relocates tests into dedicated test files; introduces modularised WireframeApp and WireframeClient builders, reorganised frame-handling and codec recovery, and a new extractor subsystem. (≤50 words)

Changes

Cohort / File(s) Summary
App builder
src/app/builder/mod.rs, src/app/builder/core.rs, src/app/builder/codec.rs, src/app/builder/config.rs, src/app/builder/lifecycle.rs, src/app/builder/protocol.rs, src/app/builder/routing.rs, src/app/builder/state.rs, src/app/builder.rs, src/app/mod.rs
Replace monolithic builder.rs with builder/ submodules. Add generic WireframeApp core, serializer/codec switching, config (read_timeout, fragmentation, DLQ), lifecycle/protocol accessors, routing/middleware, app_data storage, and re-export; remove old file.
Frame handling
src/app/frame_handling/mod.rs, src/app/frame_handling/core.rs, src/app/frame_handling/reassembly.rs, src/app/frame_handling/response.rs, src/app/frame_handling/tests.rs, src/app/frame_handling.rs
Split former frame_handling.rs into core (DeserFailureTracker, ResponseContext), reassembly (reassemble_if_needed), response (forward_response, fragment/serialize/send helpers) and tests; add façade and re-exports; delete original.
Extractor framework
src/extractor/mod.rs, src/extractor/request.rs, src/extractor/connection_info.rs, src/extractor/error.rs, src/extractor/message.rs, src/extractor/shared_state.rs, src/extractor/streaming.rs, src/extractor/trait_def.rs, src/extractor.rs
Replace monolithic extractor.rs with extractor/ submodules. Introduce MessageRequest/Payload, ConnectionInfo, ExtractError, Message<T> extractor, SharedState<T>, StreamingBody, FromMessageRequest trait; add re-exports and remove original file.
Client builder
src/client/builder/mod.rs, src/client/builder/core.rs, src/client/builder/codec.rs, src/client/builder/connect.rs, src/client/builder/lifecycle.rs, src/client/builder/preamble.rs, src/client/builder/serializer.rs, src/client/builder.rs
Split client/builder.rs into submodules. Add WireframeClientBuilder core, codec configuration helpers, async connect, lifecycle hooks, preamble support, serializer swap, builder-field-update macro and re-export; delete original monolith.
Codec recovery
src/codec/recovery/mod.rs, src/codec/recovery/policy.rs, src/codec/recovery/config.rs, src/codec/recovery/context.rs, src/codec/recovery/hook.rs, src/codec/recovery/tests.rs, src/codec/recovery.rs
Decompose recovery.rs into recovery/ submodules: policy (RecoveryPolicy), config (RecoveryConfig), context (CodecErrorContext), hook (RecoveryPolicyHook + DefaultRecoveryPolicy), tests and re-exports; remove old file.
Server config tests
src/server/config/mod.rs, src/server/config/tests/mod.rs, src/server/config/tests/tests_backoff.rs, src/server/config/tests/tests_basic.rs, src/server/config/tests/tests_binding.rs, src/server/config/tests/tests_integration.rs, src/server/config/tests/tests_preamble.rs, src/server/config/tests.rs
Extract large server/config test file into tests/ submodules for backoff, basic, binding, integration and preamble tests; add scaffolding and move tests into separate files; delete previous monolithic test.
Misc small additions
src/app/builder_defaults.rs, src/server/mod.rs, src/server/config/mod.rs
Add DEFAULT_READ_TIMEOUT_MS constant and pub(crate) fn default_worker_count() helper; update server config to call helper.
Removed monoliths
src/app/builder.rs, src/app/frame_handling.rs, src/client/builder.rs, src/codec/recovery.rs, src/extractor.rs, src/server/config/tests.rs
Delete original large source files after extraction; functionality relocated into submodules and re-exports.

Sequence Diagram(s)

(Skipped — changes are primarily reorganisation/refactor rather than a single new multi-component control flow.)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

  • #432 — Touches the same TypeId→Any app-data storage and builder type-transition concerns; changes add app_data storage and type-transition helpers.
  • #428 — Implements file-splitting and modularisation objectives; this PR splits large files into submodules and re-exports APIs.

Possibly related PRs

  • #399 — Shares a strong code-level connection around fragmentation and frame-handling changes; overlapping logic likely relevant.
  • #413 — Strongly related: FrameCodec-driven redesign, WireframeApp/Client builder adjustments and frame handling refactor overlap with this PR.

Suggested reviewers

  • codescene-delta-analysis

Poem

✂️ Files split and tidy grown,
Modules find their proper home,
Builders, codecs, extractors sing,
Re-exports keep the old front door,
🎉 Tests relocated, clean and grown.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarises the main change: a refactor modularising large monolithic files into submodules with a rebase onto origin/main.
Description check ✅ Passed The description directly relates to the changeset, detailing the modularisation of builder, frame handling, client builder, extractor, and recovery modules with clear reasoning and testing instructions.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor-modularize-large-files-ixca6n

Comment @coderabbitai help to get the list of available commands and usage tips.

@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai Bot commented Feb 5, 2026

Reviewer's Guide

Refactors several monolithic modules (app builder, frame handling, extractor, client builder, codec recovery, and server config tests) into structured submodules, introduces new extractor and codec recovery primitives, updates frame response handling to be codec-aware, and rewrites server backoff and related server-config tests to be parameterised with broader coverage while preserving the public API surface.

Sequence diagram for codec-aware response forwarding

sequenceDiagram
    participant Conn as ConnectionTask
    participant FH as frame_handling
    participant HS as HandlerService
    participant Frag as FragmentationState
    participant Ser as Serializer
    participant Codec as FrameCodec
    participant FramedIO as Framed_ConnectionCodec

    Conn->>FH: forward_response(env, service, ctx)
    FH->>HS: call(ServiceRequest(payload, correlation_id))
    HS-->>FH: Response

    FH->>FH: PacketParts::new(env.id, resp.correlation_id, body)
    FH->>Frag: fragment(envelope)
    Frag-->>FH: Vec<Envelope> responses

    loop for each response
        FH->>Ser: serialize(response)
        Ser-->>FH: Vec<u8> bytes
        FH->>Codec: wrap_payload(Bytes::from(bytes))
        Codec-->>FH: Frame
        FH->>FramedIO: send(Frame)
        FramedIO-->>FH: Result
    end

    FH-->>Conn: io::Result<()>
Loading

Class diagram for modularised WireframeApp builder

classDiagram
    class WireframeApp~S,C,E,F~ {
        +HashMap~u32,Handler~E~~ handlers
        +OnceCell~Arc~HashMap~u32,HandlerService~E~~~~ routes
        +Vec~Middleware~E~~ middleware
        +S serializer
        +HashMap~TypeId,Arc~Any+Send+Sync~~ app_data
        +Option~Arc~ConnectionSetup~C~~~ on_connect
        +Option~Arc~ConnectionTeardown~C~~~ on_disconnect
        +Option~Arc~WireframeProtocol~Frame=F::Frame,ProtocolError=()~~ protocol
        +Option~mpsc::Sender~Vec~u8~~~~ push_dlq
        +F codec
        +u64 read_timeout_ms
        +Option~FragmentationConfig~ fragmentation
        +Option~Arc~MessageAssembler~~ message_assembler
        +new() Result~WireframeApp~S,C,E,F~~
        +with_serializer(serializer S) Result~WireframeApp~S,C,E,F~~
        +rebuild_with_params~S2,F2~(serializer S2, codec F2, protocol Option~Arc~WireframeProtocol~Frame=F2::Frame,ProtocolError=()~~, fragmentation Option~FragmentationConfig~, message_assembler Option~Arc~MessageAssembler~~) WireframeApp~S2,C,E,F2~
    }

    class Serializer {
        <<interface>>
    }

    class FrameCodec {
        <<interface>>
        +wrap_payload(payload Bytes) Frame
        +max_frame_length() usize
    }

    class WireframeProtocol {
        <<interface>>
        +Frame
        +ProtocolError
    }

    class MessageAssembler {
        <<interface>>
    }

    class Handler~E~ {
    }

    class HandlerService~E~ {
    }

    class Middleware~E~ {
        <<interface>>
    }

    class Envelope {
    }

    class Packet {
        <<interface>>
    }

    class ConnectionSetup~C~ {
        <<type alias or trait>>
    }

    class ConnectionTeardown~C~ {
        <<type alias or trait>>
    }

    class FragmentationConfig {
    }

    class BincodeSerializer {
    }

    class LengthDelimitedFrameCodec {
    }

    WireframeApp --> Serializer : uses S
    WireframeApp --> FrameCodec : uses F
    WireframeApp --> WireframeProtocol : optional protocol
    WireframeApp --> MessageAssembler : optional
    WireframeApp --> HandlerService : builds routes
    WireframeApp --> Handler : registers
    WireframeApp --> Middleware : pipeline
    WireframeApp --> ConnectionSetup : on_connect
    WireframeApp --> ConnectionTeardown : on_disconnect
    Envelope ..|> Packet
    BincodeSerializer ..|> Serializer
    LengthDelimitedFrameCodec ..|> FrameCodec
Loading

Class diagram for new extractor module structure

classDiagram
    class FromMessageRequest {
        <<trait>>
        +from_message_request(req MessageRequest, payload Payload) Result~Self,Error~
        +Error
    }

    class MessageRequest {
        +Option~SocketAddr~ peer_addr
        +HashMap~TypeId,Arc~Any+Send+Sync~~ app_data
        -Option~Mutex~Option~RequestBodyStream~~~ body_stream
        +new() MessageRequest
        +with_peer_addr(addr Option~SocketAddr~) MessageRequest
        +state~T~() Option~SharedState~T~~
        +insert_state~T~(state T) void
        +set_body_stream(stream RequestBodyStream) void
        +take_body_stream() Option~RequestBodyStream~
    }

    class Payload~'a~ {
        +&'a [u8] data
        +new(data &'a [u8]) Payload
        +advance(count usize) void
        +remaining() usize
    }

    class SharedState~T~ {
        +Arc~T~ 0
        +from(inner Arc~T~) SharedState~T~
        +from(inner T) SharedState~T~
        +deref() &T
    }

    class StreamingBody {
        +RequestBodyStream stream
        +new(stream RequestBodyStream) StreamingBody
        +into_stream() RequestBodyStream
        +into_reader() RequestBodyReader
    }

    class ConnectionInfo {
        +Option~SocketAddr~ peer_addr
        +peer_addr() Option~SocketAddr~
    }

    class Message~T~ {
        +T 0
        +into_inner() T
        +deref() &T
    }

    class ExtractError {
        <<enum>>
        MissingState(&'static str)
        InvalidPayload(DecodeError)
        MissingBodyStream
    }

    class WireMessage {
        <<trait>>
        +from_bytes(bytes &[u8]) Result~(Self,usize),DecodeError~
    }

    class DecodeError {
    }

    class RequestBodyStream {
    }

    class RequestBodyReader {
        +new(stream RequestBodyStream) RequestBodyReader
    }

    FromMessageRequest <|.. SharedState
    FromMessageRequest <|.. StreamingBody
    FromMessageRequest <|.. ConnectionInfo
    FromMessageRequest <|.. Message

    MessageRequest --> SharedState : returns
    MessageRequest --> RequestBodyStream : manages
    StreamingBody --> RequestBodyStream : wraps
    StreamingBody --> RequestBodyReader : adapter

    Message --> WireMessage : T bounds WireMessage
    WireMessage --> DecodeError

    ExtractError ..> DecodeError

    SharedState --> "1" T : deref
Loading

Class diagram for codec recovery module

classDiagram
    class RecoveryPolicy {
        <<enum>>
        Drop
        Quarantine
        Disconnect
        +as_str() &str
    }

    class RecoveryConfig {
        +u32 max_consecutive_drops
        +Duration quarantine_duration
        +bool log_dropped_frames
        +max_consecutive_drops(count u32) RecoveryConfig
        +quarantine_duration(duration Duration) RecoveryConfig
        +log_dropped_frames(enabled bool) RecoveryConfig
    }

    class CodecErrorContext {
        +Option~u64~ connection_id
        +Option~SocketAddr~ peer_address
        +Option~u64~ correlation_id
        +Option~String~ codec_state
        +new() CodecErrorContext
        +with_connection_id(id u64) CodecErrorContext
        +with_peer_address(addr SocketAddr) CodecErrorContext
        +with_correlation_id(id u64) CodecErrorContext
        +with_codec_state(state String) CodecErrorContext
    }

    class RecoveryPolicyHook {
        <<trait>>
        +recovery_policy(error CodecError, ctx CodecErrorContext) RecoveryPolicy
        +quarantine_duration(error CodecError, ctx CodecErrorContext) Duration
        +on_error(error CodecError, ctx CodecErrorContext, policy RecoveryPolicy) void
    }

    class DefaultRecoveryPolicy {
        <<struct>>
    }

    class CodecError {
        +default_recovery_policy() RecoveryPolicy
    }

    DefaultRecoveryPolicy ..|> RecoveryPolicyHook
    RecoveryPolicyHook ..> RecoveryPolicy
    RecoveryPolicyHook ..> CodecError
    RecoveryPolicyHook ..> CodecErrorContext

    RecoveryConfig ..> RecoveryPolicy : config for behaviour
    CodecErrorContext ..> RecoveryConfig : may use defaults
Loading

Class diagram for modular WireframeClientBuilder

classDiagram
    class WireframeClientBuilder~S,P,C~ {
        +S serializer
        +ClientCodecConfig codec_config
        +SocketOptions socket_options
        +Option~PreambleConfig~P~~ preamble_config
        +LifecycleHooks~C~ lifecycle_hooks
        +new() WireframeClientBuilder~BincodeSerializer,(),()~
    }

    class ClientCodecConfig {
        +default() ClientCodecConfig
    }

    class SocketOptions {
        +default() SocketOptions
    }

    class PreambleConfig~P~ {
    }

    class LifecycleHooks~C~ {
        +default() LifecycleHooks~C~
    }

    class BincodeSerializer {
    }

    WireframeClientBuilder --> ClientCodecConfig
    WireframeClientBuilder --> SocketOptions
    WireframeClientBuilder --> PreambleConfig
    WireframeClientBuilder --> LifecycleHooks
    BincodeSerializer <.. WireframeClientBuilder : default S
Loading

File-Level Changes

Change Details Files
Modularise the WireframeApp builder into submodules and adjust some internal types
  • Introduce src/app/builder/core.rs defining the WireframeApp struct, its Default/new/with_serializer constructors, and an internal rebuild_with_params helper for type-changing builder flows
  • Switch routes storage to tokio::sync::OnceCell instead of the previous OnceCell type when rebuilding apps
  • Re-export WireframeApp from src/app/builder/mod.rs and remove the old flat src/app/builder.rs, builder_lifecycle.rs, and builder_protocol.rs modules
src/app/builder/core.rs
src/app/builder/mod.rs
src/app/builder/lifecycle.rs
src/app/builder/protocol.rs
src/app/mod.rs
Split frame handling into a dedicated module and make response sending codec-aware with tests
  • Create src/app/frame_handling/mod.rs with core, reassembly, and response submodules, re-exporting ResponseContext, reassemble_if_needed, and forward_response for use by the connection layer
  • Implement codec-aware send_response_payload that wraps payloads via FrameCodec::wrap_payload and handles logging/metrics on failure
  • Add async tests validating send_response_payload behaviour, ResponseContext wiring, and error paths using a custom TestCodec and TestFrame
src/app/frame_handling/mod.rs
src/app/frame_handling/core.rs
src/app/frame_handling/reassembly.rs
src/app/frame_handling/response.rs
src/app/frame_handling/tests.rs
src/app/frame_handling.rs
Refactor extractor system into a composable, typed request/extractor API
  • Introduce MessageRequest and Payload types to hold per-request metadata, shared app_data, and buffered body bytes, including state insertion and a safe one-shot body stream accessor
  • Define FromMessageRequest trait and built-in extractors: ConnectionInfo, Message, SharedState, StreamingBody, plus an ExtractError enum for consistent failure reporting
  • Replace the previous monolithic extractor module with src/extractor/mod.rs and submodules for connection_info, error, message, request, shared_state, streaming, and trait_def
src/extractor/mod.rs
src/extractor/request.rs
src/extractor/trait_def.rs
src/extractor/connection_info.rs
src/extractor/message.rs
src/extractor/shared_state.rs
src/extractor/streaming.rs
src/extractor/error.rs
src/extractor.rs
Modularise codec recovery into a dedicated stack with configuration, context, hooks, and policies plus tests
  • Create src/codec/recovery/mod.rs that re-exports RecoveryConfig, CodecErrorContext, DefaultRecoveryPolicy, RecoveryPolicyHook, and RecoveryPolicy and hosts unit tests
  • Implement RecoveryPolicy enum, RecoveryConfig builder-style configuration struct, CodecErrorContext for structured logging, and RecoveryPolicyHook/DefaultRecoveryPolicy to customize or delegate recovery decisions
  • Add focused tests for default policies, context builder methods, quarantine durations, and RecoveryConfig defaults and builders
src/codec/recovery/mod.rs
src/codec/recovery/config.rs
src/codec/recovery/context.rs
src/codec/recovery/hook.rs
src/codec/recovery/policy.rs
src/codec/recovery/tests.rs
src/codec/recovery.rs
Restructure the client builder into submodules and introduce a macro for type-changing builder methods
  • Add src/client/builder/core.rs defining WireframeClientBuilder with generic serializer, preamble, and connection-state parameters and default construction via new/default
  • Introduce src/client/builder/mod.rs to wire together codec, connect, core, lifecycle, preamble, and serializer submodules and to re-export WireframeClientBuilder
  • Define a builder_field_update macro to reconstruct WireframeClientBuilder when changing serializer, preamble_config, or lifecycle_hooks while preserving other fields and generics
  • Remove the legacy monolithic src/client/builder.rs
src/client/builder/mod.rs
src/client/builder/core.rs
src/client/builder/codec.rs
src/client/builder/connect.rs
src/client/builder/lifecycle.rs
src/client/builder/preamble.rs
src/client/builder/serializer.rs
src/client/builder.rs
Expand and restructure server config tests into a dedicated tests tree with parameterised coverage for backoff, preamble, binding, and integration flows
  • Add src/server/config/tests/mod.rs aggregating new test modules and defining shared enums/helpers such as PreambleHandlerKind and expected_default_worker_count
  • Create tests_basic.rs, tests_binding.rs, tests_integration.rs, and tests_preamble.rs to verify server builder defaults, worker configuration, binding semantics, method chaining, preamble handlers, and timeout clamping
  • Rewrite backoff tests into tests_backoff.rs using a BackoffScenario struct and rstest cases to exercise configuration, validation, exponential doubling/capping behaviour, and defaults
  • Wire tests into src/server/config/mod.rs via a cfg(test) mod tests and delete the old flat src/server/config/tests.rs
src/server/config/mod.rs
src/server/config/tests/mod.rs
src/server/config/tests/tests_basic.rs
src/server/config/tests/tests_binding.rs
src/server/config/tests/tests_integration.rs
src/server/config/tests/tests_preamble.rs
src/server/config/tests/tests_backoff.rs
src/server/config/tests.rs
Minor behavioural and stylistic adjustments related to dependencies and docs
  • Change WireframeApp rebuild to use tokio::sync::OnceCell for routes, aligning with async Tokio primitives
  • Ensure builder_protocol methods protocol and protocol_hooks remain available but are moved into the new app/builder/protocol.rs layout without behaviour change
  • Tighten or reflow some documentation comments and examples for builders, extractors, and recovery types to reflect the new modular layout
src/app/builder/lifecycle.rs
src/app/builder/protocol.rs
src/app/mod.rs
src/app/builder/core.rs
src/extractor/request.rs
src/codec/recovery/*.rs

Assessment against linked issues

Issue Objective Addressed Explanation
#428 Refactor each listed large file into feature-focused submodules (src/connection.rs, src/extractor.rs, src/app/builder.rs, src/client/builder.rs, src/app/frame_handling.rs, src/codec/recovery.rs, src/server/config/tests.rs) while keeping the public API compatible.
#428 Move in-file tests from the listed modules into sibling *_tests.rs-style files or dedicated test modules where appropriate (while wiring them via #[cfg(test)]), improving test modularity.
#428 Ensure the refactoring compiles and existing behavior is preserved by updating tests to the new module structure (including any new tests needed for the split modules).

Possibly linked issues

  • #: PR implements the requested modularization of builder, extractor, frame_handling, codec::recovery, and server config tests.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

codescene-delta-analysis[bot]

This comment was marked as outdated.

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Feb 5, 2026

@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix:

src/server/config/tests/tests_preamble.rs

Comment on lines +52 to +125

async fn test_preamble_handler_registration(
    factory: impl Fn() -> WireframeApp + Send + Sync + Clone + 'static,
    #[case] handler: PreambleHandlerKind,
) {
    let counter = Arc::new(AtomicUsize::new(0));
    let c = counter.clone();

    let server = server_with_preamble(factory);
    let server = match handler {
        PreambleHandlerKind::Success => {
            server.on_preamble_decode_success(move |_p: &TestPreamble, _| {
                let c = c.clone();
                Box::pin(async move {
                    c.fetch_add(1, Ordering::SeqCst);
                    Ok(())
                })
            })
        }
        PreambleHandlerKind::Failure => {
            server.on_preamble_decode_failure(move |_err: &DecodeError, _stream| {
                let c = c.clone();
                Box::pin(async move {
                    c.fetch_add(1, Ordering::SeqCst);
                    Ok::<(), io::Error>(())
                })
            })
        }
    };

    assert_eq!(counter.load(Ordering::SeqCst), 0);
    match handler {
        PreambleHandlerKind::Success => {
            assert!(server.on_preamble_success.is_some());
            let handler = server
                .on_preamble_success
                .as_ref()
                .expect("success handler missing");
            let listener = TcpListener::bind("127.0.0.1:0")
                .await
                .expect("bind listener");
            let addr = listener.local_addr().expect("listener addr");
            let _client = TcpStream::connect(addr)
                .await
                .expect("client connect failed");
            let (mut stream, _) = listener.accept().await.expect("accept stream");
            let preamble = TestPreamble {
                id: 0,
                message: String::new(),
            };
            handler(&preamble, &mut stream)
                .await
                .expect("handler failed");
        }
        PreambleHandlerKind::Failure => {
            assert!(server.on_preamble_failure.is_some());
            let handler = server
                .on_preamble_failure
                .as_ref()
                .expect("failure handler missing");
            let listener = TcpListener::bind("127.0.0.1:0")
                .await
                .expect("bind listener");
            let addr = listener.local_addr().expect("listener addr");
            let _client = TcpStream::connect(addr)
                .await
                .expect("client connect failed");
            let (mut stream, _) = listener.accept().await.expect("accept stream");
            handler(&DecodeError::UnexpectedEnd { additional: 0 }, &mut stream)
                .await
                .expect("handler failed");
        }
    }
    assert_eq!(counter.load(Ordering::SeqCst), 1);
}

❌ New issue: Large Method
test_preamble_handler_registration has 72 lines, threshold = 70

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Feb 5, 2026

@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix:

src/server/config/tests/tests_backoff.rs

Comment on lines +144 to +166

fn test_accept_backoff_parameter_swapping(
    factory: impl Fn() -> WireframeApp + Send + Sync + Clone + 'static,
) {
    let server = WireframeServer::new(factory.clone()).accept_backoff(BackoffConfig {
        initial_delay: Duration::from_millis(5),
        max_delay: Duration::from_millis(1),
    });
    assert_eq!(
        server.backoff_config.initial_delay,
        Duration::from_millis(1)
    );
    assert_eq!(server.backoff_config.max_delay, Duration::from_millis(5));

    let server = WireframeServer::new(factory).accept_backoff(BackoffConfig {
        initial_delay: Duration::ZERO,
        max_delay: Duration::ZERO,
    });
    assert_eq!(
        server.backoff_config.initial_delay,
        Duration::from_millis(1)
    );
    assert_eq!(server.backoff_config.max_delay, Duration::from_millis(1));
}

❌ New issue: Code Duplication
The module contains 4 functions with similar structure: test_accept_backoff_parameter_swapping,test_accept_initial_delay_configuration,test_accept_max_delay_configuration,test_backoff_validation

@coderabbitai

This comment was marked as resolved.

@coderabbitai

This comment was marked as resolved.

@leynos leynos changed the title Refactor: modularize large files into submodules Refactor: modularize into submodules; parameterize backoff tests Feb 5, 2026
@leynos leynos force-pushed the refactor-modularize-large-files-ixca6n branch from 3e408b0 to c2e5431 Compare February 5, 2026 13:46
@leynos leynos marked this pull request as ready for review February 5, 2026 18:10
sourcery-ai[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

@leynos leynos force-pushed the refactor-modularize-large-files-ixca6n branch from c2e5431 to 92faee4 Compare February 6, 2026 01:53
@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Feb 6, 2026

@coderabbitai review

@coderabbitai

This comment was marked as resolved.

@coderabbitai coderabbitai Bot added the codex label Feb 6, 2026
coderabbitai[bot]

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@src/app/builder_defaults.rs`:
- Around line 7-10: Add a Rustdoc comment for the constant
DEFAULT_READ_TIMEOUT_MS so it appears in generated docs: locate the constant
DEFAULT_READ_TIMEOUT_MS in builder_defaults.rs and add a /// comment above it
describing what the constant represents (the default read timeout in
milliseconds) and any important constraints or units (e.g., that values are in
milliseconds and bounded by MIN_READ_TIMEOUT_MS and MAX_READ_TIMEOUT_MS).

In `@src/codec/recovery/tests.rs`:
- Around line 10-20: The fixture functions default_hook and default_ctx include
unnecessary compiler_fence calls; remove the
std::sync::atomic::compiler_fence(std::sync::atomic::Ordering::SeqCst) lines so
each fixture simply returns its value (DefaultRecoveryPolicy for default_hook
and CodecErrorContext::new() for default_ctx). Ensure the fixtures remain
annotated with #[fixture] and keep the function signatures and return types
unchanged while deleting the fence calls.

In `@src/server/mod.rs`:
- Around line 20-22: Add a Rustdoc comment for the crate-visible function
default_worker_count describing its purpose and behavior: that it returns the
number of workers based on std::thread::available_parallelism() with a fallback
of 1, mention the return type usize and that it is crate-visible, and place the
doc comment (///) immediately above the fn default_worker_count() declaration so
it appears in generated cargo doc output.

Comment thread src/app/builder_defaults.rs
Comment thread src/codec/recovery/tests.rs Outdated
Comment thread src/server/mod.rs
@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Feb 9, 2026

@coderabbitai Have the following now been resolved?

Please address the comments from this code review:

## Overall Comments
- The `test_accept_exponential_backoff_doubles_and_caps` test relies on real `thread::sleep` timing with narrow tolerances, which can be flaky under CI load; consider rewriting this to simulate or inject a time source rather than sleeping in a loop.
- `expected_default_worker_count()` in `server/config/tests/mod.rs` manually mirrors the production default worker logic; if the implementation changes these tests will silently diverge, so it would be more robust to expose and reuse the actual default worker computation from the server module instead of duplicating it in tests.

## Individual Comments

### Comment 1
<location> `src/app/frame_handling/response.rs:62-64` </location>
<code_context>
-            break; // already logged
-        };
-
-        if send_response_payload::<F, W>(ctx.codec, ctx.framed, Bytes::from(bytes), &response)
-            .await
-            .is_err()
-        {
-            break;
</code_context>

<issue_to_address>
**issue (bug_risk):** forward_response swallows I/O errors from send_response_payload and always returns Ok(())

On send failure this path just `break`s, and `forward_response` still returns `Ok(())`. Given the `io::Result<()>` signature, callers can’t tell a clean completion from a write failure. Consider propagating the error (or at least returning the last `io::Error`) so upstream code can handle connection teardown or marking appropriately.
</issue_to_address>

### Comment 2
<location> `src/server/config/tests/tests_backoff.rs:72-73` </location>
<code_context>
-
-    use super::*;
-
-    #[test]
-    fn recovery_policy_default_is_drop() {
-        assert_eq!(RecoveryPolicy::default(), RecoveryPolicy::Drop);
</code_context>

<issue_to_address>
**issue (testing):** `test_accept_exponential_backoff_doubles_and_caps` is timing-based and may be flaky.

This test relies on `thread::sleep` and `Instant` with a very tight window (`expected..expected+20ms`), which can be flaky on slower or busy CI runners and also slows the suite.

Please consider making it deterministic by either:
- Extracting the backoff calculation into a pure function and asserting on the resulting `Duration` sequence, or
- Greatly relaxing the timing bounds and/or using minimal sleeps or a virtual/mock clock if available.

This will improve test reliability and execution time.
</issue_to_address>

### Comment 3
<location> `src/extractor/request.rs:51` </location>
<code_context>
+    ///
+    /// use wireframe::extractor::MessageRequest;
+    ///
+    /// let req = MessageRequest::new().with_peer_addr(Some("127.0.0.1:8080".parse().unwrap()));
+    /// assert!(req.peer_addr.is_some());
+    /// ```
</code_context>

<issue_to_address>
**nitpick (review_instructions):** Use `.expect(...)` instead of `.unwrap()` in the example to align with the project guideline of preferring `expect` over `unwrap`.

The docs example should also follow the "Prefer `.expect()` over `.unwrap()`" rule. Consider changing this to something like:

```rust
let req = MessageRequest::new()
    .with_peer_addr(Some("127.0.0.1:8080".parse().expect("valid socket address")));
```

<details>
<summary>Review instructions:</summary>

**Path patterns:** `**/*.rs`

**Instructions:**
Prefer `.expect()` over `.unwrap()`

</details>
</issue_to_address>

### Comment 4
<location> `src/extractor/request.rs:72` </location>
<code_context>
+    ///     extractor::{MessageRequest, SharedState},
+    /// };
+    ///
+    /// let _app = WireframeApp::new().unwrap().app_data(5u32);
+    /// // The framework populates the request with application data.
+    /// # let mut req = MessageRequest::default();
</code_context>

<issue_to_address>
**nitpick (review_instructions):** The documentation example should prefer `.expect(...)` over `.unwrap()` when constructing `WireframeApp`.

To match the "Prefer `.expect()` over `.unwrap()`" guideline, please switch this example to use `expect` instead of `unwrap`, e.g.:

```rust
let _app = WireframeApp::new().expect("failed to initialize app").app_data(5u32);
```

<details>
<summary>Review instructions:</summary>

**Path patterns:** `**/*.rs`

**Instructions:**
Prefer `.expect()` over `.unwrap()`

</details>
</issue_to_address>

### Comment 5
<location> `src/extractor/connection_info.rs:23` </location>
<code_context>
+    ///
+    /// use wireframe::extractor::{ConnectionInfo, FromMessageRequest, MessageRequest, Payload};
+    ///
+    /// let addr: SocketAddr = "127.0.0.1:8080".parse().unwrap();
+    /// let req = MessageRequest::new().with_peer_addr(Some(addr));
+    /// let info = ConnectionInfo::from_message_request(&req, &mut Payload::default()).unwrap();
</code_context>

<issue_to_address>
**nitpick (review_instructions):** Replace `.unwrap()` with `.expect(...)` in this example to adhere to the `expect`-over-`unwrap` guideline.

Even in examples, we should avoid bare `unwrap`. Consider something like:

```rust
let addr: SocketAddr = "127.0.0.1:8080".parse().expect("valid socket address");
```

<details>
<summary>Review instructions:</summary>

**Path patterns:** `**/*.rs`

**Instructions:**
Prefer `.expect()` over `.unwrap()`

</details>
</issue_to_address>

### Comment 6
<location> `src/extractor/connection_info.rs:25` </location>
<code_context>
+    ///
+    /// let addr: SocketAddr = "127.0.0.1:8080".parse().unwrap();
+    /// let req = MessageRequest::new().with_peer_addr(Some(addr));
+    /// let info = ConnectionInfo::from_message_request(&req, &mut Payload::default()).unwrap();
+    /// assert_eq!(info.peer_addr(), Some(addr));
+    /// ```
</code_context>

<issue_to_address>
**nitpick (review_instructions):** Update the example to use `.expect(...)` instead of `.unwrap()` when calling `from_message_request`.

To stay consistent with the "Prefer `.expect()` over `.unwrap()`" rule, this could be:

```rust
let info = ConnectionInfo::from_message_request(&req, &mut Payload::default())
    .expect("connection info extraction should succeed");
```

<details>
<summary>Review instructions:</summary>

**Path patterns:** `**/*.rs`

**Instructions:**
Prefer `.expect()` over `.unwrap()`

</details>
</issue_to_address>

@coderabbitai

This comment was marked as resolved.

@devboxerhub devboxerhub Bot force-pushed the refactor-modularize-large-files-ixca6n branch from 4085772 to 5fc52e0 Compare February 9, 2026 12:20
@leynos leynos changed the title Refactor: modularize into submodules; parameterize backoff tests Refactor: modularize into submodules; rebase onto origin/main Feb 9, 2026
coderabbitai[bot]

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/app/frame_handling/response.rs`:
- Around line 24-48: Update the doc comment for forward_response to explicitly
state that application handler errors are absorbed: note that when
service.call(request) returns Err(e) (the Err(e) branch) the error is logged and
metrics are incremented (crate::metrics::inc_handler_errors()) and the function
returns Ok(()) instead of propagating a transport error, i.e., this is an
intentional "log-and-continue" behavior for application-level handler failures
distinct from transport (io::Error) failures which are still propagated; place
this brief note in the existing /// doc above the function so callers of
forward_response (which accepts an Envelope and creates a ServiceRequest)
understand the semantics.

Comment thread src/app/frame_handling/response.rs
leynos and others added 8 commits February 9, 2026 15:39
…client builder

Removed the monolithic src/app/builder.rs file and replaced it with a modular builder implementation
split across multiple files (codec.rs, config.rs, core.rs, lifecycle.rs, mod.rs, protocol.rs, routing.rs, state.rs). This
improves maintainability and readability by organizing related functionality into dedicated modules.

Deleted the deprecated src/client/builder.rs and related client builder modules, cleaning up the codebase.

Additionally, extracted frame handling helpers into a frame_handling module with submodules for core, reassembly,
and response forwarding logic, keeping connection.rs smaller and more focused.

Overall, this commit significantly improves project structure by organizing app builder and frame handling
code into well-defined modules, and removing outdated client builder code.

Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
…s and add tcp connection setup helper

- Replace multiple individual backoff config tests with parameterized test cases for better coverage and conciseness.
- Add detailed cases including clamp, swap, and default value scenarios for backoff configuration validation.
- Extract and reuse async TCP connection setup in preamble tests to reduce redundancy and improve readability.

Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
…ruct

- Introduced BackoffScenario struct to encapsulate test parameters
- Updated test cases to use BackoffScenario instances for clarity
- Simplified assertions by using scenario description messages
- Improved maintainability and readability of backoff config tests

Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
Run cargo fmt to align parameterised test assertions
with project formatting rules.
…client builder

Removed the monolithic src/app/builder.rs file and replaced it with a modular builder implementation
split across multiple files (codec.rs, config.rs, core.rs, lifecycle.rs, mod.rs, protocol.rs, routing.rs, state.rs). This
improves maintainability and readability by organizing related functionality into dedicated modules.

Deleted the deprecated src/client/builder.rs and related client builder modules, cleaning up the codebase.

Additionally, extracted frame handling helpers into a frame_handling module with submodules for core, reassembly,
and response forwarding logic, keeping connection.rs smaller and more focused.

Overall, this commit significantly improves project structure by organizing app builder and frame handling
code into well-defined modules, and removing outdated client builder code.

Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
…r and improve logging

- Replace nested matches with `?` operator in response serialization and sending
- Enhance logging messages with consistent formatting and variable interpolation
- Add documentation comments for DeserFailureTracker and ResponseContext
- Improve error propagation and metric increments
- Add and enhance tests with rstest fixtures and parameterization
- General code cleanup for readability and safety (e.g., saturating_add)

Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
…, tests, and code clarity

- Propagate underlying I/O errors in response handling instead of generic messages
- Simplify recovery policy tests by removing rstest fixtures
- Add Debug impl for SharedState with non-exhaustive output
- Document default worker count computation in server module
- Minor test assertion cleanup and added doc comment for default read timeout constant

Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
Apply rustfmt output produced during post-rebase validation so
working tree is clean and quality gates remain reproducible.
@devboxerhub devboxerhub Bot force-pushed the refactor-modularize-large-files-ixca6n branch from cbe37fe to 56b1e1f Compare February 9, 2026 15:41
Extended the documentation of `forward_response` to clarify its behavior when handling errors from the service call. The docs now explain the difference between application-level errors, which are logged and counted but do not propagate an error, and transport-level errors, which are propagated as `io::Error`. This improves code understandability and maintenance.

Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
@leynos leynos merged commit 3ddde5a into main Feb 9, 2026
5 checks passed
@leynos leynos deleted the refactor-modularize-large-files-ixca6n branch February 9, 2026 19:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant