TOML Tools
Parse and convert TOML configuration files
Essential for Rust projects, modern configuration management, and settings files. TOML (Tom's Obvious, Minimal Language) combines the simplicity of INI files with the power of modern data types. Perfect for Cargo.toml, pyproject.toml, and application configurations that need to be human-readable and easy to edit.
What is TOML?
TOML (Tom's Obvious, Minimal Language) is a configuration file format created by Tom Preston-Werner (co-founder of GitHub) in 2013. It's designed to be easy to read due to obvious semantics, while still mapping unambiguously to a hash table.
History & Creation
TOML was created by Tom Preston-Werner on February 23, 2013, as a response to the complexity of YAML and the limitations of JSON for configuration files. Version 1.0.0 was released on January 12, 2021, after 8 years of refinement. The goal was to create a format that's minimal, obvious, and easy to parse.
Where TOML is Used
- Rust's Cargo.toml package management
- Python's pyproject.toml (PEP 518)
- Hugo static site generator config
- Netlify deployment configuration
- GitLab CI/CD configuration
- Application settings files
- Build tool configurations
Benefits Over Other Formats
- vs JSON: Comments support, more readable, trailing commas allowed, multi-line strings
- vs YAML: No ambiguity, simpler spec, no significant whitespace issues, easier to parse
- vs INI: Nested tables, arrays, proper data types, standardized format
- vs XML: Human-readable, minimal syntax, no closing tags, native data types
TOML Design Principles
Obvious
A person should be able to read a TOML file and understand what it means without learning the format.
Minimal
The format should be simple with a small specification that's easy to implement correctly.
Unambiguous
A TOML file should map to a single hash table without any ambiguity or edge cases.
Working with TOML Across Languages
🦀 Rust
- Built-in Cargo support
- toml crate for parsing
- serde integration
- cargo edit for CLI editing
🐍 Python
- tomllib (Python 3.11+)
- toml package
- poetry uses pyproject.toml
- pip supports PEP 518
📦 Node.js
- @iarna/toml package
- toml npm package
- smol-toml for small size
- Used in various tools
Convert TOML to JSON
Parse TOML files and convert them to JSON format
TOML Structure Examples & Best Practices
TOML's clarity and simplicity make it ideal for configuration files. Here are comprehensive examples demonstrating its features and best practices.
Basic TOML Structures
Simple Key-Value Pairs
Basic configuration with various data types
# This is a TOML document
title = "TOML Example"
[owner]
name = "Tom Preston-Werner"
dob = 1979-05-27T07:32:00-08:00 # Datetime with offset
[database]
server = "192.168.1.1"
ports = [ 8001, 8001, 8002 ]
connection_max = 5000
enabled = true
Tables and Nested Tables
Organizing configuration with table structures
[server]
host = "localhost"
port = 8080
[server.ssl]
enabled = true
cert = "/path/to/cert.pem"
key = "/path/to/key.pem"
# Inline table for compact representation
[server.limits]
request = { max_size = "10MB", timeout = 30 }
[clients.alpha]
ip = "10.0.0.1"
dc = "eqdc10"
[clients.beta]
ip = "10.0.0.2"
dc = "eqdc10"
Real-World Configuration Examples
Rust Cargo.toml
Package manifest for Rust projects
[package]
name = "my-app"
version = "0.1.0"
authors = ["Jane Doe <jane@example.com>"]
edition = "2021"
license = "MIT OR Apache-2.0"
description = "A blazingly fast web server"
repository = "https://github.com/example/my-app"
keywords = ["web", "server", "http"]
categories = ["web-programming::http-server"]
[dependencies]
tokio = { version = "1.35", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
axum = "0.7"
tracing = "0.1"
tracing-subscriber = "0.3"
[dev-dependencies]
cargo-watch = "8.4"
pretty_assertions = "1.4"
[profile.release]
opt-level = 3
lto = true
codegen-units = 1
[[bin]]
name = "server"
path = "src/main.rs"
Python pyproject.toml
Modern Python project configuration (PEP 518/621)
[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "awesome-package"
version = "2.0.0"
authors = [
{ name="Jane Smith", email="jane@example.com" },
]
description = "A small but awesome package"
readme = "README.md"
license = {file = "LICENSE"}
requires-python = ">=3.8"
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
]
dependencies = [
"httpx>=0.23",
"pydantic>=2.0",
"rich>=13.0",
]
[project.optional-dependencies]
dev = [
"pytest>=7.0",
"black>=23.0",
"mypy>=1.0",
"ruff>=0.1",
]
[project.urls]
"Homepage" = "https://github.com/example/awesome-package"
"Bug Tracker" = "https://github.com/example/awesome-package/issues"
[tool.black]
line-length = 88
target-version = ['py38', 'py39', 'py310', 'py311']
[tool.ruff]
line-length = 88
select = ["E", "F", "I", "N", "W"]
[tool.mypy]
python_version = "3.8"
warn_return_any = true
warn_unused_configs = true
Application Configuration
Complex application settings with multiple environments
# Application Configuration
title = "My Awesome App"
version = "1.0.0"
[app]
name = "awesome-app"
debug = false
log_level = "info"
# Arrays of tables for multiple instances
[[app.workers]]
id = "worker-1"
type = "cpu"
threads = 4
[[app.workers]]
id = "worker-2"
type = "io"
threads = 2
# Environment-specific settings
[environments.development]
database_url = "postgresql://localhost/myapp_dev"
redis_url = "redis://localhost:6379/0"
api_keys = [
{ name = "stripe", key = "sk_test_..." },
{ name = "sendgrid", key = "SG.test..." }
]
[environments.production]
database_url = "postgresql://prod-db.example.com/myapp"
redis_url = "redis://prod-redis.example.com:6379/0"
api_keys = [
{ name = "stripe", key = "sk_live_..." },
{ name = "sendgrid", key = "SG.prod..." }
]
[features]
enable_analytics = true
enable_notifications = true
experimental_features = ["dark_mode", "ai_assistant"]
[cache]
driver = "redis"
ttl = 3600
prefix = "myapp:"
[cache.options]
max_connections = 100
idle_timeout = 300
Advanced TOML Features
Multi-line Strings
Different ways to handle long text in TOML
# Basic strings
str1 = "I'm a string."
str2 = 'You can "quote" me.'
str3 = 'Name José
Loc SF.'
# Multi-line basic strings
str4 = """
Roses are red
Violets are blue"""
# Line ending backslash
str5 = """ The quick brown fox jumps over the lazy dog. """
# Literal strings (no escaping)
winpath = 'C:Users
odejs emplates'
winpath2 = '\ServerXadmin$system32'
quoted = 'Tom "Dubs" Preston-Werner'
# Multi-line literal strings
regex = '''I [dw]on't need d{2} apples'''
lines = '''
The first newline is
trimmed in raw strings.
All other whitespace
is preserved.
'''
Arrays and Mixed Types
Working with arrays of different types
# Arrays of integers
integers = [ 1, 2, 3 ]
# Arrays of different types (each array must be homogeneous)
colors = [ "red", "yellow", "green" ]
numbers = [ 0.1, 0.2, 0.5, 1, 2, 5 ]
# Nested arrays
nested_arrays = [ [ 1, 2 ], [3, 4, 5] ]
nested_mixed = [ [ 1, 2 ], ["a", "b", "c"] ]
# Arrays can span multiple lines
integers2 = [
1, 2, 3
]
integers3 = [
1,
2, # this is ok
]
Dates and Times
TOML's native datetime support
# Offset Date-Time
odt1 = 1979-05-27T07:32:00Z
odt2 = 1979-05-27T00:32:00-07:00
odt3 = 1979-05-27T00:32:00.999999-07:00
# Local Date-Time (no timezone)
ldt1 = 1979-05-27T07:32:00
ldt2 = 1979-05-27T00:32:00.999999
# Local Date
ld1 = 1979-05-27
# Local Time
lt1 = 07:32:00
lt2 = 00:32:00.999999
# Using in configuration
[deployment]
scheduled_at = 2024-01-20T15:00:00Z
maintenance_window = { start = 02:00:00, end = 04:00:00 }
backup_retention_days = 30
Static Site Generator Config
Hugo Configuration
Static site generator configuration example
baseURL = "https://example.com/"
languageCode = "en-us"
title = "My Personal Blog"
theme = "hugo-theme-stack"
paginate = 10
[params]
author = "John Doe"
description = "Personal blog about technology and life"
mainSections = ["posts", "docs"]
featuredImageField = "image"
rssFullContent = true
favicon = "/favicon.ico"
[params.footer]
since = 2023
customText = "CC BY-NC-SA 4.0"
[params.comments]
enabled = true
provider = "utterances"
[params.comments.utterances]
repo = "username/blog-comments"
issueTerm = "pathname"
label = "comment"
theme = "github-light"
[[menu.main]]
identifier = "home"
name = "Home"
url = "/"
weight = 1
[[menu.main]]
identifier = "posts"
name = "Posts"
url = "/posts/"
weight = 2
[[menu.main]]
identifier = "about"
name = "About"
url = "/about/"
weight = 3
[markup]
[markup.highlight]
anchorLineNos = false
codeFences = true
guessSyntax = false
hl_Lines = ""
lineAnchors = ""
lineNoStart = 1
lineNos = false
lineNumbersInTable = true
noClasses = true
style = "monokai"
tabWidth = 4
Best Practices and Common Patterns
File Organization
- Start with a clear title or description comment
- Group related settings in tables
- Use consistent naming (snake_case is common)
- Place metadata tables (like [package]) at the top
- Keep arrays of tables at the bottom
Common Pitfalls to Avoid
- Redefining tables: Can't define [a.b] after [a.b.c]
- Mixed types in arrays: Arrays must be homogeneous
- Floating keys: All key/value pairs must belong to a table
- Invalid key names: Bare keys can only contain A-Z, a-z, 0-9, _, -
- Ambiguous syntax: Use quotes for keys with special characters
TOML vs Other Formats
Configuration Format Comparison
Same configuration in TOML vs JSON vs YAML
# TOML - Clear and minimal
[app]
name = "myapp"
version = "1.0.0"
[database]
host = "localhost"
port = 5432
credentials = { user = "admin", pass = "secret" }
# JSON equivalent - No comments, more brackets
{
"app": {
"name": "myapp",
"version": "1.0.0"
},
"database": {
"host": "localhost",
"port": 5432,
"credentials": {
"user": "admin",
"pass": "secret"
}
}
}
# YAML equivalent - Whitespace sensitive
app:
name: myapp
version: 1.0.0
database:
host: localhost
port: 5432
credentials:
user: admin
pass: secret
Parsing TOML in Different Languages
Rust - Using the toml crate
Parsing TOML with serde in Rust
use serde::{Deserialize, Serialize};
use std::fs;
#[derive(Debug, Deserialize, Serialize)]
struct Config {
app: AppConfig,
database: DatabaseConfig,
}
#[derive(Debug, Deserialize, Serialize)]
struct AppConfig {
name: String,
version: String,
debug: Option<bool>,
}
#[derive(Debug, Deserialize, Serialize)]
struct DatabaseConfig {
host: String,
port: u16,
credentials: Credentials,
}
#[derive(Debug, Deserialize, Serialize)]
struct Credentials {
user: String,
pass: String,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Read and parse TOML file
let content = fs::read_to_string("config.toml")?;
let config: Config = toml::from_str(&content)?;
println!("App: {} v{}", config.app.name, config.app.version);
println!("Database: {}:{}", config.database.host, config.database.port);
// Convert back to TOML
let toml_string = toml::to_string_pretty(&config)?;
println!("
{}", toml_string);
Ok(())
}
Python - Using tomllib (3.11+)
Reading TOML files in modern Python
import tomllib # Python 3.11+
import json
from datetime import datetime
from pathlib import Path
# Read TOML file
with open("config.toml", "rb") as f:
config = tomllib.load(f)
# Access nested values
app_name = config["app"]["name"]
db_host = config["database"]["host"]
workers = config["app"]["workers"]
print(f"App: {app_name}")
print(f"Database: {db_host}")
print(f"Workers: {len(workers)}")
# For Python < 3.11, use the toml package
# pip install toml
import toml
# Read TOML
with open("config.toml", "r") as f:
config = toml.load(f)
# Write TOML
data = {
"project": {
"name": "example",
"version": "0.1.0",
"dependencies": ["requests", "pandas"]
}
}
with open("output.toml", "w") as f:
toml.dump(data, f)
# Pretty print with custom encoder
class DateTimeEncoder(toml.TomlEncoder):
def dump_value(self, v):
if isinstance(v, datetime):
return v.isoformat()
return super().dump_value(v)
JavaScript/Node.js - Using @iarna/toml
Working with TOML in Node.js applications
const fs = require('fs');
const TOML = require('@iarna/toml');
// Read and parse TOML file
const content = fs.readFileSync('config.toml', 'utf-8');
const config = TOML.parse(content);
console.log('App:', config.app.name);
console.log('Database:', config.database.host);
// Create TOML from JavaScript object
const data = {
package: {
name: 'my-app',
version: '1.0.0',
authors: ['John Doe <john@example.com>']
},
dependencies: {
express: '^4.18.0',
'body-parser': '^1.20.0'
},
scripts: {
start: 'node index.js',
test: 'jest'
}
};
// Convert to TOML string
const tomlString = TOML.stringify(data);
console.log(tomlString);
// Write to file
fs.writeFileSync('output.toml', tomlString);
// Handle dates
const configWithDates = {
deployment: {
scheduled: new Date('2024-01-20T15:00:00Z'),
completed: null
}
};
const tomlWithDates = TOML.stringify(configWithDates);
console.log(tomlWithDates);
TOML Validation and Schema
Validation Best Practices
Structure Validation
- Verify required tables exist
- Check data types match expectations
- Validate array homogeneity
- Ensure no duplicate keys
Content Validation
- Check value ranges (ports, percentages)
- Validate paths and URLs
- Verify enum values
- Confirm required fields present