-
Notifications
You must be signed in to change notification settings - Fork 0
Commit 666149c
committed
Merge #137: Add type-safe channel routing for UserOutput with newtype wrappers
d24db0a refactor: Improve encapsulation with writeln methods (copilot-swe-agent[bot])
470d643 style: Apply rustfmt formatting (copilot-swe-agent[bot])
b49f0dd feat: Add type-safe channel routing with newtype wrappers (copilot-swe-agent[bot])
5016d98 Initial plan (copilot-swe-agent[bot])
Pull request description:
Channel routing (stdout vs stderr) in `UserOutput` was done via runtime pattern matching. This adds compile-time guarantees using newtype wrappers to prevent accidental channel confusion.
## Changes
**Type-Safe Wrappers** (private)
- `StdoutWriter` and `StderrWriter` newtypes wrapping `Box<dyn Write + Send + Sync>`
- Methods: `new()`, `write_line()`, `writeln()`
- Zero-cost abstraction via newtype pattern
**UserOutput Structure**
- Fields changed from `Box<dyn Write>` to typed wrappers (`stdout: StdoutWriter`, `stderr: StderrWriter`)
- Private helpers `write_to_stdout()` and `write_to_stderr()` for type-safe dispatch
- `write()` method now uses compile-time channel routing instead of runtime matching
**Example**
Before (runtime dispatch):
```rust
let writer = match message.channel() {
Channel::Stdout => &mut self.stdout_writer,
Channel::Stderr => &mut self.stderr_writer,
};
write!(writer, "{formatted}").ok();
```
After (type-safe dispatch):
```rust
match message.channel() {
Channel::Stdout => self.write_to_stdout(&formatted),
Channel::Stderr => self.write_to_stderr(&formatted),
}
```
**Compatibility**
- All existing tests pass without modification
- Public API unchanged
- Added 8 tests demonstrating compile-time safety
> [!WARNING]
>
> <details>
> <summary>Firewall rules blocked me from connecting to one or more addresses (expand for details)</summary>
>
> #### I tried to connect to the following addresses, but was blocked by firewall rules:
>
> - `192.0.2.1`
> - Triggering command: `ssh -i /nonexistent/key -p 22 -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o ConnectTimeout=5 testuser@192.0.2.1 echo 'SSH connected'` (packet block)
> - Triggering command: `ssh -i /nonexistent/key -p 22 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=5 testuser@192.0.2.1 echo 'SSH connected'` (packet block)
>
> If you need me to access, download, or install something from one of these locations, you can either:
>
> - Configure [Actions setup steps](https://gh.io/copilot/actions-setup-steps) to set up my environment, which run before the firewall is enabled
> - Add the appropriate URLs or hosts to the custom allowlist in this repository's [Copilot coding agent settings](https://github.com/torrust/torrust-tracker-deployer/settings/copilot/coding_agent) (admins only)
>
> </details>
<!-- START COPILOT CODING AGENT SUFFIX -->
<details>
<summary>Original prompt</summary>
----
*This section details on the original issue you should resolve*
<issue_title>Type-Safe Channel Routing for User Output</issue_title>
<issue_description>## Overview
Add compile-time type safety for channel routing (stdout vs stderr) in the `UserOutput` module by introducing newtype wrappers for writers. Currently, channel routing is done with runtime pattern matching which, while functional, doesn't provide compile-time guarantees that messages go to the correct channel.
This refactoring introduces `StdoutWriter` and `StderrWriter` newtype wrappers that make channel routing explicit in the type system, preventing accidental channel confusion at compile time.
## Specification
See detailed specification: [docs/issues/135-type-safe-channel-routing-for-user-output.md](https://github.com/torrust/torrust-tracker-deployer/blob/main/docs/issues/135-type-safe-channel-routing-for-user-output.md)
## 🏗️ Architecture Requirements
**DDD Layer**: Presentation
**Module Path**: `src/presentation/user_output.rs`
**Pattern**: Type-safe wrappers using newtype pattern
### Module Structure Requirements
- [ ] Follow module organization conventions (see [docs/contributing/module-organization.md](https://github.com/torrust/torrust-tracker-deployer/blob/main/docs/contributing/module-organization.md))
- [ ] Keep writer wrappers private as implementation details
- [ ] Maintain public API compatibility with existing code
### Architectural Constraints
- [ ] Zero-cost abstraction using newtype pattern
- [ ] No runtime overhead compared to current implementation
- [ ] Preserve existing error handling behavior
- [ ] Error handling follows project conventions (see [docs/contributing/error-handling.md](https://github.com/torrust/torrust-tracker-deployer/blob/main/docs/contributing/error-handling.md))
### Anti-Patterns to Avoid
- ❌ Exposing writer wrappers in public API unnecessarily
- ❌ Adding runtime checks when compile-time safety is available
- ❌ Breaking existing test infrastructure
## Implementation Plan
### Phase 1: Create Newtype Wrappers (30 minutes)
- [ ] Task 1.1: Create `StdoutWriter` newtype struct with documentation
- [ ] Task 1.2: Create `StderrWriter` newtype struct with documentation
- [ ] Task 1.3: Implement `new()` constructor for both wrappers
- [ ] Task 1.4: Implement `write_line()` method for both wrappers
- [ ] Task 1.5: Add unit tests for wrapper creation and writing
### Phase 2: Update UserOutput Structure (45 minutes)
- [ ] Task 2.1: Update `UserOutput` struct fields to use typed wrappers
- [ ] Task 2.2: Add private helper methods `write_to_stdout()` and `write_to_stderr()`
- [ ] Task 2.3: Update all constructors to wrap writers in typed newtype
- [ ] Task 2.4: Update `write()` method to use typed writer helpers
- [ ] Task 2.5: Verify all existing public methods compile and work correctly
### Phase 3: Update Tests (30 minutes)
- [ ] Task 3.1: Update test infrastructure to work with newtype wrappers
- [ ] Task 3.2: Add tests for type-safe channel routing
- [ ] Task 3.3: Verify all existing tests pass without modification
- [ ] Task 3.4: Add test cases demonstrating compile-time safety benefits
### Phase 4: Documentation and Quality (30 minutes)
- [ ] Task 4.1: Update module documentation to mention type-safe routing
- [ ] Task 4.2: Add code examples showing type safety benefits
- [ ] Task 4.3: Run pre-commit checks and fix any issues
- [ ] Task 4.4: Verify documentation builds correctly
## Acceptance Criteria
> **Note for Contributors**: These criteria define what the PR reviewer will check. Use this as your pre-review checklist before submitting the PR to minimize back-and-forth iterations.
**Quality Checks**:
- [ ] Pre-commit checks pass: `./scripts/pre-commit.sh`
- [ ] All tests pass: `cargo test`
- [ ] Documentation builds: `cargo doc --no-deps`
**Task-Specific Criteria**:
- [ ] `StdoutWriter` and `StderrWriter` newtype wrappers are implemented
- [ ] Both wrappers have `new()` and `write_line()` methods
- [ ] `UserOutput` struct uses typed wrappers instead of raw `Box<dyn Write>`
- [ ] All constructors wrap raw writers in typed newtypes
- [ ] Private helper methods `write_to_stdout()` and `write_to_stderr()` exist
- [ ] The `write()` method uses typed helpers instead of direct writer access
- [ ] All existing tests pass without modification
- [ ] New tests demonstrate compile-time safety benefits
- [ ] Module documentation is updated to reflect type-safe routing
- [ ] Code examples show the benefits of the newtype pattern
- [ ] No performance regression (zero-cost abstraction)
- [ ] IDE autocomplete shows channel-specific methods
## Related
- Parent: #102 (Epic: User Output Architecture Improvements)
- Specification: docs/issues/135-type-safe-channel-routing-for-user-output.md
- Refactoring Plan: [docs/refactors/plans/user-output-architecture-improvements.md](https://github.com/torrust/torrust-tracker-deployer/blob/main/docs/refactors/plans/use...
</details>
- Fixes #135
<!-- START COPILOT CODING AGENT TIPS -->
---
✨ Let Copilot coding agent [set things up for you](https://github.com/torrust/torrust-tracker-deployer/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot) — coding agent works faster and does higher quality work when set up for your repo.
ACKs for top commit:
josecelano:
ACK d24db0a
Tree-SHA512: 9e916c8e7dfa799e9314bf7f0d8925cfd24d584c85ee395acf166f0a5d736867f2ead9f9dc3bea1c0359305f8ebaefe859c9a9c73a3e9835cd4f1bf278388c4bFile tree
Expand file treeCollapse file tree
1 file changed
+306
-38
lines changedOpen diff view settings
Filter options
- src/presentation
Expand file treeCollapse file tree
1 file changed
+306
-38
lines changedOpen diff view settings
0 commit comments