Объявление структуры
#[amethystate] заставляет обычную структуру Rust жить в store. Эта страница —
про саму структуру: где она лежит, к какой «линии» версий относится и как
структуры держат друг друга. Остальное в разделе — по странице на тему:
- Поля — где лежит каждое поле и как;
- Когда значение не читается — байты, которые не декодируются, и ключ, удалённый под полем;
- Правила — каким значению быть, в обе стороны: на каждом входе и каждом выходе;
- Как открыть структуру по-своему —
open = manualи инварианты между полями; - Что здесь значит serde.
Атрибуты структуры
Заголовок раздела «Атрибуты структуры»#[amethystate(prefix = "network", version = 1, mode = "reactive")]pub struct NetworkState { ... }| Атрибут | Тип | Описание |
|---|---|---|
prefix |
String |
Место в store, под которым лежат эти поля. Корневой структуре нужен он или as_root. |
version |
u32 |
Версия схемы для миграций. По умолчанию 0. |
id |
String |
К какой линии версий на префиксе относится структура. Нужен, только когда префикс делят несколько структур, — см. ниже. |
rename_all |
&str |
Как пишется имя каждого поля там, где его хранят. Список закрытый, восемь вариантов: lowercase, UPPERCASE, PascalCase, camelCase, snake_case, SCREAMING_SNAKE_CASE, kebab-case, SCREAMING-KEBAB-CASE. Всё остальное — ошибка компиляции. Поля, у которых есть свой path, он не трогает. |
mode |
String |
Режим генерации кода: "reactive" (по умолчанию) или "persistent". "reactive" открывается через new_with, и каждое поле в нём — Field; "persistent" — обычная структура, которую читает load и пишет save, см. Режим Persistent-only, и ей нужен prefix или as_root. |
as_root |
flag |
Поля ложатся на самый верх store, без имени над ними. Пишется вместо prefix: они говорят разное про одно и то же место, поэтому написать оба — ошибка компиляции. |
on_unreadable |
вариант | Что делать со значением, которое не декодируется. Refuse (по умолчанию) или UseDefault. См. Когда значение не читается. |
on_delete |
вариант | Что делает поле, когда под ним удалили его ключ. Keep (по умолчанию) или UseDefault. |
unreadable_entries |
вариант | Что делает ReactiveMap с записью, которую не может прочитать. Refuse (по умолчанию) или Skip. У полей, которые не карты, записей нет, и они его не замечают. |
open |
manual |
Open структуры пишут руками, и через глобальный store её никто не открывает. См. Как открыть структуру по-своему. |
rule у структуры не бывает: правило судит одно значение, и место ему на поле.
Инвариант между полями проверяют в собственном Open структуры.
Структура, не сказавшая ни prefix, ни as_root, — компонент: своего места у
неё нет, и её встраивают в другую через nested. Место ей даёт то поле, которое
её держит.
Места и линии
Заголовок раздела «Места и линии»Сам prefix не занимает ничего — занимают поля под ним, каждое свой путь.
Поэтому две структуры над одним префиксом прекрасно уживаются, пока не сойдутся
два их пути; вторая, открывающаяся над занятым путём, получит отказ. Исключение
— карта: она владеет всем под собой, потому что её ключи возникают во время
работы. Отсюда же растёт то, как ведут себя prefix и составное имя с точками:
Кто владеет каким местом.
Места — одно, версии — другое. Версии считают по линиям, а линия — это префикс
вместе с id структуры. Структуры без id — версии одной, безымянной линии
префикса. Поэтому, если префикс делят несколько структур, id нужен всем, кроме
одной:
#[amethystate(prefix = "ui", id = "panels", version = 3)]pub struct Panels { ... }Две безымянные структуры на одном префиксе с одной версией — это одна версия,
объявленная дважды, и store не откроется: MigrationError::DeclaredTwice
назовёт обе. Места у линий на общем префиксе всё равно свои: если две линии
владеют одним местом или одна лежит внутри другой, store тоже не откроется,
а MigrationError::ClaimedTwice назовёт место и обе структуры. Написанный id
уже не меняют. Структура с другим id начинает новую линию, а то, что записала
старая, вернётся отчётом о дрейфе.
Вложенные структуры
Заголовок раздела «Вложенные структуры»Структура без prefix — компонент: своего места в store у неё нет, её
встраивают в родительскую через nested. Префикс родителя встаёт впереди всех
вложенных полей.
#[amethystate]pub struct DatabaseConfig { #[amestate(default = "localhost".to_string())] pub host: String,}
#[amethystate(prefix = "sys")]pub struct SystemSettings { #[amestate(nested)] pub db: DatabaseConfig, // stored as "sys.db.host"}Вложенное поле может положить свои поля и на уровень хозяина, без собственного
сегмента: flatten.
Как разделить одно место между двумя структурами
Заголовок раздела «Как разделить одно место между двумя структурами»Одно место двум структурам не объявить — вторая при открытии получит отказ. Где до одного значения надо дотянуться с двух сторон, адресуйте его по пути из той структуры, которая его не объявляла: Kv читает и пишет всюду, где место не занято структурой. Где проходит эта граница, решает Кто владеет каким местом.
Хранение на уровне корня (as_root)
Заголовок раздела «Хранение на уровне корня (as_root)»По умолчанию поля лежат под prefix структуры. С as_root они ложатся прямо в
корень store, без всякого пространства имён.
#[amethystate(mode = "persistent", as_root)]pub struct AppConfig { #[amestate(default = "acme".to_string())] pub name: String,
#[amestate(default = false)] pub verbose: bool,}Получается файл вида:
name = "acme"verbose = falseЭта форма нужна, когда файл читает кто-то кроме этого крейта: конфиг, который правят руками, или такой, чьи ключи другая программа уже ждёт на верхнем уровне. Корневые поля занимают место так же, как любые другие, поэтому две структуры, потянувшиеся к одному ключу, всё равно столкнутся: Кто владеет каким местом.