Data formats

CSV

CSV in and out: rows as maps keyed by the header, with quotes, commas and line breaks in cells handled.

Version 1.0.0 · 4 functions · 1 crate

#csv#import#export#spreadsheets

Functions

What use csv.qwer gives a program, with the types Quillo sees. To take only some, or under other names: use csv_parse, csv_read_file from csv (Quillo 0.6).

csv_parse(text: str) → listor an error

CSV text as a list of maps, keyed by the first row. Cells are text; rows may be short.

csv_read_file(path: str) → listor an error

A CSV file as a list of maps, read straight from the disk: for exports too big to read as text first.

csv_rows(text: str, delimiter: str) → listor an error

CSV text as lists of cells, the first row too; delimiter is one character: ",", ";", "\t" or "|".

csv_write(rows: list, columns: list) → stror an error

Maps as CSV text: columns are the header and the order. null is an empty cell; lists and maps are written as JSON.

Get it

$ quillo install csv               # csv.qwer into this folder$ quillo install csv --example     # and its example, csv.qwe

Or with curl:

$ curl -O https://quillolang.org/plugins/files/csv.qwer$ curl -O https://quillolang.org/plugins/files/csv.qwe

Then, in your program, all of it or only what you call:

main.qwe
use csv.qweruse csv_parse, csv_read_file from csv      // Quillo 0.6: only these

It uses this crate, which cargo fetches the first time the plugin is built:

[dependencies]
csv = "1"

Example

csv.qwe
// A CSV export, and an import that reads it back.////   quillo run csv.qwe      then open http://localhost:8080/orders.csvuse "csv.qwer"let orders = [    { id: 1, customer: "Ada Lovelace", total: 120.5, note: "gift, wrap it" },    { id: 2, customer: "Grace \"Amazing\" Hopper", total: 99, note: null },]route GET /orders.csv = respond(200, csv_write(orders, ["id", "customer", "total", "note"])?, {    "content-type": "text/csv; charset=utf-8", "content-disposition": "attachment; filename=orders.csv" })route GET /import {    let rows = csv_parse(csv_write(orders, ["id", "customer", "total", "note"])?)?    rows | map((r) => { id: int(r.id), customer: r.customer, total: float(r.total) })}
$ quillo run csv.qwe$ curl 'localhost:8080/orders.csv'$ curl 'localhost:8080/import'

Source

csv.qwer
//! CSV in and out: rows as maps keyed by the header, quotes, commas and line breaks in cells handled.//! version: 1.0.0//!//! [dependencies]//! csv = "1"use serde_json::Value;use std::collections::HashMap;fn maps<R: std::io::Read>(mut r: csv::Reader<R>) -> Result<Vec<HashMap<String, String>>, String> {    let headers: Vec<String> = r.headers().map_err(|e| e.to_string())?.iter().map(|h| h.trim().to_string()).collect();    let mut out = Vec::new();    for rec in r.records() {        let rec = rec.map_err(|e| e.to_string())?;        out.push(headers.iter().cloned().zip(rec.iter().map(str::to_string)).collect());    }    Ok(out)}fn delim(d: &str) -> Result<u8, String> {    match d.as_bytes() {        [b] => Ok(*b),        _ => Err(format!("the delimiter is one character, not `{}`", d)),    }}/// CSV text as a list of maps, keyed by the first row. Cells are text; rows may be short.pub fn csv_parse(text: &str) -> Result<Vec<HashMap<String, String>>, String> {    maps(csv::ReaderBuilder::new().flexible(true).from_reader(text.as_bytes()))}/// A CSV file as a list of maps, read straight from the disk: for exports too big to read as text first.pub fn csv_read_file(path: &str) -> Result<Vec<HashMap<String, String>>, String> {    maps(csv::ReaderBuilder::new().flexible(true).from_path(path).map_err(|e| format!("{}: {}", path, e))?)}/// CSV text as lists of cells, the first row too; `delimiter` is one character: ",", ";", "\t" or "|".pub fn csv_rows(text: &str, delimiter: &str) -> Result<Vec<Vec<String>>, String> {    let mut r = csv::ReaderBuilder::new().has_headers(false).flexible(true).delimiter(delim(delimiter)?).from_reader(text.as_bytes());    r.records().map(|rec| rec.map(|r| r.iter().map(str::to_string).collect()).map_err(|e| e.to_string())).collect()}fn cell(v: Option<&Value>) -> String {    match v {        None | Some(Value::Null) => String::new(),        Some(Value::String(s)) => s.clone(),        Some(v) => v.to_string(),    }}/// Maps as CSV text: `columns` are the header and the order. `null` is an empty cell; lists and maps are written as JSON.pub fn csv_write(rows: Vec<std::collections::HashMap<String, serde_json::Value>>, columns: Vec<String>) -> Result<String, String> {    let mut w = csv::Writer::from_writer(Vec::new());    w.write_record(&columns).map_err(|e| e.to_string())?;    for row in &rows {        w.write_record(columns.iter().map(|c| cell(row.get(c)))).map_err(|e| e.to_string())?;    }    String::from_utf8(w.into_inner().map_err(|e| e.to_string())?).map_err(|e| e.to_string())}
Thread
88Useful? Like it, or add to its thread below.

Thread 0 posts

On CSV: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.