SagaSaga
SagaCsv

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.toml declares the Rust crate (crate-type = ["cdylib"], rustler = "0.36").
  • native/src/lib.rs defines two #[rustler::nif] functions and registers them with rustler::init!("saga_csv_nif"). The name must match the Erlang module that loads the NIF.
  • lib/saga_csv_nif.erl is the loader. lib/saga_csv_bridge.erl is the Erlang-side thin wrapper that translates saga calls into NIF calls.
  • rebar.config runs make in native/ as a pre-hook so rebar3 compile builds the Rust library before compiling the bridge module.
  • lib/SagaCsv.saga calls the bridge via @external("erlang", "saga_csv_bridge", "...").

That is the whole chain. Everything else is library code.