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

Быстрый старт

Кратчайший путь от пустого места до работающего store. На каждом шаге — ссылка на раздел, где этот шаг разобран как следует.

Всё, что импортируется ниже, лежит в прелюдии, и обычная программа берёт её целиком:

use amethystate::prelude::*;

В ней есть всё, что нужно, чтобы объявить, открыть, прочитать и записать, — в том числе StoreExt и StoreBackend. Это трейты, и без них у store как будто нет ни get, ни save_now. В примерах ниже импорты выписаны поимённо, чтобы было видно, откуда что берётся.

Один атрибут — и поля структуры живут в store и будят подписчиков. prefix задаёт, где они лежат.

use amethystate::amethystate;
#[amethystate(prefix = "network")]
pub struct NetworkState {
#[amestate(default = "127.0.0.1".to_string())]
pub host: String,
#[amestate(default = 8080u16)]
pub port: u16,
}

Значения по умолчанию, вложенные структуры, volatile-поля, политики чтения, правила для значений и serde — в разделе Состояние.

let store = StoreBuilder::new(settings)
.disk(|d| d.debounce(Duration::from_millis(500)))
.build()?;
let state = NetworkState::new_with(&store);

new_with принимает параметром Store — тот, что вы держите сами. Как открыть один на весь процесс, дать платформе выбрать место для файла и что вернёт close, если последний сброс не удался: Открытие store.

Задержки, повторные попытки и то, чего store не примет: Настройка store. В какой файл пишет движок: Установка.

println!("{}", state.host().get());
let _sub = state.port().subscribe(|port| {
println!("port changed to {port}");
});
state.port().set(9090);

Запись доходит до памяти сразу, до диска — с задержкой. Как звать колбэки в своём потоке, отсеивать собственные записи и сколько стоит подписка: Подписки.

Кому нужна запись сейчас, а не «когда-нибудь потом»: Durability.

Карта кладёт каждую запись по своему пути — добавлять и слушать можно по одной.

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct AlertThresholds {
pub warning: u64,
pub critical: u64,
}
#[amethystate(prefix = "sys")]
pub struct SystemSettings {
#[amestate(default = {
"cpu": AlertThresholds { warning: 70, critical: 90 },
"mem": AlertThresholds { warning: 80, critical: 95 }
})]
pub limits: ReactiveMap<String, AlertThresholds>,
}
state.limits().insert(
"gpu".to_string(),
&AlertThresholds {
warning: 60,
critical: 85,
},
)?;
let cpu = state.limits().get("cpu");
for (key, value) in state.limits().entries() {
println!("{key}: {value:?}");
}
let _sub = state.limits().subscribe_any(|change| {
println!("{change:?}");
});

entries() отдаёт записи в порядке store — то есть по именам, которые ключи одалживают. Нестроковый ключ идёт через Id и упорядочен по написанию, которое Id держит, поэтому Id<u16> приходит как 10, 100, 9.

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

Поднимите version и объявите шаги между версиями — тогда библиотека перенесёт данные, записанные старой сборкой. Поле переименовали или сменили ему тип, а версию не подняли — это дрейф: о нём скажут, и запуск продолжится.

Открывайте store через migrate всегда, когда в бинарнике есть #[migrate]: build не выполняет ни одного шага.

Миграции.

Для фреймворков с явным циклом отрисовки/обновления — egui, iced, ratatui — mode = "persistent" оставляет поля обычными: сохраняются вручную.

#[amethystate(prefix = "kept", mode = "persistent")]
pub struct KeptSettings {
#[amestate(default = "127.0.0.1".to_string())]
pub host: String,
#[amestate(default = 8080u16)]
pub port: u16,
}
let mut state = KeptSettings::load_with(&store);
state.port = 9090;
state.save()?;
state.mutate(|d| {
d.host = "0.0.0.0".to_string();
d.port = 443;
})?;

save_lazy и mutate_lazy делают то же самое, но на диск сбрасывает дебаунсер.

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