Getting started
saga_csv is a CSV reader and writer for saga. It is also a working example of a saga library backed by a Rust NIF — specifically, a rustler-built crate wrapping csv-core, wired into the project via rebar3 pre-hooks. If you are looking for a template for shipping Rust code from a saga project, this repo is small enough to read end-to-end.
Install
Add saga_csv to your project.toml:
[dependencies]
saga_csv = { git = "https://github.com/dylantf/saga_csv" }Then saga install to fetch it. Because the library ships a NIF, the first build will need both cargo and rebar3 on PATH. rebar3 compile builds the Rust crate as a cdylib, drops the shared library into priv/, and saga build picks it up.
Your first round-trip
The two entry points to remember are reader () and writer (). They each produce a Config value you customize with builders, then hand to parse or write.
import SagaCsv (reader, writer, with_headers, skip_header, parse, write)
let csv =
writer ()
|> with_headers ["name", "age"]
|> write [["Alice", "30"], ["Bob", "25"]]
# "name,age\nAlice,30\nBob,25\n"
let rows =
reader ()
|> skip_header
|> parse csv
# [["Alice", "30"], ["Bob", "25"]]That is the whole shape of the API: build a config, pipe it into a parse or write call.
When to reach for keyed variants
parse and write deal in List (List String) — positional rows. If your code is happier with Dict String String keyed by column name, use parse_keyed and write_keyed instead. Both rely on the header row: parse_keyed takes it from the input, write_keyed takes it from with_headers.
See Reading CSV and Writing CSV for the details of each side.
How the NIF fits in
If you are using this repo as a reference for your own NIF:
native/Cargo.tomldeclares the Rust crate (crate-type = ["cdylib"],rustler = "0.36").native/src/lib.rsdefines two#[rustler::nif]functions and registers them withrustler::init!("saga_csv_nif"). The name must match the Erlang module that loads the NIF.lib/saga_csv_nif.erlis the loader.lib/saga_csv_bridge.erlis the Erlang-side thin wrapper that translates saga calls into NIF calls.rebar.configrunsmakeinnative/as a pre-hook sorebar3 compilebuilds the Rust library before compiling the bridge module.lib/SagaCsv.sagacalls the bridge via@external("erlang", "saga_csv_bridge", "...").
That is the whole chain. Everything else is library code.