package otoml

  1. Overview
  2. Docs
TOML parsing, manipulation, and pretty-printing library (1.0.0-compliant)

Install

Dune Dependency

Authors

Maintainers

Sources

0.9.1.tar.gz
md5=0369a4f615ad2bf86abd84467588f948
sha512=4986ea5890d9d970122850a42951f5a81002108f758199e8c44ad25e779a90ea7425b574523fdc82db50ef2ddd290eeee5cf017f2f5fad7da65e96f277ec6b64

Description

OTOML is a library for parsing, manipulating, and pretty-printing TOML files.

  • Fully 1.0.0-compliant.
  • No extra dependencies: default implementation uses native numbers and represents dates as strings.
  • Provides a functor for building alternative implementations: plug your own bignum and calendar libraries if required.
  • Informative parse error reporting.
  • Pretty-printer offers flexible indentation options.

Published: 02 Sep 2021

README

OTOML

A TOML parsing and manipulation library for OCaml.

In short:

  • TOML 1.0-compliant.

  • Transparent (no abstract types).

  • Easy access to deeply nested values.

  • Preserves original syntax variant (e.g. inline vs normal table) when parsing and printing.

  • Flexible pretty-printing options.

  • Does not force a calendar or bignum library dependency on you (you can plug your own into the functor).

Goals

The main goal for writing another TOML library is to provide a library for manipulating TOML files, not just reading them.

TOML is designed as a configuration file format. It's not just a serialization format for machines to talk to one another. A lot of time it's written, edited, and read by humans.

That is why TOML supports comments and multiple ways to write the same data.

Ideally, when a program reads a TOML file and writes it back, it should be able to echo it back and respect user's choice of using inline records vs sections (i.e. section = {...} vs [section]) and so on.

OTOML preserves that information and makes it available to the user.

It also offers a convenient interface for accessing and modifying values in deeply nested tables.

Example

(* Parse a TOML string. *)

utop # let t = Otoml.Parser.from_string "
[settings]
  [settings.basic]
    crash_randomly = true
" ;;
val t : Otoml.t =
  Otoml.TomlTable
   [("settings",
     Otoml.TomlTable
      [("basic", Otoml.TomlTable [("crash_randomly", Otoml.TomlBoolean true)])])]

(* Look up a deeply nested value with a known type. *)
utop # Otoml.find t Otoml.get_boolean ["settings"; "basic"; "crash_randomly"] ;;
- : bool = true

(* Update a deeply nested value. *)
utop # let t = Otoml.update t ["settings"; "basic"; "crash_randomly"] (Some (Otoml.TomlInteger 0)) ;;
val t : Otoml.t =
  Otoml.TomlTable
   [("settings",
     Otoml.TomlTable
      [("basic", Otoml.TomlTable [("crash_randomly", Otoml.TomlInteger 0)])])]

(* Look up a value and convert it to desired type (if possible). *)
utop # Otoml.find t (Otoml.get_boolean ~strict:false) ["settings"; "basic"; "crash_randomly"] ;;
- : bool = false

(* There's a pretty-printer, too! *)
utop # let t = Otoml.Parser.from_string "[foo] \n [foo.bar] \n baz = {quux = false} \n xyzzy = [ ] \n" |>
  Otoml.Printer.to_channel ~indent_width:4 ~indent_subtables:true ~collapse_tables:true stdout ;;

[foo.bar]
    baz = {quux = false}
    xyzzy = []

val t : unit = ()

Bring your own dependencies

The TOML specification requires support for datetime values and arbitrary large numbers. For a language that uses machine types and doesn't have datetime support in the standrad library, it means that implementations have to make a choice whether to be light on dependencies and easy to use or be standard-compliant.

OTOML solves that problem with OCaml functors.

The default implementation is provided for convenience: it represents integer and floating point numbers with OCaml's native int and float types, and stores date/time values as strings that you can parse with your favorite calendar library.

However, it's not hardcoded but built with a functor. This is how you could assemble the default implementation yourself.

module DefaultToml = Otoml.Base.Make (Otoml.Base.OCamlInteger) (Otoml.Base.OCamlFloat) (Otoml.Base.StringDate)

Thus you can replace any of the modules or all of them with your own. For example, this is how you can use zarith for a big integer implementation but keep native floats and simple string dates:

(* No signature ascription:
   `module BigInteger : Otoml.Base.TomlInteger` would make the type t abstract,
   which is inconvenient.
 *)
module BigInteger = struct
  type t = Z.t
  let of_string = Z.of_string
  let to_string = Z.to_string
  let of_boolean b = if b then Z.one else Z.zero
  let to_boolean n = (n <> Z.zero)
end

module MyToml = Otoml.Base.Make (BigInteger) (Otoml.Base.OCamlFloat) (Otoml.Base.StringDate)

Deviations from the TOML 1.0 specification

The default implementation is not completely compliant with the standard. These are the deviations:

The default implementation uses OCaml's native integer type, which is 63-bit on 64-bit architectures and 31-bit on 32-bit ones.

The default implementation does not interpret datetime values at all, only checks them for superficial validity and returns as strings.

For example, "1993-09-947" is considered invalid (as expected), but 1993-02-29 is valid despite the fact that 1993 wasn't a leap year.

Thus, actual precision depends on the library you use to parse those date strings or plug into the functor.

Dependencies (5)

  1. uutf >= "1.0.0"
  2. dune >= "2.0.0"
  3. menhirLib >= "20200525"
  4. menhir
  5. ocaml >= "4.08.0"

Dev Dependencies

None

Used by (3)

  1. dirsift >= "0.0.4"
  2. lab
  3. soupault >= "3.0.0" & < "3.2.1"

Conflicts

None