Перейти к содержимому

Kv

Реактивные значения, к которым обращаются по пути, без всякой объявленной структуры. Для набора ключей, который складывается в рантайме, и для случаев, где схема — возни больше, чем задача того стоит.

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")?;

Любое имя здесь занимает один уровень, а вкладывает namespace. Поэтому kv.set("ui.width", ..) пишет один ключ с именем ui.width — имя, в котором просто стоит точка, — а kv.namespace("ui").set("width", ..) пишет width внутри ui.

Точку в имени на диске экранируют, так что это два разных места, разными они и останутся. Перепутать их местами — ошибка, за которой стоит следить: строка с точками выглядит как путь, а путь она не задаёт.

keys() перечисляет всё под тем хендлом, на котором его позвали, — на всех уровнях ниже и по порядку. Сужает перечисление namespace; корневой хендл перечислит весь 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"]);

Приходят целые пути, поэтому width внутри ui значится как ui.width, а корневой theme из хендла ui не значится вовсе.

let width = kv.namespace("ui").cell("width", 800u32)?;
let flags = kv.map::<String, bool>("flags")?;
flags.insert("dark".to_string(), &true)?;

Приходит обычный ReactiveCell или ReactiveMap, поэтому подписки тут работают ровно как у объявленных полей. Тип ячейки берут из её значения по умолчанию — писать одно и то же дважды не придётся.

Объявленный prefix принадлежит своей структуре, и запись внутрь него через Kv получит отказ:

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())?;

Сам по себе отказ печатается одной строкой:

network.port is declared by Network

А три факта лежат на варианте: at — путь, куда писали, declared_at — путь, который объявила схема (он же сам at или тот, внутри которого at лежит), и by — структура, которая его объявила:

network.port lies in network.port, which Network declared

networkish при этом не задет: объявление владеет своим путём и тем, что лежит внутри него, а не всеми именами, которые так же начинаются.

cell или map над тем же местом — это открытие, а не запись, поэтому оттуда приходит OpenStruct::Taken: то же столкновение, сказанное набором того вызова, которому оно принадлежит.

Причина — в том, что чужой тип делает со структурой. Положите String туда, где объявлен u16, — подписка поля это не декодирует, поэтому поле будет показывать старое значение, пока store держит другое. А следующий запуск просто упадёт, читая этот путь обратно.

let ui = kv.namespace("ui");
let _width = ui.cell("width", 800u32)?;
let refused = ui.cell("width", String::new());

Тип здесь нигде не записан. Ячейка читает свой путь, чтобы заполнить себя, — и второй запрос находит то, что положил первый, и декодировать это не может. Ошибка приходит от кодека и называет, что он нашёл.

Выходит, проверяет тут само сохранённое значение, и отсюда следствия, которых у реестра типов не было бы. Проверка переживает перезапуск: то, что записал прошлый прогон, всё ещё лежит и читается. Работает она и на пустом пути: первая же ячейка кладёт туда своё значение по умолчанию. А вот для сырого доступа её нет вовсе:

kv.set("thing", &1u32)?;
kv.set("thing", &"now a string".to_string())?; // replaces it
kv.get::<u32>("thing")?; // Err, at the read

Сырой set перезапишет что угодно, какого бы типа оно ни было. Расхождение всплывёт на следующем get, который попросит прежний тип.

Версии, миграции и поиск дрейфа остаются объявленным структурам. Если у данных есть форма и её собираются развивать — объявите её.