Kv
Reactive values addressed by path, with no struct declared. For a key set that is not known at compile time, or where a schema is more ceremony than the job is worth.
let kv = store.kv();
kv.set("theme", &"dark".to_string())?;let theme = kv.get::<String>("theme")?;
let ui = kv.namespace("ui");ui.set("width", &800u32)?;let under_ui = ui.keys()?;
kv.remove("theme")?;A name is one level
Section titled “A name is one level”Every name here is a single level, and namespace is what nests. So
kv.set("ui.width", ..) writes one key called ui.width - a name that happens
to hold a dot - while kv.namespace("ui").set("width", ..) writes width
inside ui.
The dot in a name is escaped on disk, so the two are different places and stay different. Getting this the wrong way round is the mistake to watch for: a dotted string looks like a path and is not one.
keys() lists everything under the handle it is called on, at every level below
it and sorted. Narrowing a listing is what namespace is for; the root handle
lists the whole store.
kv.set("theme", &"dark".to_string())?;kv.namespace("ui").set("width", &800u32)?;kv.namespace("ui").namespace("panel").set("left", &true)?;
let from_ui: Vec<String> = kv .namespace("ui") .keys()? .iter() .map(|key| key.to_string()) .collect();
assert_eq!(from_ui, ["ui.panel.left", "ui.width"]);The paths that come back are whole, so a width inside ui is listed as
ui.width, and theme at the root is not listed at all from a ui handle.
Cells and maps
Section titled “Cells and maps”let width = kv.namespace("ui").cell("width", 800u32)?;let flags = kv.map::<String, bool>("flags")?;
flags.insert("dark".to_string(), &true)?;What comes back is an ordinary
ReactiveCell or ReactiveMap, so
subscriptions work as they do for declared fields. The type of a cell comes from
its default, so there is nothing to spell out twice.
Where a struct already lives
Section titled “Where a struct already lives”A declared prefix belongs to that struct, and writing inside it through Kv
is refused:
let said = match kv.namespace("network").set("port", &"8080".to_string()) { Err(KvWrite::Declared { at, declared_at, by, }) => { format!("{at} lies in {declared_at}, which {by} declared") } other => { other?; "the write went through".to_string() }};
kv.namespace("networkish") .set("port", &"8080".to_string())?;Printed as it stands, the refusal is one line:
network.port is declared by NetworkThe three facts are on the variant. at is the path that was written,
declared_at the path the schema declared - at itself, or one it lies inside
- and
bythe struct that declared it:
network.port lies in network.port, which Network declarednetworkish is untouched by any of it: what a declaration owns is its own path
and what lies inside it, not every name that starts the same way.
A cell or a map over the same place is an open rather than a write, so that
one comes back as OpenStruct::Taken - the same collision said by the set the
call belongs to.
The reason is what a wrong type does to the struct. Storing a String where a
u16 is declared leaves the field’s subscription unable to decode it, so the
field goes on showing its old value while the store holds something else - and
the next startup fails outright reading that path back.
One path, one type
Section titled “One path, one type”let ui = kv.namespace("ui");
let _width = ui.cell("width", 800u32)?;let refused = ui.cell("width", String::new());Nothing here records a type. A cell reads its path to seed itself, so the second ask finds what the first stored and fails to decode it. The error is a codec error naming what it found.
The check is therefore the stored value, and that has consequences a registry would not. It holds across restarts, because what an earlier run wrote is still there to be read. It holds over a path that was empty, because the first cell writes its default there. And it does not hold for raw access at all:
kv.set("thing", &1u32)?;kv.set("thing", &"now a string".to_string())?; // replaces itkv.get::<u32>("thing")?; // Err, at the readA raw set overwrites whatever was there, whatever its type. The disagreement
surfaces at the next get that asks for the old one.
Where it lands in the file
Section titled “Where it lands in the file”On redb and sqlite there is nothing to say: every key is stored whole, and a
Kv write is one more of them.
A document engine - json, toml, ron - holds two things, and this is where you see it. A declared struct is written as a tree, level by level, because the declarations say where each of its values ends. A path nothing declares is written whole, as one key beside those trees:
store .kv() .namespace("plugins") .namespace("left") .set("width", &240u32)?;{ "chrome": { "width": 800 }, "plugins.left.width": 240}