csv-cat
CSV processing built on comp-cat-rs. All operations return Io<CsvError, _> for composable effect handling. Nothing runs until you call .run().
Wraps the battle-tested csv crate for RFC 4180 compliance; adds lazy evaluation, resource safety, and streaming on top.
Installation
[]
= "0.1"
Quick start
use ;
use ;
use CsvError;
// Read from a string
let rows = from_str.run?;
// Access fields by index
let name = rows.get?; // "alice"
let age = rows.get?; // "30"
// Write back to a string
let output = to_string.run?;
// "name,age\nalice,30\nbob,25\n"
Reading
From a file
use ;
let rows = read_all.run?;
The file is opened, fully read, and closed within the Io. Nothing happens until .run().
From a string
use ;
let rows = from_str.run?;
With a Resource
For explicit acquire/release lifecycle:
use ;
let resource = reader_resource;
let result = resource.use_resource.run?;
Configuration
use ReaderConfig;
let config = new
.has_headers // first row is data, not headers
.delimiter // tab-separated
.flexible; // allow varying field counts
| Method | Default | Description |
|---|---|---|
has_headers(bool) |
true |
Whether the first row is a header |
delimiter(u8) |
b',' |
Field delimiter byte |
flexible(bool) |
false |
Allow rows with different field counts |
Writing
To a file
use ;
use Row;
let rows = vec!;
write_all.run?;
To a string
use ;
let output = to_string.run?;
Configuration
use WriterConfig;
let config = new
.delimiter // tab-separated output
.has_headers; // skip header row
Row
Row is a newtype over csv::StringRecord with accessor methods.
| Method | Signature | Description |
|---|---|---|
get(index) |
usize -> Result<&str, CsvError> |
Get field by index |
len() |
-> usize |
Number of fields |
is_empty() |
-> bool |
Whether the row has zero fields |
fields() |
-> impl Iterator<Item = &str> |
Iterate over all fields |
to_vec() |
-> Vec<String> |
Collect all fields as owned strings |
deserialize(headers) |
-> Result<T, CsvError> |
Deserialize into a typed value via serde |
Typed deserialization
use Deserialize;
use ;
let data = "name,age\nalice,30\nbob,25\n";
let rows = from_str.run?;
let headers = from;
let person: Person = rows.deserialize?;
// person.name == "alice", person.age == 30
Error handling
CsvError is a hand-rolled enum covering all failure modes:
| Variant | Source | When |
|---|---|---|
Csv(csv::Error) |
csv crate | Malformed CSV, encoding issues |
Io(std::io::Error) |
std | File not found, permission denied |
MissingField { index } |
csv-cat | Row::get with out-of-bounds index |
Deserialize(String) |
csv-cat | Row::deserialize type mismatch |
All variants implement From for ? ergonomics:
use CsvError;
Composing with other comp-cat-rs effects
Since everything is Io<CsvError, _>, you can compose CSV operations with the full comp-cat-rs toolkit.
Error recovery
let rows = read_all
.handle_error; // empty vec on failure
Parallel processing
use par_zip;
let file_a = read_all;
let file_b = read_all;
// Read both files concurrently on separate threads
let = par_zip.run?;
Chaining with other IO
let pipeline = from_str
.map
.flat_map;
let message = pipeline.run?;
// "Read 1 rows"
Why comp-cat-rs?
The csv crate is excellent on its own. csv-cat adds:
- Lazy evaluation: nothing executes until
.run(), so you can build and compose pipelines before committing to side effects - Resource safety:
reader_resource/writer_resourceuse the bracket pattern to guarantee cleanup - Composability:
map,flat_map,zip,handle_errorchain CSV operations with any otherIo-based effect - Concurrency:
Fiber::forkandpar_zipfor parallel file processing, with no async/tokio
License
MIT