Data formats

INI

INI files read and written, sections and keys: php.ini, setup.cfg, .gitconfig and the settings of older software.

Version 1.0.0 · 3 functions · 1 crate

#config#settings#import#export

Functions

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

ini_parse(text: str) → anyor an error

INI text as a map: each [section] a map of its keys, keys before the first section at the top. Values are text, with quotes around them taken off (name = "Ada"); ; and # start comments; a key given twice keeps the last. Sections named a.b stay one key, "a.b".

ini_read_file(path: str) → anyor an error

An INI file read as ini_parse reads text.

ini_write(value: any) → stror an error

A map as INI text: values that are maps become [sections] (in the map's order of keys, sorted), the others keys at the top. Numbers and booleans are written as text, null as nothing; a line break in a value is an error, since INI has none.

Get it

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

Or with curl:

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

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

main.qwe
use ini.qweruse ini_parse, ini_read_file from ini      // Quillo 0.6: only these

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

[dependencies]
rust-ini = "0.21"

Example

ini.qwe
// Settings imported from an old app's .ini file, and written back with a change.////   quillo run ini.qwe      then open http://localhost:8080/settingsuse "ini.qwer"route GET /settings {    let s = ini_read_file("ini_sample.ini")?    { app: s.app_name, debug: s.debug == "true", db: `postgres://${s.database.host}:${int(s.database.port)?}/${s.database.name}`, from: s.mail.from, uploads: s.paths.uploads }}route GET /updated.ini {    let s = ini_read_file("ini_sample.ini")?    respond(200, ini_write({ ...s, debug: true, database: { ...s.database, host: "db2.internal" } })?, { "content-type": "text/plain; charset=utf-8" })}
$ quillo run ini.qwe$ curl 'localhost:8080/settings'$ curl 'localhost:8080/updated.ini'

Source

ini.qwer
//! INI files read and written (`[section]` and `key = value`): php.ini, setup.cfg, .gitconfig, desktop files and the settings of older Windows software.//! version: 1.0.0//!//! [dependencies]//! rust-ini = "0.21"use ini::{EscapePolicy, Ini, ParseOption, WriteOption};use serde_json::{Map, Value};/// Files up to 10 MB are read.const MAX: usize = 10 * 1024 * 1024;fn to_map(ini: &Ini) -> Value {    let mut out = Map::new();    for (sec, props) in ini.iter() {        match sec {            None => {                for (k, v) in props.iter() {                    out.insert(k.to_string(), Value::String(v.to_string()));                }            }            Some(name) => {                let entry = out.entry(name.to_string()).or_insert_with(|| Value::Object(Map::new()));                if !entry.is_object() {                    *entry = Value::Object(Map::new());                }                if let Value::Object(m) = entry {                    for (k, v) in props.iter() {                        m.insert(k.to_string(), Value::String(v.to_string()));                    }                }            }        }    }    Value::Object(out)}/// INI text as a map: each `[section]` a map of its keys, keys before the first section at the top. Values are text, with/// quotes around them taken off (`name = "Ada"`); `;` and `#` start comments; a key given twice keeps the last. Sections named/// `a.b` stay one key, `"a.b"`.pub fn ini_parse(text: &str) -> Result<Value, String> {    if text.len() > MAX {        return Err("INI files up to 10 MB".into());    }    let ini = Ini::load_from_str_opt(text, ParseOption { enabled_quote: true, enabled_escape: false, ..ParseOption::default() }).map_err(|e| format!("not INI: {}", e))?;    Ok(to_map(&ini))}/// An INI file read as ini_parse reads text.pub fn ini_read_file(path: &str) -> Result<Value, String> {    let size = std::fs::metadata(path).map_err(|e| format!("{}: {}", path, e))?.len();    if size as usize > MAX {        return Err(format!("{}: INI files up to 10 MB", path));    }    ini_parse(&std::fs::read_to_string(path).map_err(|e| format!("{}: {}", path, e))?)}fn text(v: &Value) -> String {    match v {        Value::String(s) => s.clone(),        Value::Null => String::new(),        other => other.to_string(),    }}/// A map as INI text: values that are maps become `[sections]` (in the map's order of keys, sorted), the others keys at the top./// Numbers and booleans are written as text, null as nothing; a line break in a value is an error, since INI has none.pub fn ini_write(value: serde_json::Value) -> Result<String, String> {    let Value::Object(m) = value else { return Err("INI is written from a map".into()) };    let mut ini = Ini::new();    let check = |k: &str, v: &str| if v.contains('\n') || k.contains('\n') || k.contains('=') { Err(format!("`{}`: INI keys and values are one line, without `=` in a key", k)) } else { Ok(()) };    for (k, v) in &m {        if !v.is_object() {            let t = text(v);            check(k, &t)?;            ini.with_general_section().set(k.as_str(), t);        }    }    for (name, v) in &m {        if let Value::Object(props) = v {            for (k, x) in props {                let t = text(x);                check(k, &t)?;                ini.with_section(Some(name.as_str())).set(k.as_str(), t);            }            if props.is_empty() {                ini.with_section(Some(name.as_str()));            }        }    }    let mut out = Vec::new();    ini.write_to_opt(&mut out, WriteOption { escape_policy: EscapePolicy::Nothing, kv_separator: " = ", ..WriteOption::default() }).map_err(|e| e.to_string())?;    String::from_utf8(out).map_err(|e| e.to_string())}
Thread
87Useful? Like it, or add to its thread below.

Thread 0 posts

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

No posts yet. The first one opens the thread.