Expand description
Derive macro for parsing KEY=VALUE formats.
This crate provides Kv for automatically implementing parsers
for structs from KEY=VALUE formatted input.
§Field Types
| Rust Type | Attribute | Behavior |
|---|---|---|
T | Required single value | |
Option<T> | Optional single value | |
Option<T> | #[kv(lenient)] | Optional single value; an unparseable value becomes None instead of erroring |
Vec<T> | Whitespace-separated values on single line | |
Option<Vec<T>> | Optional whitespace-separated values | |
Vec<T> | #[kv(multiline)] | Multiple lines collected into Vec |
Option<Vec<T>> | #[kv(multiline)] | Optional multiple lines |
HashMap<String, String> | #[kv(collect)] | Collects unhandled keys |
Vec<KvWarning> | #[kv(warnings)] | Collects parse failures from lenient fields |
§Container Attributes
#[kv(allow_unknown)]- Ignore unknown keys instead of returning an error#[kv(serde)]- Emitserde::Serialize/Deserializeimpls for the struct#[kv(crate = "path")]- Override the path used to reach thepkgsrc-kvruntime
§Field Attributes
#[kv(variable = "KEY")]- Use custom key name instead of uppercased field name#[kv(multiline)]- Collect multiple lines with the same key into aVec#[kv(collect)]- Collect all unhandled keys into thisHashMap<String, String>#[kv(lenient)]- For anOption<T>field, treat a value that fails to parse asNonerather than erroring (recorded in a#[kv(warnings)]field if one is present)#[kv(warnings)]- Collect parse failures fromlenientfields into thisVec<KvWarning>
§Duplicate Key Behavior
For non-multiline fields, duplicate keys overwrite the previous value.
For multiline fields, each occurrence appends to the Vec.
§Examples
These examples are written against the pkgsrc-kv crate, which
re-exports this macro alongside the runtime it targets. They are marked
ignore here only because this engine crate does not depend on the
runtime; they run as written once pkgsrc-kv is a dependency.
ⓘ
use indoc::indoc;
use pkgsrc_kv::{Kv, KvError};
#[derive(Kv)]
pub struct Package {
pkgname: String,
#[kv(variable = "SIZE_PKG")]
size: u64,
#[kv(multiline)]
description: Vec<String>,
homepage: Option<String>,
}
let input = indoc! {"
PKGNAME=foo-1.0
SIZE_PKG=1234
DESCRIPTION=A package that does
DESCRIPTION=many interesting things.
"};
let pkg = Package::parse(input)?;
assert_eq!(pkg.pkgname, "foo-1.0");
assert_eq!(pkg.size, 1234);
assert_eq!(pkg.description, vec!["A package that does", "many interesting things."]);
assert_eq!(pkg.homepage, None);
/* Missing required fields return an error. */
assert!(Package::parse("PKGNAME=bar-1.0\n").is_err());Use collect to collect unhandled keys into a HashMap, for example
when parsing +BUILD_INFO where arbitrary variables will be present:
ⓘ
use indoc::indoc;
use std::collections::HashMap;
use pkgsrc_kv::{Kv, KvError};
#[derive(Kv)]
pub struct BuildInfo {
build_host: Option<String>,
machine_arch: Option<String>,
#[kv(collect)]
vars: HashMap<String, String>,
}
let input = indoc! {"
BUILD_DATE=2025-01-15 10:30:00 +0000
BUILD_HOST=builder.example.com
MACHINE_ARCH=x86_64
PKGPATH=devel/example
"};
let info = BuildInfo::parse(input)?;
assert_eq!(info.build_host, Some("builder.example.com".to_string()));
assert_eq!(info.machine_arch, Some("x86_64".to_string()));
assert_eq!(info.vars.get("PKGPATH"), Some(&"devel/example".to_string()));
assert_eq!(info.vars.get("VARBASE"), None);Derive Macros§
- Kv
- Derive macro for parsing
KEY=VALUEformatted input.