| Crates.io | korni |
| lib.rs | korni |
| version | 0.1.4 |
| created_at | 2025-12-15 19:46:43.988941+00 |
| updated_at | 2026-01-08 12:38:43.804536+00 |
| description | A blazingly fast, nearly stateless and nearly zero-copy parser for Ecolog Dotenv File Format (EDF) files |
| homepage | |
| repository | https://github.com/ph1losof/korni |
| max_upload_size | |
| id | 1986627 |
| size | 182,895 |
An ultra-fast, nearly stateless, and failure-tolerant parser for .env files, written in Rust.
Designed for high-performance tooling (LSPs, linters, formatters) and applications that need deep introspection into environment configuration files.
Cow strings) and SIMD-friendly slice iterators.[dependencies]
korni = "0.1.4"
use korni::{parse, ParseOptions};
fn main() {
let input = r#"
# Database Configuration
DB_HOST=localhost
DB_PORT=5432
"#;
// Simple parsing (fast mode)
let entries = parse(input);
for entry in entries {
if let Some(pair) = entry.as_pair() {
println!("Key: {}, Value: {}", pair.key, pair.value);
}
}
// Advanced parsing with positions and comments
let full_entries = korni::parse_with_options(input, ParseOptions::full());
}
Entry<'a>The main parsing result type representing a single line in an .env file:
pub enum Entry<'a> {
Comment(Span), // A comment line (# ...)
Pair(KeyValuePair<'a>), // A key-value pair (KEY=value)
Error(Error), // A parsing error
}
KeyValuePair<'a>Represents a parsed key-value pair with full introspection:
pub struct KeyValuePair<'a> {
pub key: Cow<'a, str>, // The key name
pub key_span: Option<Span>, // Span of the key
pub value: Cow<'a, str>, // The parsed value
pub value_span: Option<Span>, // Span of the value
pub quote: QuoteType, // Single, Double, or None
pub open_quote_pos: Option<Position>,
pub close_quote_pos: Option<Position>,
pub equals_pos: Option<Position>, // Position of the '='
pub is_exported: bool, // Whether 'export' keyword was used
pub is_comment: bool, // Whether this was in a comment (# KEY=value)
}
ParseOptionsConfigure parsing behavior:
pub struct ParseOptions {
pub include_comments: bool, // Parse & include comments in output
pub track_positions: bool, // Track line/col/offset positions
}
// Presets
ParseOptions::fast() // Default: no comments, no positions
ParseOptions::full() // Comments + positions enabled
Position and SpanFor precise location tracking:
pub struct Position {
pub line: usize, // 0-indexed line number
pub col: usize, // 0-indexed column number
pub offset: usize, // Byte offset from file start
}
pub struct Span {
pub start: Position,
pub end: Position,
}
QuoteTypeIndicates how a value was quoted:
pub enum QuoteType {
Single, // 'value'
Double, // "value"
None, // value (unquoted)
}
parse(input: &str) -> Vec<Entry>Fast parsing - returns key-value pairs only, no position tracking:
let entries = korni::parse("KEY=value");
parse_with_options(input: &str, options: ParseOptions) -> Vec<Entry>Configurable parsing with custom options:
let entries = korni::parse_with_options(input, ParseOptions::full());
The builder API provides a fluent interface for parsing from various sources:
use korni::Korni;
let env = Korni::from_str("KEY=value")
.preserve_comments()
.track_positions()
.parse()?;
println!("{}", env.get("KEY").unwrap());
let env = Korni::from_file(".env")
.preserve_comments()
.parse()?;
Searches current directory and ancestors for the file:
let env = Korni::find_file(".env")?
.parse()?;
let bytes = b"KEY=value";
let env = Korni::from_bytes(bytes).parse()?;
use std::io::Cursor;
let reader = Cursor::new("KEY=value");
let env = Korni::from_reader(reader).parse()?;
The Environment struct provides a HashMap-like interface:
use korni::Korni;
let env = Korni::from_str("
DB_HOST=localhost
DB_PORT=5432
").parse()?;
// Get a value
let host = env.get("DB_HOST"); // Option<&str>
let port = env.get_or("DB_PORT", "3306"); // &str with default
// Get full entry with metadata
if let Some(entry) = env.get_entry("DB_HOST") {
println!("Quoted: {:?}", entry.quote);
println!("Exported: {}", entry.is_exported);
}
// Iterate all pairs
for pair in env.iter() {
println!("{} = {}", pair.key, pair.value);
}
// Check for errors
if env.has_errors() {
for error in env.errors() {
eprintln!("Error: {}", error);
}
}
// Export to HashMap<String, String>
let map = env.to_map();
Stream entries one at a time for memory efficiency:
use korni::{Parser, EnvIterator};
let parser = Parser::new("KEY1=a\nKEY2=b");
let iter = EnvIterator::new(parser);
for entry in iter {
// Process entry
}
All parsing errors include byte offsets for precise error reporting:
pub enum Error {
InvalidUtf8 { offset: usize, reason: String },
UnclosedQuote { quote_type: &'static str, offset: usize },
InvalidKey { offset: usize, reason: String },
ForbiddenWhitespace { location: &'static str, offset: usize },
DoubleEquals { offset: usize },
InvalidBom { offset: usize },
Expected { offset: usize, expected: &'static str },
Generic { offset: usize, message: String },
Io(String),
}
// Get the byte offset of any error
let offset = error.offset();
[A-Za-z0-9_])=VALID_KEY=value
_also_valid=value
# Invalid: 123_KEY=value (starts with digit)
# Invalid: KEY = value (whitespace around =)
KEY=simple_value
MULTI=line1\
line2
KEY='raw value with $VAR not expanded'
\n, \r, \t, \\, \", \$KEY="line1\nline2"
ESCAPED="contains \"quotes\""
# are commentsKEY=value # comment (requires whitespace before #)# KEY=value) are parsed with is_comment: trueThe optional export prefix is supported:
export DATABASE_URL=postgres://localhost/db
\xEF\xBB\xBF) at file start is silently skippeddotenvydotenvy is the standard, battle-tested crate for loading environment variables in Rust applications. korni serves a different, more specialized purpose.
| Feature | dotenvy |
korni |
|---|---|---|
| Primary Goal | Load env vars into std::env |
Parse env files into structured data |
| Output | Modifies process environment | AST / Structured Iterators |
| Introspection | None (opaque loading) | Full (Spans, Line/Col, Offsets) |
| Comments | Ignored | Parsed & Preserved |
| Error Handling | Stops on first error | Failure-tolerant (collects all errors) |
Modifies std::env |
✅ Yes | ❌ No (Pure parsing) |
| Use Case | Application Configuration | Tooling (IDEs, Linters), Complex Configs |
dotenvycargo run to pick up your .env file.korni.env file (e.g. "where is DB_PORT defined?").This parser implements the EDF (Ecolog Dotenv File Format) 1.0.1 specification.
korni aims for EDF 1.0.1 compliance. Per the specification's compliance requirements:
A parser implementation claiming EDF 1.0.1 Compliance MUST adhere to ALL requirements specified in the specification. This is a strict, all-or-nothing compliance model.
This implementation varies in:
Cow strings, builders, and iteratorsis_comment flag for commented-out pairsMIT