iyi

Standard library, 0.18.0

std/ini

INI configuration text: [section] headers, key = value lines, comments opened by # or ;, and keys before the first header filed under the section named "".

import std/ini to use it. Source: src/std/ini.iyi at v0.18.0.

INI.parse reads a String or an IO into Hash(String, Hash(String, String)) and panics with a located sentence for what it refuses: a [ with no ] (unterminated section at line 3, column 6), anything after the ] including a trailing space (data after section at line 3, column 7), and a line that is neither header, comment nor assignment (expected declaration at line 4, column 4). The column counts characters, so a section named [café] is a section and not an error. A line of nothing but whitespace is skipped like an empty one. Repeating a header continues the earlier section; a value keeps every = after its first.

INI.build(ini, space) and INI.build(io, ini, space) write the same text: top-level keys first, then each section as [name], its keys and one blank line. space chooses key = value over key=value and is false unless asked, so parse(build(x)) and build(parse(text)) round trip for text written the compact way. What parse would read back as something else is refused rather than written: a line break in a name or a value, a ] in a section's name, a key that is empty, holds = or opens with #, ;, [ or U+FEFF (read as a byte order mark), and blanks at either end of a key or a value. INI has no escapes, so the one honest answer is the panic. The IO form takes the IO first, the way io.write reads, and calls only write.

INIstruct

std/iniiyi doc src/std/ini.iyi
pub struct INI
  def self.build(ini : Hash(String, Hash(String, String)), space : Bool = false) : String
  def self.build(io : IO, ini : Hash(String, Hash(String, String)), space : Bool = false) : Nil
  def initialize
  # Past a byte order mark, which Windows PowerShell 5.1's `Set-Content
  # -Encoding UTF8` and older Notepad write: it panicked "expected
  # declaration at line 1, column 8" at a `[section]` behind one, and the
  # first key of a file without a section kept it and never matched.
  def self.parse(string : String) : Hash(String, Hash(String, String))
  def self.parse(io : IO) : Hash(String, Hash(String, String))
end