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

Объявление структуры

#[amethystate] заставляет обычную структуру Rust жить в store. Эта страница — про саму структуру: где она лежит, к какой «линии» версий относится и как структуры держат друг друга. Остальное в разделе — по странице на тему:

#[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 читает и пишет всюду, где место не занято структурой. Где проходит эта граница, решает Кто владеет каким местом.

По умолчанию поля лежат под 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

Эта форма нужна, когда файл читает кто-то кроме этого крейта: конфиг, который правят руками, или такой, чьи ключи другая программа уже ждёт на верхнем уровне. Корневые поля занимают место так же, как любые другие, поэтому две структуры, потянувшиеся к одному ключу, всё равно столкнутся: Кто владеет каким местом.