Skip to main content

AsyncLineReader

Struct AsyncLineReader 

Source
pub struct AsyncLineReader<R: AsyncRead + Unpin> { /* private fields */ }
Expand description

Buffered async line reader with line number tracking.

Reads input line-by-line asynchronously, automatically handling different line endings (LF, CRLF) and tracking the current line number for error reporting.

§Performance Characteristics

  • Buffering: Configurable buffer size (default 64KB) for efficient I/O
  • Zero-Copy: String allocations only for consumed lines
  • Async: Non-blocking I/O suitable for high-concurrency scenarios

§When to Use Async vs Sync

Use Async When:

  • Processing network streams or pipes
  • High-concurrency scenarios (many parallel streams)
  • Integration with async web servers or frameworks
  • Need to process I/O without blocking threads

Use Sync When:

  • Processing local files
  • Single-threaded batch processing
  • Simpler code without async complexity
  • CPU-bound workloads with minimal I/O wait

§Examples

§Reading from Async Source

use hedl_stream::AsyncLineReader;
use tokio::fs::File;

let file = File::open("data.hedl").await?;
let mut reader = AsyncLineReader::new(file);

while let Some((line_num, line)) = reader.next_line().await? {
    println!("{}: {}", line_num, line);
}

§With Custom Buffer Size

use hedl_stream::AsyncLineReader;
use std::io::Cursor;

let input = "line1\nline2";
let reader = AsyncLineReader::with_capacity(Cursor::new(input), 256 * 1024);

Implementations§

Source§

impl<R: AsyncRead + Unpin> AsyncLineReader<R>

Source

pub fn new(reader: R) -> Self

Create a new async line reader with default buffer size (64KB) and max line length (1MB).

§Examples
use hedl_stream::AsyncLineReader;
use std::io::Cursor;

let input = "line1\nline2";
let reader = AsyncLineReader::new(Cursor::new(input));
Source

pub fn with_capacity(reader: R, capacity: usize) -> Self

Create an async line reader with a specific buffer capacity and default max line length (1MB).

§Parameters
  • reader: The async readable source
  • capacity: Buffer size in bytes
§Examples
use hedl_stream::AsyncLineReader;
use std::io::Cursor;

// Use a larger buffer for large files
let reader = AsyncLineReader::with_capacity(
    Cursor::new("data"),
    256 * 1024  // 256KB
);
Source

pub fn with_max_length(reader: R, max_line_length: usize) -> Self

Create with a specific max line length.

Source

pub fn with_capacity_and_max_length( reader: R, capacity: usize, max_line_length: usize, ) -> Self

Create with a specific buffer capacity and max line length.

Source

pub fn line_number(&self) -> usize

Get the current line number.

Returns 0 before any lines are read, then increments with each line.

§Examples
use hedl_stream::AsyncLineReader;
use std::io::Cursor;

let mut reader = AsyncLineReader::new(Cursor::new("line1\nline2"));

assert_eq!(reader.line_number(), 0);
reader.next_line().await?;
assert_eq!(reader.line_number(), 1);
reader.next_line().await?;
assert_eq!(reader.line_number(), 2);
Source

pub async fn next_line(&mut self) -> StreamResult<Option<(usize, String)>>

Read the next line asynchronously.

Returns Ok(Some((line_num, line))) if a line was read, Ok(None) at EOF, or Err on I/O errors.

Trailing newlines (LF or CRLF) are automatically stripped.

§Performance

This method awaits on I/O and yields to the runtime if data is not available, allowing other tasks to run. It does not block the thread.

§Examples
use hedl_stream::AsyncLineReader;
use std::io::Cursor;

let mut reader = AsyncLineReader::new(Cursor::new("hello\nworld"));

let (num, line) = reader.next_line().await?.unwrap();
assert_eq!(num, 1);
assert_eq!(line, "hello");

let (num, line) = reader.next_line().await?.unwrap();
assert_eq!(num, 2);
assert_eq!(line, "world");

assert_eq!(reader.next_line().await?, None);
Source

pub async fn peek_line(&mut self) -> StreamResult<Option<&(usize, String)>>

Peek at the next line without consuming it.

Returns a reference to the next line without advancing the reader. Subsequent calls to peek_line() return the same line. Call next_line() to consume it.

§Examples
use hedl_stream::AsyncLineReader;
use std::io::Cursor;

let mut reader = AsyncLineReader::new(Cursor::new("line1\nline2"));

// Peek multiple times
assert_eq!(reader.peek_line().await?, Some(&(1, "line1".to_string())));
assert_eq!(reader.peek_line().await?, Some(&(1, "line1".to_string())));

// Consume
reader.next_line().await?;

// Next peek is the second line
assert_eq!(reader.peek_line().await?, Some(&(2, "line2".to_string())));
Source

pub fn push_back(&mut self, line_num: usize, line: String)

Push a line back to be read again.

The next call to next_line() or peek_line() will return this line. Only one line can be pushed back at a time; subsequent calls overwrite the previously pushed line.

§Examples
use hedl_stream::AsyncLineReader;
use std::io::Cursor;

let mut reader = AsyncLineReader::new(Cursor::new("line1\nline2"));

let line = reader.next_line().await?.unwrap();
assert_eq!(line, (1, "line1".to_string()));

// Push it back
reader.push_back(line.0, line.1);

// Read it again
let line = reader.next_line().await?.unwrap();
assert_eq!(line, (1, "line1".to_string()));

Auto Trait Implementations§

§

impl<R> Freeze for AsyncLineReader<R>
where R: Freeze,

§

impl<R> RefUnwindSafe for AsyncLineReader<R>
where R: RefUnwindSafe,

§

impl<R> Send for AsyncLineReader<R>
where R: Send,

§

impl<R> Sync for AsyncLineReader<R>
where R: Sync,

§

impl<R> Unpin for AsyncLineReader<R>

§

impl<R> UnwindSafe for AsyncLineReader<R>
where R: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.