### TOML Array of Tables Example Source: https://github.com/lpil/tom/blob/main/_autodocs/toml-format-support.md Demonstrates the '[[key]]' syntax for creating an array of tables, where each element is a table. ```toml [[products]] name = "Widget" price = 9.99 [[products]] name = "Gadget" price = 19.99 ``` -------------------------------- ### Add tom Package Source: https://github.com/lpil/tom/blob/main/README.md Install the tom package using the gleam add command. ```sh gleam add tom ``` -------------------------------- ### TOML Integer Numbers Example Source: https://github.com/lpil/tom/blob/main/_autodocs/toml-format-support.md Provides examples of integer numbers in decimal, hexadecimal, octal, and binary formats, including positive, negative, and underscored values. ```toml decimal = 1000 negative = -42 positive = +99 hex = 0xDEADBEEF octal = 0o755 binary = 0b11010110 underscored = 1_000_000 ``` -------------------------------- ### TOML Dotted Keys Example Source: https://github.com/lpil/tom/blob/main/_autodocs/toml-format-support.md Illustrates the use of dotted keys as a shorthand for defining nested tables. ```toml a.b.c = 1 ``` -------------------------------- ### TOML Tables (Sections) Example Source: https://github.com/lpil/tom/blob/main/_autodocs/toml-format-support.md Shows the basic structure of TOML tables (sections) defined by headers in square brackets, containing key-value pairs. ```toml [section] key1 = "value1" key2 = 42 [section.subsection] nested = true ``` -------------------------------- ### Case-Sensitive Keys Example Source: https://github.com/lpil/tom/blob/main/_autodocs/toml-format-support.md Demonstrates that TOML keys are case-sensitive. 'Name' and 'name' are treated as distinct keys. ```toml Name = "Lucy" name = "Bob" # These are two different keys ``` -------------------------------- ### TOML Comments Example Source: https://github.com/lpil/tom/blob/main/_autodocs/toml-format-support.md Demonstrates the use of single-line and inline comments in TOML format. Comments start with '#' and are ignored by the parser. ```toml # This is a comment name = "Lucy" # Inline comment # Multi-line # comment ``` -------------------------------- ### Duplicate Key Rejection Example Source: https://github.com/lpil/tom/blob/main/_autodocs/internals.md Illustrates a case where duplicate keys are rejected, resulting in a 'KeyAlreadyInUse' error. This occurs when the values are not both arrays of tables. ```toml key = 1 key = 2 ``` -------------------------------- ### TOML Dates Example Source: https://github.com/lpil/tom/blob/main/_autodocs/toml-format-support.md Illustrates the format for local dates (YYYY-MM-DD) in TOML. Parsed as a Date tuple. ```toml birthday = 1990-05-15 release = 2020-01-01 ``` -------------------------------- ### Dotted Key Insertion Example Source: https://github.com/lpil/tom/blob/main/_autodocs/internals.md Demonstrates how dotted keys like 'a.b.c' are parsed and inserted into nested tables. The 'insert()' function recursively creates intermediate tables. ```gleam a.b.c = 1 // Becomes: // [a] // [a.b] // c = 1 ``` -------------------------------- ### TOML Times Example Source: https://github.com/lpil/tom/blob/main/_autodocs/toml-format-support.md Shows the format for local times (HH:MM:SS) in TOML, with optional nanoseconds. Parsed as a Time object. ```toml alarm = 07:30:00 precise = 14:00:00.123456789 ``` -------------------------------- ### Handle KeyAlreadyInUse Parse Error Source: https://github.com/lpil/tom/blob/main/_autodocs/errors.md Shows how to handle a `KeyAlreadyInUse` error, which is raised when a TOML key is defined more than once. The example prints the duplicate key path. ```gleam case tom.parse(toml_string) { Error(tom.KeyAlreadyInUse(key)) -> { io.println("Duplicate key in TOML document") io.println(" Key: " <> string.join(key, ".")) // Could locate the duplicate and suggest a fix } Ok(parsed) -> process_config(parsed) } ``` -------------------------------- ### TOML Multi-line Basic Strings Example Source: https://github.com/lpil/tom/blob/main/_autodocs/toml-format-support.md Illustrates multi-line basic strings using triple double quotes. Newlines within the string are preserved. ```toml multiline = """ The quick brown fox jumps over the lazy dog """ ``` -------------------------------- ### TOML Inline Tables Example Source: https://github.com/lpil/tom/blob/main/_autodocs/toml-format-support.md Demonstrates inline tables, which are compact key-value pairs enclosed in curly braces on a single line. Parsed as an InlineTable (equivalent to Table). ```toml point = { x = 1, y = 2 } person = { name = "Lucy", age = 30, active = true } ``` -------------------------------- ### TOML Booleans Example Source: https://github.com/lpil/tom/blob/main/_autodocs/toml-format-support.md Shows the representation of boolean values in TOML, using lowercase 'true' and 'false'. ```toml enabled = true disabled = false ``` -------------------------------- ### Parse and Access TOML Configuration Source: https://github.com/lpil/tom/blob/main/_autodocs/INDEX.md Demonstrates how to parse a TOML string and access its values using get functions. Ensure the TOML string is valid and keys exist. ```gleam import tom // Parse let config = " [server] host = \"localhost\" port = 8080 " let assert Ok(parsed) = tom.parse(config) // Access let host = tom.get_string(parsed, ["server", "host"]) let port = tom.get_int(parsed, ["server", "port"]) ``` -------------------------------- ### TOML Local Datetimes Example Source: https://github.com/lpil/tom/blob/main/_autodocs/toml-format-support.md Shows the format for local datetimes (without timezone offset) in TOML. These are ambiguous and cannot be converted to a Timestamp. ```toml local = 1979-05-27T07:32:00 ``` -------------------------------- ### TOML Literal Strings Example Source: https://github.com/lpil/tom/blob/main/_autodocs/toml-format-support.md Demonstrates literal strings enclosed in single quotes, which do not interpret escape sequences. ```toml path = 'C:\\Users\\john' regex = '[a-z]+' ``` -------------------------------- ### Example Error Handling for TOML Parsing Source: https://github.com/lpil/tom/blob/main/_autodocs/types.md Demonstrates how to handle potential parsing errors, including unexpected tokens and duplicate keys, using a case statement. ```gleam case tom.parse(toml_string) { Ok(parsed) -> io.println("Parsed successfully") Error(tom.Unexpected(got, expected)) -> io.println("Unexpected: got '" <> got <> "' expected '" <> expected <> "'") Error(tom.KeyAlreadyInUse(key)) -> io.println("Duplicate key: " <> string.join(key, ".")) } ``` -------------------------------- ### TOML Parsing Error Example Source: https://github.com/lpil/tom/blob/main/_autodocs/quick-start.md Illustrates the error format returned by Tom when encountering unexpected characters or duplicate keys during TOML parsing. ```gleam Error(tom.Unexpected("]", "{")) // Expected "{" but found "]" Error(tom.KeyAlreadyInUse(["server", "port"])) // The key "server.port" was defined twice ``` -------------------------------- ### TOML Float Numbers Example Source: https://github.com/lpil/tom/blob/main/_autodocs/toml-format-support.md Illustrates float numbers, including decimal, exponential notation, and underscored values. Follows IEEE 754 format. ```toml pi = 3.14159 negative_float = -0.01 exponent = 1e6 exponent_neg = 1e-6 exponent_plus = 1e+6 underscored = 1_000.123_456 ``` -------------------------------- ### TOML Arrays Example Source: https://github.com/lpil/tom/blob/main/_autodocs/toml-format-support.md Illustrates various ways to define arrays in TOML, including simple, mixed-type, nested, and multi-line arrays. Parsed as an Array of Toml values. ```toml simple = [1, 2, 3] mixed = ["apple", 1, true, 3.14] nested = [[1, 2], [3, 4]] multiline = [ "item 1", "item 2", "item 3" ] ``` -------------------------------- ### Parse TOML String Source: https://github.com/lpil/tom/blob/main/README.md Parse a TOML configuration string and retrieve values using get functions. Ensure the TOML string is correctly formatted. ```gleam import tom const config = " [person] name = \"Lucy\" is_cool = true " pub fn main() { // Parse a string of TOML let assert Ok(parsed) = tom.parse(config) // Now you can work with the data directly, or you can use the `get_*` // functions to retrieve values. tom.get_string(parsed, ["person", "name"]) // -> Ok(\"Lucy\") let is_cool = tom.get_bool(parsed, ["person", "is_cool"]) // -> Ok(True) } ``` -------------------------------- ### Access TOML Arrays Source: https://github.com/lpil/tom/blob/main/_autodocs/usage-patterns.md Shows how to retrieve TOML arrays, which are parsed into Gleam lists of `Toml` values. Elements must be converted to their expected types, for example, strings. ```gleam let assert Ok(parsed) = tom.parse(" fruits = [\"apple\", \"banana\", \"cherry\"] ") case tom.get_array(parsed, ["fruits"]) { Ok(items) -> { // items is List(Toml) let strings = list.filter_map(items, fn(item) { case item { tom.String(s) -> Ok(s) _ -> Error(Nil) } }) io.println(string.join(strings, ", ")) // -> "apple, banana, cherry" } Error(_) -> Nil } ``` -------------------------------- ### TOML Value Access Error Example Source: https://github.com/lpil/tom/blob/main/_autodocs/quick-start.md Shows the error formats for missing keys or type mismatches when accessing values within a parsed TOML structure. ```gleam Error(tom.NotFound(["database", "url"])) // The key "database.url" does not exist Error(tom.WrongType(["port"], "Int", "String")) // Expected Int at "port" but found String ``` -------------------------------- ### TOML Basic Strings Example Source: https://github.com/lpil/tom/blob/main/_autodocs/toml-format-support.md Shows basic strings enclosed in double quotes, including supported escape sequences like \n, \r, \t, \b, \f, \e, \", and \\. ```toml name = "John Doe" path = "C:\\Users\\john" escaped = "Line 1\nLine 2" ``` -------------------------------- ### Handle NotFound GetError Source: https://github.com/lpil/tom/blob/main/_autodocs/errors.md Illustrates how to manage a `NotFound` error when attempting to retrieve a value from a parsed TOML document. This error occurs if the specified key path does not exist. The example provides a default value. ```gleam case tom.get_string(parsed, ["database", "url"]) { Error(tom.NotFound(key)) -> { io.println("Configuration key not found: " <> string.join(key, ".")) io.println("Using default value") "sqlite://default.db" } Ok(url) -> url } ``` -------------------------------- ### Handle TOML GetError Source: https://github.com/lpil/tom/blob/main/_autodocs/types.md Example of handling GetError when retrieving an integer from a parsed TOML document. It demonstrates matching on NotFound and WrongType errors. ```gleam case tom.get_int(parsed, ["config", "timeout"]) { Ok(value) -> io.println("Timeout: " <> int.to_string(value) <> "ms") Error(tom.NotFound(key)) -> io.println("Key not found: " <> string.join(key, ".")) Error(tom.WrongType(key, expected, got)) -> io.println("Expected " <> expected <> " at " <> string.join(key, ".") <> " but got " <> got) } ``` -------------------------------- ### TOML Offset-Aware Datetimes Example Source: https://github.com/lpil/tom/blob/main/_autodocs/toml-format-support.md Demonstrates offset-aware datetime formats in TOML, including UTC ('Z') and explicit timezone offsets (+HH:MM or -HH:MM). Parsed into DateTime with Offset. ```toml utc = 1979-05-27T07:32:00Z with_offset = 1979-05-27T07:32:00+05:30 negative_offset = 1979-05-27T07:32:00-08:00 ``` -------------------------------- ### Handle Wrong Type Errors When Getting TOML Values Source: https://github.com/lpil/tom/blob/main/_autodocs/00-START-HERE.md Demonstrates how to handle specific errors, such as 'NotFound' or 'WrongType', when attempting to retrieve integer values from TOML configuration. ```gleam case tom.get_int(config, ["port"]) { Ok(port) -> use_port(port) Error(tom.NotFound(_)) -> io.println("Port not configured") Error(tom.WrongType(key, expected, got)) -> io.println("Port should be " <> expected <> " not " <> got) } ``` -------------------------------- ### Get Value or Default Source: https://github.com/lpil/tom/blob/main/_autodocs/README.md Safely retrieve a value from parsed TOML data, returning a default if the key is not found or the getter fails. Useful for configuration where some settings are optional. ```gleam fn get_or_default(parsed, key, default, getter) { case getter(parsed, key) { Ok(value) -> value Error(_) -> default } } let host = get_or_default(parsed, ["server", "host"], "localhost", tom.get_string) ``` -------------------------------- ### TOML Multi-line Literal Strings Example Source: https://github.com/lpil/tom/blob/main/_autodocs/toml-format-support.md Shows multi-line literal strings using triple single quotes. Newlines are preserved without escape sequence interpretation. ```toml regex = ''' [a-z]+ [0-9]{3} ''' ``` -------------------------------- ### get Source: https://github.com/lpil/tom/blob/main/_autodocs/module-index.md Retrieves a value from a parsed TOML dictionary using a key path. Returns a Result containing the Toml value or a GetError if the path is invalid or the key is not found. ```APIDOC ## Function get ### Description Get value of any type using key path. Returns a Result containing the Toml value or a GetError. ### Signature `Dict(String, Toml), List(String) -> Result(Toml, GetError)` ``` -------------------------------- ### Get a value from TOML using a key path Source: https://github.com/lpil/tom/blob/main/_autodocs/api-reference.md Use `tom.get` to retrieve a specific value from a parsed TOML dictionary using a list of strings as a key path. This function supports nested access through tables and returns a Result, indicating success with the `Toml` value or failure with a `GetError` if the key is not found or the path leads to a non-table value unexpectedly. ```gleam let assert Ok(parsed) = tom.parse("a.b.c = 1") tom.get(parsed, ["a", "b", "c"]) // -> Ok(Int(1)) ``` -------------------------------- ### Typical TOM Usage Flow Source: https://github.com/lpil/tom/blob/main/_autodocs/module-index.md Demonstrates the standard steps for using the TOM library: importing the module, parsing a TOML string, accessing values, and optionally using decoders with Dynamic types. ```gleam import tom let toml_string = "..." let parsed = tom.parse(toml_string) // -> Result(Dict(String, Toml), ParseError) let value = tom.get_string(parsed, ["section", "key"]) // -> Result(String, GetError) let dynamic_parsed = tom.parse_to_dynamic(toml_string) // -> Result(Dynamic, ParseError) // import gleam/dynamic/decode // decode.run(dynamic_parsed, my_decoder) // -> Result(MyType, DecodeError) ``` -------------------------------- ### Configuration Loading with Default Values Source: https://github.com/lpil/tom/blob/main/_autodocs/usage-patterns.md Demonstrates a helper function to retrieve configuration values with defaults, falling back to a specified default if the key is not found or the getter fails. ```gleam fn get_with_default(parsed, key, default, getter) { case getter(parsed, key) { Ok(value) -> value Error(_) -> default } } let host = get_with_default(parsed, ["server", "host"], "localhost", tom.get_string) let port = get_with_default(parsed, ["server", "port"], 8080, tom.get_int) let debug = get_with_default(parsed, ["debug"], False, tom.get_bool) ``` -------------------------------- ### Load TOML Configuration from File Source: https://github.com/lpil/tom/blob/main/_autodocs/quick-start.md Reads a file, parses its content as TOML, and returns the parsed TOML structure or an error message. Requires `simplifile`. ```gleam import simplifile pub fn load_config(path: String) -> Result(Dict(String, tom.Toml), String) { use content <- result.try( simplifile.read(path) |> result.map_error(fn(_) { "Cannot read file" }) ) tom.parse(content) |> result.map_error(fn(_) { "Invalid TOML" }) } ``` -------------------------------- ### Tom Parser Pipeline Overview Source: https://github.com/lpil/tom/blob/main/_autodocs/internals.md Illustrates the sequential steps involved in parsing TOML input, from initial string input to a structured dictionary output. ```text Input String ↓ to_graphemes (split into characters) ↓ drop_comments (remove comment lines) ↓ skip_whitespace (remove leading whitespace) ↓ parse_table (parse key-value pairs) ↓ parse_tables (parse remaining [section] blocks) ↓ reverse_arrays_of_tables (fix ordering) ↓ Dict(String, Toml) ``` -------------------------------- ### Load Application Configuration from TOML Source: https://github.com/lpil/tom/blob/main/_autodocs/usage-patterns.md Loads and parses an application configuration from a TOML file. Handles various data types and provides default values for optional fields like server timeout. Requires explicit error handling for missing or invalid fields. ```gleam import tom import simplifile import gleam/dynamic/decode as decode pub type AppConfig { AppConfig( name: String, version: String, server: ServerConfig, database: DatabaseConfig, features: List(String), ) } pub type ServerConfig { ServerConfig(host: String, port: Int, timeout_ms: Int) } pub type DatabaseConfig { DatabaseConfig(url: String, pool_size: Int, ssl: Bool) } pub fn load_config(path: String) -> Result(AppConfig, String) { use content <- result.try( simplifile.read(path) |> result.map_error(fn(_) { "Cannot read file" }) ) use parsed <- result.try( tom.parse(content) |> result.map_error(fn(_) { "Invalid TOML" }) ) use name <- result.try( tom.get_string(parsed, ["name"]) |> result.map_error(fn(_) { "Missing name" }) ) use version <- result.try( tom.get_string(parsed, ["version"]) |> result.map_error(fn(_) { "Missing version" }) ) use server_host <- result.try( tom.get_string(parsed, ["server", "host"]) |> result.map_error(fn(_) { "Missing server.host" }) ) use server_port <- result.try( tom.get_int(parsed, ["server", "port"]) |> result.map_error(fn(_) { "Missing server.port" }) ) let server_timeout = case tom.get_int(parsed, ["server", "timeout_ms"]) { Ok(t) -> t Error(_) -> 30_000 // 30 second default } use db_url <- result.try( tom.get_string(parsed, ["database", "url"]) |> result.map_error(fn(_) { "Missing database.url" }) ) use db_pool <- result.try( tom.get_int(parsed, ["database", "pool_size"]) |> result.map_error(fn(_) { "Missing database.pool_size" }) ) use db_ssl <- result.try( tom.get_bool(parsed, ["database", "ssl"]) |> result.map_error(fn(_) { "Missing database.ssl" }) ) use features <- result.try( tom.get_array(parsed, ["features"]) |> result.try_map(fn(items) { list.try_map(items, tom.as_string) |> result.map_error(fn(_) { "Invalid features list" }) }) ) Ok(AppConfig( name: name, version: version, server: ServerConfig( host: server_host, port: server_port, timeout_ms: server_timeout, ), database: DatabaseConfig( url: db_url, pool_size: db_pool, ssl: db_ssl, ), features: features, )) } ``` -------------------------------- ### Load TOML Configuration from File Source: https://github.com/lpil/tom/blob/main/_autodocs/README.md Loads TOML configuration from a file path, handling potential file reading and TOML parsing errors. Returns a `Result` with a dictionary or an error string. ```gleam import tom import simplifile pub fn load_config(path: String) -> Result(Dict(String, tom.Toml), String) { use content <- result.try( simplifile.read(path) |> result.map_error(fn(_) { "Cannot read file" }) ) tom.parse(content) |> result.map_error(fn(_) { "Invalid TOML" }) } ``` -------------------------------- ### Basic TOML Decoding Source: https://github.com/lpil/tom/blob/main/_autodocs/usage-patterns.md Demonstrates how to parse a TOML string into a dynamic value and then define a decoder to extract specific fields like host, port, and debug status. ```gleam import gleam/dynamic/decode as decode let config = " host = "localhost" port = 8080 debug = true " let assert Ok(dynamic) = tom.parse_to_dynamic(config) let decoder = { use host <- decode.field("host", decode.string) use port <- decode.field("port", decode.int) use debug <- decode.field("debug", decode.bool) decode.success(#(host, port, debug)) } case decode.run(dynamic, decoder) { Ok(#(host, port, debug)) -> io.println("Config loaded") Error(_) -> io.println("Invalid config structure") } ``` -------------------------------- ### Get TOML Values with Defaults Source: https://github.com/lpil/tom/blob/main/_autodocs/quick-start.md A helper function to retrieve a TOML value using a specified getter, returning a default value if an error occurs. ```gleam fn get_or_default(parsed, key, default, getter) { case getter(parsed, key) { Ok(value) -> value Error(_) -> default } } let host = get_or_default(parsed, ["server", "host"], "localhost", tom.get_string) let port = get_or_default(parsed, ["server", "port"], 8080, tom.get_int) ``` -------------------------------- ### Error Recovery Pattern: Validation Source: https://github.com/lpil/tom/blob/main/_autodocs/errors.md A function that attempts to get an integer and then validates it using a provided validator function. Returns an error if validation fails. ```gleam fn get_validated(parsed, key, validator) { case tom.get_int(parsed, key) { Ok(value) if validator(value) -> Ok(value) Ok(value) -> Error("Invalid value: " <> int.to_string(value)) Error(e) -> Error("Missing or wrong type: " <> error_to_string(e)) } } let port = get_validated(parsed, ["server", "port"], fn(p) { p > 0 && p < 65536 }) ``` -------------------------------- ### Process Array of Tables Source: https://github.com/lpil/tom/blob/main/_autodocs/README.md Iterates over an array of tables found at a specific key in the parsed TOML. Each table is then processed individually, for example, to extract server configurations. ```gleam case tom.get_array(parsed, ["servers"]) { Ok(servers) -> { let configs = list.filter_map(servers, tom.as_table) list.each(configs, fn(server) { let name = tom.get_string(server, ["name"]) // Process each server }) } Error(_) -> Nil } ``` -------------------------------- ### Load TOML from File Source: https://github.com/lpil/tom/blob/main/_autodocs/usage-patterns.md Reads a TOML configuration from a file path. It handles file reading errors and TOML parsing errors, returning a Result. ```gleam import tom import simplifile pub fn load_config(path: String) -> Result(Dict(String, tom.Toml), String) { case simplifile.read(path) { Ok(content) -> tom.parse(content) |> result.map_error(fn(_) { "Parse error" }) Error(_) -> Error("Could not read file") } } ``` -------------------------------- ### Loading Configuration File with Tom Source: https://github.com/lpil/tom/blob/main/_autodocs/usage-patterns.md Provides a function to load a TOML configuration file from a given path, parse its content, and extract specific configuration values into a typed struct. Handles potential errors during file reading, parsing, and field extraction. ```gleam import tom import simplifile pub type ServerConfig { ServerConfig(host: String, port: Int, ssl_enabled: Bool) } pub fn load_config(path: String) -> Result(ServerConfig, String) { use content <- result.try( simplifile.read(path) |> result.map_error(fn(_) { "Cannot read file" }) ) use parsed <- result.try( tom.parse(content) |> result.map_error(fn(_) { "Invalid TOML" }) ) use host <- result.try( tom.get_string(parsed, ["server", "host"]) |> result.map_error(fn(_) { "Missing server.host" }) ) use port <- result.try( tom.get_int(parsed, ["server", "port"]) |> result.map_error(fn(_) { "Missing or invalid server.port" }) ) use ssl <- result.try( tom.get_bool(parsed, ["server", "ssl_enabled"]) |> result.map_error(fn(_) { "Missing server.ssl_enabled" }) ) Ok(ServerConfig(host, port, ssl)) } ``` -------------------------------- ### Chaining Parsing Operations with 'do' Source: https://github.com/lpil/tom/blob/main/_autodocs/internals.md Shows how the 'do' function is used to sequentially parse keys, expect an equals sign, and then parse a value, managing the token stream. ```gleam use key, input <- do(parse_key(input, [])) use input <- expect(input, "=") use value, input <- do(parse_value(input)) Ok(#(#(key, value), input)) ``` -------------------------------- ### Parse TOML File and Handle Errors Source: https://github.com/lpil/tom/blob/main/_autodocs/00-START-HERE.md Shows how to read a TOML file using simplifile and parse its content with Tom, including error handling for file and parse errors. ```gleam import tom import simplifile case simplifile.read("config.toml") { Ok(content) -> case tom.parse(content) { Ok(config) -> process_config(config) Error(_) -> handle_parse_error() } Error(_) -> handle_file_error() } ``` -------------------------------- ### Use Decoders with Parsed TOML Dynamic Data Source: https://github.com/lpil/tom/blob/main/_autodocs/00-START-HERE.md Shows how to parse TOML into dynamic data and then use Gleam's dynamic decoding library to extract and validate specific fields like name and port. ```gleam import gleam/dynamic/decode as decode let assert Ok(dynamic) = tom.parse_to_dynamic(toml_string) let decoder = { use name <- decode.field("name", decode.string) use port <- decode.field("port", decode.int) decode.success(#(name, port)) } let assert Ok(#(name, port)) = decode.run(dynamic, decoder) ``` -------------------------------- ### Load and Parse TOML Configuration File Source: https://github.com/lpil/tom/blob/main/_autodocs/quick-start.md Reads a TOML file from the filesystem, parses its content, and extracts an integer value for a specific key. Handles potential errors during file reading and parsing. ```gleam import tom import simplifile pub fn main() { case simplifile.read("config.toml") { Ok(content) -> case tom.parse(content) { Ok(parsed) -> { let port = tom.get_int(parsed, ["server", "port"]) case port { Ok(p) -> io.println("Port: " <> int.to_string(p)) Error(_) -> io.println("Port not found") } } Error(_) -> io.println("Invalid TOML") } Error(_) -> io.println("Cannot read file") } } ``` -------------------------------- ### TOML Special Float Values Example Source: https://github.com/lpil/tom/blob/main/_autodocs/toml-format-support.md Demonstrates the special float values: infinity (inf, -inf) and Not-a-Number (nan, -nan). These are parsed into specific variants in the Toml type. ```toml max_value = inf min_value = -inf undefined = nan ``` -------------------------------- ### Import Toml Types and Functions Source: https://github.com/lpil/tom/blob/main/_autodocs/README.md Shows the necessary import statement to use the Toml library and its associated types. This includes importing the main module and specific types for error handling and value representation. ```gleam import tom import tom.{Toml, ParseError, GetError, Number, Sign, Offset} ``` -------------------------------- ### Type Relationships for TOML Access Source: https://github.com/lpil/tom/blob/main/_autodocs/README.md Illustrates how to parse TOML into a dictionary and access values by type using various getter functions. It also shows how to convert individual TOML values using accessor functions. ```gleam // Parse returns a dictionary of Toml values parse(String) -> Result(Dict(String, Toml), ParseError) // Access values by type get_string(Dict(String, Toml), List(String)) -> Result(String, GetError) get_int(Dict(String, Toml), List(String)) -> Result(Int, GetError) // Or convert a single Toml value as_string(Toml) -> Result(String, GetError) as_int(Toml) -> Result(Int, GetError) // Special handling for numbers get_number(Dict(...)) -> Result(Number, GetError) // Number = NumberInt(Int) | NumberFloat(Float) // | NumberNan(Sign) | NumberInfinity(Sign) ``` -------------------------------- ### Convert parsed TOML dictionary to dynamic type Source: https://github.com/lpil/tom/blob/main/_autodocs/api-reference.md Use `tom.to_dynamic` to convert an already parsed TOML dictionary into a `Dynamic` value. This is beneficial when you need to leverage `gleam/dynamic/decode` for complex data extraction from a parsed TOML structure. The example demonstrates converting a parsed TOML and then decoding fields. ```gleam let config = "name = \"Lucy\"\npoints = 5" let assert Ok(parsed) = tom.parse(config) let dynamic = tom.to_dynamic(parsed) let decoder = { use name <- decode.field("name", decode.string) use points <- decode.field("points", decode.int) decode.success(#(name, points)) } decode.run(dynamic, decoder) // -> Ok(#("Lucy", 5)) ``` -------------------------------- ### Decoding Special Types (Date, Time, Number) Source: https://github.com/lpil/tom/blob/main/_autodocs/usage-patterns.md Shows how to use pre-built decoders for special types such as dates, times of day, and numbers (integers and floats) from a TOML string. ```gleam let config = " created = 2020-01-01 time = 12:30:00 count = 42 pi = 3.14159 " let assert Ok(dynamic) = tom.parse_to_dynamic(config) let decoder = { use created <- decode.field("created", tom.date_decoder()) use time <- decode.field("time", tom.time_of_day_decoder()) use count <- decode.field("count", tom.number_decoder()) use pi <- decode.field("pi", tom.number_decoder()) decode.success(#(created, time, count, pi)) } let assert Ok(#(date, time, tom.NumberInt(count), tom.NumberFloat(pi))) = decode.run(dynamic, decoder) ``` -------------------------------- ### Array Parsing with Reverse Accumulation Source: https://github.com/lpil/tom/blob/main/_autodocs/internals.md Demonstrates the recursive parsing of array elements, accumulating them in reverse order for efficient list prepending. ```gleam fn parse_array(input: Tokens, elements: List(Toml)) -> Parsed(Toml) { case input { ["]", ..] -> Ok(#(Array(list.reverse(elements)), input)) _ -> { use element, input <- do(parse_value(input)) // Recursively parse next element } } } ``` -------------------------------- ### Decoding Nested TOML Structures Source: https://github.com/lpil/tom/blob/main/_autodocs/usage-patterns.md Illustrates how to decode nested TOML structures by defining decoders for nested objects, such as server and SSL configurations. ```gleam let config = " [server] host = "localhost" port = 8080 [server.ssl] enabled = true cert = "/path/to/cert" " let assert Ok(dynamic) = tom.parse_to_dynamic(config) let ssl_decoder = { use enabled <- decode.field("enabled", decode.bool) use cert <- decode.field("cert", decode.string) decode.success(#(enabled, cert)) } let server_decoder = { use host <- decode.field("host", decode.string) use port <- decode.field("port", decode.int) use ssl <- decode.field("ssl", ssl_decoder) decode.success(#(host, port, ssl)) } let assert Ok(#(host, port, #(ssl_enabled, cert_path))) = decode.field("server", server_decoder) |> decode.run(dynamic) ``` -------------------------------- ### Parse TOML to Dynamic and Use Decoders Source: https://github.com/lpil/tom/blob/main/_autodocs/INDEX.md Shows how to parse TOML into a dynamic type and then use Gleam's decoder framework to extract specific data structures. This is useful for complex or nested data. ```gleam import gleam/dynamic/decode as decode let assert Ok(dynamic) = tom.parse_to_dynamic(config) let decoder = { use host <- decode.field("host", decode.string) use port <- decode.field("port", decode.int) decode.success(#(host, port)) } let assert Ok(#(h, p)) = decode.run(dynamic, decoder) ``` -------------------------------- ### Access Arrays of TOML Tables Source: https://github.com/lpil/tom/blob/main/_autodocs/usage-patterns.md Illustrates how to parse and work with TOML arrays of tables, which use the `[[key]]` syntax. Each item in the array is converted to a table (dictionary) for further processing. ```gleam let assert Ok(parsed) = tom.parse(" [[products]] name = \"Widget\" price = 9.99 [[products]] name = \"Gadget\" price = 19.99 ") case tom.get_array(parsed, ["products"]) { Ok(product_items) -> { // Each item is converted to Table(dict) let products = list.filter_map(product_items, tom.as_table) // Now work with the list of dictionaries let prices = list.filter_map(products, fn(product) { tom.get_float(product, ["price"]) }) } Error(_) -> Nil } ``` -------------------------------- ### Flexible TOML Value Access Patterns Source: https://github.com/lpil/tom/blob/main/_autodocs/README.md Illustrates different methods for accessing TOML values: dictionary-style access, direct value conversion for lists, and using decoders for complex structures. ```gleam // 1. Dictionary access (simple) tom.get_string(parsed, ["section", "key"]) // 2. Direct value conversion (for lists) list.filter_map(items, tom.as_string) // 3. Decoders (for complex structures) decode.run(dynamic, my_decoder) ``` -------------------------------- ### Key Functions Source: https://github.com/lpil/tom/blob/main/_autodocs/DOCUMENTATION-MANIFEST.txt This section highlights the most important functions for interacting with TOML data, including parsing and retrieving specific values. ```APIDOC ## Key Functions This section details the most frequently used functions in the Tom library. ### `parse/1` Parses a TOML string into a Toml value. - **Method**: `parse` - **Arity**: 1 - **Source**: line 462 ### `get_string/2` Retrieves a string value from a Toml structure. - **Method**: `get_string` - **Arity**: 2 - **Source**: line 261 ### `get_int/2` Retrieves an integer value from a Toml structure. - **Method**: `get_int` - **Arity**: 2 - **Source**: line 195 ### `get_table/2` Retrieves a table (map) from a Toml structure. - **Method**: `get_table` - **Arity**: 2 - **Source**: line 395 ### `get_array/2` Retrieves an array (list) from a Toml structure. - **Method**: `get_array` - **Arity**: 2 - **Source**: line 372 ``` -------------------------------- ### Parse TOML to Dynamic and Decode Server Config Source: https://github.com/lpil/tom/blob/main/_autodocs/types.md Parses a TOML string into a dynamic value and then decodes a specific 'server' configuration using a custom decoder. This showcases dynamic integration for complex data structures. ```gleam import tom import gleam/dynamic/decode as decode let assert Ok(config) = tom.parse_to_dynamic(" \ [server] host = \"localhost\" port = 8080 enabled = true ") let server_decoder = { use host <- decode.field("host", decode.string) use port <- decode.field("port", decode.int) use enabled <- decode.field("enabled", decode.bool) decode.success(#(host, port, enabled)) } let assert Ok(server) = decode.field("server", server_decoder) |> decode.run(config) // server = #("localhost", 8080, True) ``` -------------------------------- ### Pattern 1: Result Chain for Parsing Config Source: https://github.com/lpil/tom/blob/main/_autodocs/quick-start.md Chains multiple Result operations to parse TOML content and extract required configuration values, returning a custom Config type or an error string. ```gleam pub fn parse_config(content: String) -> Result(Config, String) { use parsed <- result.try(tom.parse(content) |> result.map_error(err_msg)) use host <- result.try(tom.get_string(parsed, ["host"]) |> result.map_error(err_msg)) use port <- result.try(tom.get_int(parsed, ["port"]) |> result.map_error(err_msg)) Ok(Config(host, port)) } ``` -------------------------------- ### Define and Use Custom Decoders Source: https://github.com/lpil/tom/blob/main/_autodocs/README.md Creates a custom decoder for Gleam's dynamic decoding system to extract specific fields like 'host' and 'port' from TOML data. This is useful for structured configuration. ```gleam import gleam/dynamic/decode as decode let decoder = { use host <- decode.field("host", decode.string) use port <- decode.field("port", decode.int) decode.success(#(host, port)) } let assert Ok(#(host, port)) = decode.run(dynamic, decoder) ``` -------------------------------- ### Parse TOML Configuration Source: https://github.com/lpil/tom/blob/main/_autodocs/README.md Parse a TOML configuration string using the `tom.parse` function. Handles success and failure cases. ```gleam import tom const config = " name = \"MyApp\" version = \"1.0.0\" " pub fn main() { case tom.parse(config) { Ok(parsed) -> io.println("Parsed!") Error(_) -> io.println("Parse failed") } } ``` -------------------------------- ### Access Simple and Complex TOML Values Source: https://github.com/lpil/tom/blob/main/_autodocs/00-START-HERE.md Illustrates how to retrieve various types of values (simple and complex) from a parsed TOML configuration using Tom's accessor functions. ```gleam let assert Ok(config) = tom.parse(toml_string) // Simple values tom.get_string(config, ["key"]) tom.get_int(config, ["section", "number"]) tom.get_bool(config, ["section", "subsection", "flag"]) // Complex values tom.get_array(config, ["items"]) tom.get_table(config, ["database"]) ``` -------------------------------- ### Handling ParseError and GetError Source: https://github.com/lpil/tom/blob/main/_autodocs/README.md Demonstrates how to handle specific TOML parsing and access errors using a case statement. This allows for user-friendly error messages for issues like syntax errors or duplicate keys. ```gleam case tom.parse(config) { Ok(parsed) -> process_config(parsed) Error(tom.Unexpected(got, expected)) -> io.println("Syntax error: expected " <> expected <> " but got " <> got) Error(tom.KeyAlreadyInUse(key)) -> io.println("Duplicate key: " <> string.join(key, ".")) } ``` -------------------------------- ### Parse TOML String Source: https://github.com/lpil/tom/blob/main/_autodocs/usage-patterns.md Use the `parse()` function to convert a TOML string into a dictionary. This is the most basic way to load TOML configuration. ```gleam import tom import gleam/dict const config = " name = \"MyApp\" version = \"1.0.0\" debug = true " pub fn main() { case tom.parse(config) { Ok(parsed) -> { // parsed is Dict(String, Toml) dict.size(parsed) |> io.println } Error(e) -> io.println("Failed to parse TOML") } } ``` -------------------------------- ### Parse TOML to Dynamic and Decode Source: https://github.com/lpil/tom/blob/main/_autodocs/quick-start.md Parses a TOML string into a dynamic structure and then decodes it into a specific Gleam tuple using decoders. Requires `gleam/dynamic`. ```gleam import gleam/dynamic/decode as decode const config = "host = \"localhost\" port = 8080" let assert Ok(dynamic) = tom.parse_to_dynamic(config) let decoder = { use host <- decode.field("host", decode.string) use port <- decode.field("port", decode.int) decode.success(#(host, port)) } let assert Ok(#(host, port)) = decode.run(dynamic, decoder) ``` -------------------------------- ### Access TOML Tables Source: https://github.com/lpil/tom/blob/main/_autodocs/usage-patterns.md Demonstrates how to extract TOML tables, which are represented as dictionaries in Gleam. The `get_table` function returns a `Result` containing the dictionary. ```gleam let assert Ok(parsed) = tom.parse(" [person] name = \"Alice\" age = 25 ") case tom.get_table(parsed, ["person"]) { Ok(person_dict) -> { // person_dict is Dict(String, Toml) dict.get(person_dict, "name") } Error(_) -> Nil } ``` -------------------------------- ### Parse TOML String and Access Values Source: https://github.com/lpil/tom/blob/main/_autodocs/00-START-HERE.md Demonstrates parsing a TOML string and accessing values type-safely using Tom functions. ```gleam import tom // Parse a TOML string let assert Ok(config) = tom.parse("\n name = \"MyApp\"\n port = 8080\n") // Access values type-safely let name = tom.get_string(config, ["name"]) // Ok("MyApp") let port = tom.get_int(config, ["port"]) // Ok(8080) ``` -------------------------------- ### Handle TOML Access Errors Source: https://github.com/lpil/tom/blob/main/_autodocs/quick-start.md Demonstrates handling specific errors when accessing TOML values, such as Key Not Found or Wrong Type, providing default values. ```gleam case tom.get_int(parsed, ["server", "port"]) { Ok(port) -> io.println("Port: " <> int.to_string(port)) Error(tom.NotFound(key)) -> { io.println("Key not found: " <> string.join(key, ".")) 8080 // Use default } Error(tom.WrongType(key, expected, got)) -> { io.println("Expected " <> expected <> " but got " <> got) 8080 } } ``` -------------------------------- ### Error Recovery Pattern: Nested Fallback Source: https://github.com/lpil/tom/blob/main/_autodocs/errors.md Retrieves a value using a primary key, and if that fails, attempts to retrieve it using a fallback key. ```gleam fn get_with_fallback(parsed, primary_key, fallback_key, getter) { case getter(parsed, primary_key) { Ok(value) -> Ok(value) Error(_) -> getter(parsed, fallback_key) } } let timeout = get_with_fallback( parsed, ["server", "timeout"], ["default", "timeout"], tom.get_int ) ``` -------------------------------- ### Handling Infinity and NaN in TOML Source: https://github.com/lpil/tom/blob/main/_autodocs/usage-patterns.md Explains how Tom handles special numeric values like infinity (`inf`, `-inf`) and Not-a-Number (`nan`, `-nan`) from TOML, which are not directly supported in Gleam, by wrapping them in specific types. ```gleam let assert Ok(parsed) = tom.parse("max_value = inf min_value = -inf error = nan ") case tom.get_number(parsed, ["max_value"]) { Ok(tom.NumberInfinity(tom.Positive)) -> io.println("Positive infinity") Ok(tom.NumberInfinity(tom.Negative)) -> io.println("Negative infinity") _ -> Nil } case tom.get_number(parsed, ["error"]) { Ok(tom.NumberNan(tom.Positive)) -> io.println("NaN") _ -> Nil } ``` -------------------------------- ### Whitespace Handling in TOML Source: https://github.com/lpil/tom/blob/main/_autodocs/toml-format-support.md Illustrates how leading and trailing whitespace around TOML keys and values is ignored, while whitespace within strings is preserved. ```toml key = "value" # Leading/trailing whitespace ignored key2 = "value" # Spacing before value ignored ``` -------------------------------- ### Erlang FFI for NaN and Infinity Source: https://github.com/lpil/tom/blob/main/_autodocs/internals.md Shows how Erlang Foreign Function Interface (FFI) is used to create 'Dynamic' representations of 'NaN' and 'Infinity', which are not directly supported in Gleam. ```erlang -module(tom_ffi). nan_to_dynamic(Sign) -> dynamic:from_term(nan). infinity_to_dynamic(Sign) -> dynamic:from_term(infinity). ``` -------------------------------- ### Conversion to Dynamic Values Source: https://github.com/lpil/tom/blob/main/_autodocs/internals.md Demonstrates the conversion of parsed TOML data into Gleam's 'Dynamic' type using 'table_to_dynamic' and 'value_to_dynamic'. This facilitates integration with 'gleam/dynamic/decode'. ```gleam table_to_dynamic(dict) // Dict → Dynamic properties value_to_dynamic(toml) // Toml → Dynamic ``` -------------------------------- ### Array of Tables Merging Source: https://github.com/lpil/tom/blob/main/_autodocs/internals.md Shows how duplicate keys within arrays of tables are handled. If both values are arrays of tables, they are merged. Otherwise, a 'KeyAlreadyInUse' error is returned. ```toml [[arr]] x = 1 [[arr]] y = 2 ``` -------------------------------- ### Monadic Parsing 'do' Function Source: https://github.com/lpil/tom/blob/main/_autodocs/internals.md Demonstrates the custom monadic parsing pattern used in Tom, which chains parsing operations and threads remaining tokens through the process. ```gleam fn do( result: Result(#(a, Tokens), ParseError), next: fn(a, Tokens) -> Result(b, ParseError), ) -> Result(b, ParseError) ``` -------------------------------- ### Type-Safe Integer Retrieval Source: https://github.com/lpil/tom/blob/main/_autodocs/00-START-HERE.md Demonstrates retrieving an integer value from configuration using dictionary-style access. The function returns a Result type, explicitly handling potential errors during retrieval. ```gleam tom.get_int(config, ["port"]) // -> Result(Int, GetError) ``` -------------------------------- ### Handling WrongType Error Source: https://github.com/lpil/tom/blob/main/_autodocs/errors.md Demonstrates how to catch a WrongType error when retrieving an integer and provide a default value. ```gleam case tom.get_int(parsed, ["server", "port"]) { Error(tom.WrongType(key, expected, got)) -> { io.println("Type mismatch at " <> string.join(key, ".")) io.println(" Expected: " <> expected) io.println(" Got: " <> got) 8080 // default value } Ok(port) -> port } ``` -------------------------------- ### Pattern 2: Try-Map with List for Extracting Strings Source: https://github.com/lpil/tom/blob/main/_autodocs/quick-start.md Extracts a list of strings from a TOML array using `result.try_map` and `list.try_map` for safe, chained error handling. ```gleam // Extract list of strings from TOML array use items <- tom.get_array(parsed, ["features"]) |> result.try_map(fn(items) { list.try_map(items, tom.as_string) }) ``` -------------------------------- ### Handle TOML Access Errors Source: https://github.com/lpil/tom/blob/main/_autodocs/INDEX.md Illustrates how to handle potential errors when accessing TOML values, such as NotFound or WrongType. Use case statements to manage different error variants. ```gleam case tom.get_int(parsed, ["server", "timeout"]) { Ok(timeout) -> io.println(int.to_string(timeout)) Error(tom.NotFound(_)) -> io.println("Using default timeout") Error(tom.WrongType(key, expected, got)) -> io.println("Type error at " <> string.join(key, ".")) } ``` -------------------------------- ### Parse TOML Dates Source: https://github.com/lpil/tom/blob/main/_autodocs/usage-patterns.md Demonstrates parsing TOML date values into Gleam date types. The `get_date` function is used for this purpose, returning a `Result`. ```gleam let assert Ok(parsed) = tom.parse(" birthday = 1990-05-15 release_date = 2020-01-01 ") case tom.get_date(parsed, ["birthday"]) { Ok(date) -> io.println("Birthday: " <> date.year <> "-" <> date.month) Error(_) -> Nil } ``` -------------------------------- ### Scenario: WrongType for Integer Source: https://github.com/lpil/tom/blob/main/_autodocs/errors.md Illustrates a WrongType error when expecting an integer but receiving a float. ```gleam // Scenario 1: Type mismatch let assert Ok(parsed) = tom.parse("timeout = 30.5") case tom.get_int(parsed, ["timeout"]) { Error(tom.WrongType(["timeout"], "Int", "Float")) -> io.println("timeout should be an integer, not a float") _ -> Nil } ```