Skip to main content

Crate deser_ini

Crate deser_ini 

Source
Expand description

INI files (and git’s config files) for deser.

use deser::{Deserialize, Serialize};

#[derive(Debug, Deserialize, Serialize)]
struct Config {
    name: String,
    server: Server,
}

#[derive(Debug, Deserialize, Serialize)]
struct Server {
    host: String,
    port: u16,
    #[deser(default)]
    tags: Vec<String>,
}

let config: Config = deser_ini::from_str("
name = shop

[server]
host = localhost  ; the host to bind to
port = 8080
tags = a
tags = b
").unwrap();
assert_eq!(config.name, "shop");
assert_eq!(config.server.port, 8080);
assert_eq!(config.server.tags, ["a", "b"]);

assert_eq!(
    deser_ini::to_string(&config).unwrap(),
    "name = shop\n\n[server]\nhost = localhost\nport = 8080\ntags = a\ntags = b\n"
);

§Dialects

INI files have no specification, every implementation reads them a little differently. The default (DeserializerConfig::new) reads what is common today:

  • Lines that start with ; or # are comments. After values, a ; that follows whitespace starts a comment and so does a # that follows whitespace after the value (key = value ; comment), so 1;2;3, A;B; and #ff0000 are values (see InlineComments).

  • = and : separate keys and values, whitespace around keys and values is removed.

  • Lines that are indented more than the line of their key continue its value (see Continuation), like in Python’s configparser:

    [options]
    install_requires =
        deser
        requests
  • A value that is quoted as a whole ("value" or 'value') is read without the quotes (see Quotes). This is how whitespace at the start or end of a value and comment characters are written.

  • A key without value (skip-name-resolve) is a key with a null value.

  • Keys can come before the first section.

These choices are based on a survey of INI files on GitHub: values are quoted for PHP, MySQL, Windows, Unreal and most other readers that are not Python, values with indented continuation lines are common in Python tools (tox.ini, setup.cfg, pylintrc) and PlatformIO, and keys are rarely indented more than the key before them. DeserializerConfig::python reads files of Python’s configparser (no inline comments and quotes) and DeserializerConfig::git reads git’s config files (Syntax::Git).

§Data Model

An INI file is a map of its sections and the keys before the first section, sections are maps of their keys:

INI filedeser
a = 1{"a": "1"}
[s] a = 1{"s": {"a": "1"}}
[s] a = 1 a = 2{"s": {"a": "1", "a": "2"}} (a repeated key)
[s] a = 1 [s] b = 2{"s": {"a": "1", "b": "2"}}
[s] a{"s": {"a": null}}
[s]{"s": {}}
[s "x"] a = 1 (git){"s": {"x": {"a": "1"}}}

Sections that are given more than once are merged. Keys can repeat (the maps are multimaps): fields and map values that are collections (like Vec<T>) collect the values of all occurrences of their key, also if other keys are between them. Types that expect a single value receive the last one unless the Context has another DuplicateKeys policy. A name that is a key and a section is an error.

Everything in an INI file is text, the type of a value is only known to the type it’s deserialized into. Keys and values are therefore passed on as lexical atoms which are parsed by the types they are delivered to: numbers parse them, strings take them as they are. Booleans accept true, yes, on and 1 and false, no, off and 0. Empty values are None for optionals of types that do not accept them (port = is None for an Option<u16> and Some("") for an Option<String>). Keys without value are null, the Flag adapter reads them as true:

use deser::adapters::Flag;

#[derive(deser::Deserialize)]
struct Mysqld {
    #[deser(as = Flag)]
    skip_name_resolve: bool,
    port: Option<u16>,
}

#[derive(deser::Deserialize)]
struct MyCnf {
    mysqld: Mysqld,
}

let cnf: MyCnf =
    deser_ini::from_str("[mysqld]\nskip_name_resolve\nport =\n").unwrap();
assert!(cnf.mysqld.skip_name_resolve);
assert_eq!(cnf.mysqld.port, None);

Lists in a single value (hosts = a, b) use the Separated adapter, values on continuation lines are separated by line breaks (Separated<'\n'>).

Interpolation (%(name)s, ${name}) and the DEFAULT section of configparser are not supported, they are text.

§Errors

Errors point at the line and column of the input, also errors of values that do not parse (like port = http for a u16). With deser-path they also have the path of the value (server.port), see Deserializer.

§Serialization

Values are written as INI files (see SerializerConfig): the value has to be a map (like a struct), its entries with maps as values are sections and the others are written before the first section. Sequences are written as repeated keys.

§Streams

INI files are read from a Read with from_reader and written to a Write with to_writer. The configurations also create readers and writers of deser::io (DeserializerConfig::reader and SerializerConfig::writer). An INI file holds a single value, the whole stream is read before it’s parsed.

§Features

  • io (enabled by default): reading and writing streams of the standard library, see streams.
  • speedups (enabled by default): has no effect yet, it exists so that all formats have it.

Structs§

Deserializer
Deserializes INI files.
DeserializerConfig
Configures how INI files are deserialized.
DeserializerConfigBuilder
Builds a DeserializerConfig.
Serializer
Serializes values into INI files.
SerializerConfig
Configures how values are serialized to INI files.
SerializerConfigBuilder
Builds a SerializerConfig.
StreamDeserializer
Reads an INI file from a stream (see deser::stream).

Enums§

Continuation
How values continue on the next lines.
InlineComments
Where comments start after values.
Quotes
How quoted values are read.
Syntax
The syntax of the files.

Functions§

from_reader
Deserializes an INI file from a reader.
from_slice
Deserializes a value from an INI file in a byte slice.
from_str
Deserializes a value from an INI file.
to_string
Serializes a value to an INI file.
to_writer
Serializes a value to a writer.