- D 100%
| demo | ||
| doc | ||
| files | ||
| src/dex/cf | ||
| .editorconfig | ||
| .gitignore | ||
| CODE_STYLE.md | ||
| dub.sdl | ||
| LICENSE | ||
| README.md | ||
| RULES.md | ||
dex-cf
A D language implementation of the CF (Configuration File) v1.0 specification.
CF is a modern, human-friendly configuration file format that combines the best features of JSON, YAML, TOML, and HCL while maintaining simplicity and unambiguous parsing.
Features
- Full CF v1.0 Compliance — Implements the complete CF specification
- Idiomatic D API — Natural
[]chaining, type-safe.as!Tconversion - All Value Types — Strings, integers, floats, booleans, null, dates, times, datetimes
- Multiple Number Formats — Decimal, hex (
0xFF), octal (0o77), binary (0b1010) - Flexible Strings — Double/single quoted, triple-quoted multiline, raw strings
- Environment Variables —
${VAR},${VAR:-default},${VAR:?error} - Include Directives — Modular configuration with
include "file.cf" - Comments — Hash (
#), double-slash (//), and block (/* */) styles - Roundtrip Preservation — Document model preserves formatting and comments
- Safe by Default — Null-safe access, nesting depth limits, include path validation
Installation
Add to your dub.sdl:
dependency "dex-cf" version="~>1.0.3"
Or dub.json:
"dependencies": {
"dex-cf": "~>1.0.3"
}
Quick Start
import dex.cf;
import std.stdio;
void main() {
auto doc = parseCFDocument(`
app {
name = "MyApp"
version = "1.0.0"
server {
host = "localhost"
port = 8080
}
features = ["auth", "api", "cache"]
}
`);
// Natural [] chaining
writeln(doc["app"]["name"].as!string); // "MyApp"
writeln(doc["app"]["server"]["port"].as!long); // 8080
// Array access
writeln(doc["app"]["features"][0].as!string); // "auth"
writeln(doc["app"]["features"].length); // 3
// Safe access - missing keys don't crash
writeln(doc["missing"]["key"].isValid); // false
writeln(doc["app"].getOr!long("timeout", 30)); // 30 (default)
}
This example is available as a runnable file: demo/quick_start.d
dub run --single demo/quick_start.d
Running the Full Demo
dub run --single demo/example.d
This comprehensive demo showcases all API features including nested access, arrays, temporal types, safe access patterns, and type checking.
API Overview
Parsing
// Parse from string
auto doc = parseCFDocument(source);
auto doc = parseCFDocument(source, "filename.cf");
// Parse from file
auto doc = parseCFDocumentFile("config.cf");
// Parse with environment variable substitution
auto doc = parseCFDocumentWithEnv(source);
auto doc = parseCFDocumentFileWithEnv("config.cf");
Accessing Values
// Chain [] operators naturally
doc["server"]["port"].as!long
// Type conversion
node.as!string // String value
node.as!long // Integer value
node.as!double // Float value
node.as!bool // Boolean value
// Type checking
node.isObject // true if object
node.isArray // true if array
node.isString // true if string
node.isInteger // true if integer
node.isNull // true if null or invalid reference
// Collection properties
node.length // Number of elements/members
node.keys // Array of object keys
node.hasKey("x") // Check if key exists
// Safe access with defaults
node.getOr!long("timeout", 30)
node.getOr!string("host", "localhost")
Iteration
// Iterate over object members
foreach (key, value; doc["settings"]) {
writefln("%s = %s", key, value.as!string);
}
// Iterate over array elements
foreach (item; doc["items"]) {
writeln(item.as!string);
}
Writing
// Serialize document back to CF format
string output = toCF(doc);
// With configuration
CfWriterConfig cfg;
cfg.minified = true;
cfg.indent = " ";
string output = toCF(doc, cfg);
Include Directives
CfParserConfig config;
config.enableIncludes = true;
config.fileReader = (string path) @safe {
return std.file.readText(path);
};
auto doc = parseCFDocument(source, "main.cf", config);
Environment Variables
CfParserConfig config;
config.enableEnvSubstitution = true;
config.envReader = (string name) @safe {
import core.stdc.stdlib : getenv;
auto result = getenv(name.toStringz);
return result ? fromStringz(result).idup : null;
};
// Or use the convenience function
auto doc = parseCFDocumentWithEnv(`
host = ${DB_HOST:-localhost}
port = ${DB_PORT:-5432}
`);
CF Format Examples
Basic Key-Value
name = "MyApp"
version = "1.0.0"
port = 8080
debug = false
Objects (HCL-style)
server {
host = "localhost"
port = 8080
}
# Or with equals sign
database = {
driver = "postgres"
host = "db.example.com"
}
Arrays
ports = [80, 443, 8080]
users = [
{ name = "Alice", role = "admin" }
{ name = "Bob", role = "user" }
]
All Number Formats
decimal = 1000
hex = 0xFF
octal = 0o755
binary = 0b1010
with_underscores = 1_000_000
float = 3.14159
scientific = 1.5e-10
infinity = inf
not_a_number = nan
String Varieties
double_quoted = "Hello, World!\n"
single_quoted = 'No escape processing'
raw_string = r"C:\path\to\file"
Triple-Quoted Strings (Multiline with Indentation Stripping)
Triple-quoted strings automatically strip leading indentation based on the position
of the closing """. This allows you to indent multiline strings naturally within
your configuration without including that indentation in the value:
# Indentation is stripped based on closing """ position
description = """
This is a multiline string.
The leading indentation is removed.
All lines align with the closing quotes.
"""
# Result: "This is a multiline string.\nThe leading indentation..."
# Extra indentation beyond the base is preserved (useful for code)
script = """
def hello():
print("Hello")
if True:
print("World")
"""
# Result: "def hello():\n print(\"Hello\")\n if True:\n print(\"World\")"
# Inline triple-quoted (no stripping needed)
inline = """no indentation here"""
Temporal Types
date = 2024-01-15
time = 10:30:00
datetime = 2024-01-15T10:30:00Z
with_offset = 2024-01-15T10:30:00+05:30
Environment Variables
# Basic substitution
host = ${DB_HOST}
# With default value
port = ${DB_PORT:-5432}
# Required (error if not set)
secret = ${API_KEY:?API key is required}
# In strings
url = "postgres://${DB_HOST}:${DB_PORT}/mydb"
# Escaped (literal ${)
pattern = "Use \${VAR} syntax"
Comments
# Hash comment
// Double-slash comment
/* Block
comment */
key = "value" # Inline comment
Separators
# All equivalent:
a = 1, b = 2, c = 3
a = 1; b = 2; c = 3
a = 1
b = 2
c = 3
Specification
dex-cf implements the CF v1.0 specification. For the complete specification, see:
- Specification Repository: https://codeberg.org/configuration-file/spec
- EBNF Grammar: files/cf.ebnf
Repository
- Source Code: https://codeberg.org/configuration-file/d
- Issue Tracker: https://codeberg.org/configuration-file/d/issues
License
BSD-3-Clause
See Also
- CF Specification — The official CF format specification
- Configuration File Examples — Complete example showcasing all CF features