gullwing
Runtime formatting and parsing with Python's Format Specification Mini-Language.
gullwing brings Python-style string formatting and parsing to Rust, enabling you to:
- Format values at runtime using format strings (like Python's
format()andstr.format()) - Parse structured data from strings (like Python's
parsepackage)
Features
- 🎯 Runtime Format Strings: Use format strings determined at runtime, not just compile-time
- 🔄 Bidirectional: Both format values to strings AND parse strings to values
- 📐 Full Format Spec Support: Alignment, padding, precision, type specifiers, and more
- 🦀 Pure Rust: Zero unsafe code, comprehensive error handling
- 🚀 Fast: Regex-based parsing, efficient formatting
- 📚 Well Documented: Extensive examples and API documentation
Installation
Add this to your Cargo.toml:
[]
= "1.0.0-rc.1"
Quick Start
Formatting
use ;
use HashMap;
let formatter = new?;
let mut values = new;
values.insert;
values.insert;
let result = formatter.format_map?;
assert_eq!;
Parsing
use Parser;
let parser = new?;
let result = parser.parse?.unwrap;
assert_eq!;
assert_eq!;
Format Specification Mini-Language
gullwing implements Python's format specification syntax:
[[fill]align][sign][z][#][0][width][grouping][.precision][type]
Examples
use ;
// Right-align in 10 characters
let f = new?;
// "> hello"
// Zero-pad integers to 5 digits
let f = new?;
// "00042"
// Format float with 2 decimal places
let f = new?;
// "3.14"
// Hexadecimal with 0x prefix
let f = new?;
// "0xff"
// Thousands separator
let f = new?;
// "1,000,000"
// Center-align with custom fill
let f = new?;
// "*******hello********"
Supported Type Specifiers
| Type | Description | Example Input | Parsed As |
|---|---|---|---|
s |
String (default) | "hello" |
String |
d |
Decimal integer | "42", "-17" |
i64 |
b |
Binary integer | "1010", "0b1010" |
i64 |
o |
Octal integer | "755", "0o755" |
i64 |
x, X |
Hexadecimal | "ff", "0xFF" |
i64 |
f, F |
Fixed-point float | "3.14", "-2.5" |
f64 |
e, E |
Scientific notation | "1.5e10" |
f64 |
g, G |
General float | "3.14", "1e5" |
f64 |
% |
Percentage | "50%" |
f64 (0.50) |
c |
Character | "A" |
char |
Use Cases
Log File Transformation
use ;
let parser = new?;
let formatter = new?;
let line = "2024-01-15T10:30:00 INFO Server started";
if let Some = parser.parse?
CSV/Data Reformatting
use ;
let parser = new?;
let formatter = new?;
let csv_line = "5,Alice,95.7";
if let Some = parser.parse?
The Shuffle Tool
gullwing includes a shuffle example that demonstrates text transformation capabilities:
# Build the example
# Use it to transform log files
| \
# Output: INFO: Hello World
# Extract and reformat CSV data
| \
# Output: Alice (30) - Engineer
Advanced Features
Search and FindAll
use Parser;
let parser = new?;
// Search finds the first match
let result = parser.search?.unwrap;
assert_eq!;
// FindAll finds all matches
let results: = parser.findall?.collect;
assert_eq!;
Functional Formatting
use ;
let formatter = new?;
let result = formatter.format_fn?;
assert_eq!;
Positional Arguments
use ;
let formatter = new?;
let values = vec!;
let result = formatter.format_positional?;
assert_eq!;
Comparison with Python
Formatting
Python:
Rust with gullwing:
let formatter = new?;
let mut values = new;
values.insert;
values.insert;
formatter.format_map?
Parsing
Python:
=
Rust with gullwing:
let parser = new?;
let result = parser.parse?.unwrap;
println!;
Architecture
gullwing is built with:
- Hand-written format spec parser for fast, accurate parsing of format specifications
- Regex-based text parsing for efficient pattern matching (inspired by Python's parse package)
- Type-safe value system with
Valueenum for runtime value handling - Zero-copy operations where possible for performance
Performance
Benchmarks run on an Intel x86_64 processor using criterion.rs. All operations are measured at runtime (no compile-time optimizations):
Format Specification Parsing
| Operation | Time |
|---|---|
Simple spec (>10) |
~26 ns |
Complex spec (0<+#20,.2f) |
~63 ns |
All features (*>+z#030_,.6e) |
~128 ns |
Formatting Operations
| Operation | Time |
|---|---|
| Simple string | ~104 ns |
| Integer | ~151 ns |
| Integer with grouping | ~337 ns |
| Float with precision | ~224 ns |
| Aligned/padded string | ~175 ns |
| Hex with prefix | ~160 ns |
| Complex pattern (3 fields) | ~895 ns |
| Multiple fields (10) | ~1.22 µs |
Parsing Operations
| Operation | Time |
|---|---|
| Simple pattern | ~38.5 µs |
| Integer | ~63.6 µs |
| Float | ~151.7 µs |
| Integer with grouping | ~32.0 µs |
| Multiple fields (3) | ~197.6 µs |
| Complex pattern | ~279 µs |
| Search (first match) | ~489 ns |
| FindAll (4 matches) | ~2.74 µs |
| Pattern creation | ~61.5 µs |
Key Takeaways:
- Format specification parsing is extremely fast (sub-microsecond)
- Formatting operations are comparable to
std::fmt(nanoseconds) - Parsing includes regex compilation and matching, measured in microseconds
- Pattern creation is one-time cost; reuse Parser instances for best performance
Run benchmarks yourself:
Limitations
- No support for nested field access (e.g.,
{obj.field}) - Locale-aware formatting (
ntype) falls back to default formatting - Some edge cases in floating-point formatting may differ slightly from Python
Contributing
Contributions are welcome! Please feel free to submit issues or pull requests.
License
Licensed under the Apache License, Version 2.0. See LICENSE for details.
Acknowledgments
- Inspired by Python's Format Specification Mini-Language
- Parse functionality inspired by Richard Jones'
parsepackage - Created to enable ergonomic data transformation tools in Rust
See Also
- Python Format Specification
- Python parse package
- Implementation Plan - detailed design decisions and architecture