runnel 0.4.2

the pluggable io stream. now support: stdio, string io, in memory pipe, line pipe
Documentation
# Design Specification

This document details the design of the `runnel` library, aligning with the acceptance criteria derived from the source code.

## 1. Core Architecture: Pluggable I/O

The library is designed around a central concept of abstract, swappable I/O streams. The core architecture allows an application to be decoupled from the specific source of its input and destination of its output/error streams. This is achieved through a set of public traits that define the capabilities of a stream, and a container struct that holds concrete implementations of these traits.

This design directly satisfies the core requirement for a pluggable I/O system (**U1**, **O1**).

## 2. Core Components

### 2.1. I/O Traits

The foundation of the library is a set of three traits that abstract I/O operations:

*   `trait StreamIn`: Represents a readable input stream. It provides methods for reading bytes (`lock_bufread`) and iterating over lines (`lines`).
*   `trait StreamOut`: Represents a writable output stream. It provides methods for writing bytes (`lock`) and handling line-based writing (`write_line`, `flush_line`).
*   `trait StreamErr`: Represents a writable error stream, with a parallel interface to `StreamOut`.

These traits ensure that any custom I/O implementation will be compatible with the system, satisfying the extensibility requirement (**U2**).

### 2.2. `RunnelIoe` Struct

This is the main container struct that an application interacts with. It holds three boxed trait objects:

```rust
// Simplified
pub struct RunnelIoe {
    pg_in: Box<dyn StreamIn>,
    pg_out: Box<dyn StreamOut>,
    pg_err: Box<dyn StreamErr>,
}
```

By using trait objects, `RunnelIoe` can hold any struct that implements the required I/O traits, providing the core of the pluggable architecture.

### 2.3. `RunnelIoeBuilder`

To simplify the creation of `RunnelIoe` instances, a builder pattern is implemented (**O1**). `RunnelIoeBuilder` allows a user to optionally specify implementations for each of the three streams. If any stream is not specified, it defaults to the standard I/O implementation upon calling `.build()`. This directly addresses criterion **E1**.

```rust
// Usage
let sioe = RunnelIoeBuilder::new()
    .pg_in(MyCustomInput::new())
    .build(); // pg_out and pg_err default to stdout/stderr
```

## 3. Stream Implementations (The `medium` module)

The library provides four concrete implementations of the I/O traits.

### 3.1. `stdio`

This module provides thin wrappers around `std::io::stdin`, `std::io::stdout`, and `std::io::stderr`. `StdIn`, `StdOut`, and `StdErr` structs implement the corresponding `StreamIn`, `StreamOut`, and `StreamErr` traits. This is the default implementation, ensuring out-of-the-box usability.

### 3.2. `stringio`

This implementation uses in-memory `String` and `Vec<u8>` buffers to simulate I/O streams (`StringIn`, `StringOut`). This is crucial for testing application logic without depending on a live console, satisfying **S1** and **O2**.

### 3.3. `pipeio`

This module implements an in-memory, thread-safe byte pipe. It uses a `std::sync::mpsc::sync_channel` to send `Vec<u8>` chunks between threads. `PipeOut` is the sender and `PipeIn` is the receiver. This design fulfills the requirements for inter-thread byte-stream communication (**S2**, **O3**, **E3**).

### 3.4. `linepipeio`

Similar to `pipeio`, this module also uses an `mpsc::sync_channel`, but it is optimized for sending `String` objects line-by-line. This avoids the overhead of parsing a byte stream back into lines on the receiving end, offering a more performant and convenient API for line-based protocols. This design fulfills the requirements for inter-thread line-based communication (**S3**, **O4**, **E4**).

## 4. Concurrency and Thread Safety

Thread safety is a primary concern, especially for the `pipeio` and `linepipeio` modules. The design addresses this in two ways:

1.  **Channel-based Communication**: The use of `mpsc` channels for pipes is inherently thread-safe, as these channels are designed for single-consumer, multiple-producer message passing.
2.  **Mutex Guards**: For other shared resources, the library uses `std::sync::Mutex` to protect shared data. The `lock()` methods on the traits return a guard object, ensuring that the lock is held for the duration of the I/O operation and automatically released afterward.

This locking strategy ensures that concurrent access to streams is safe, satisfying criterion **U3**.