Правила
Позиция окна −32000, кегль ноль и имя темы, которую никто не ставил,
декодируются прекрасно. Когда значение не читается
— про байты, которые не читаются, а rule — то место, где приложение говорит,
какими должны быть те, что читаются.
Правило объявляют один раз, на поле, и оно стоит на каждом пути, которым значение проходит через это поле, в обе стороны:
- внутрь — прочитано из store, когда структуру строят или загружают;
принесено правкой файла; записано в обход поля — по пути через
Store::set; - наружу — записано самим процессом через
set,updateилиmodifyили сохраненоsaveу загруженной структуры.
Объявленного значения по умолчанию среди них нет. Это собственное значение
объявления, и его берут как написано: когда в store ничего нет, после
UseDefault и как первое значение volatile-поля. Если правило такое значение
поправило бы, менять надо значение по умолчанию.
Правило не отказывает. Оно берёт значение по &mut и оставляет его годным:
кегль вне рамок подрезает, неустановленную тему заменяет. Что оно оставило, то
и держит поле, и то же держит store: поправку на входе записывают обратно в
store, а поправку при save оставляют в структуре у вас в руках. Так что никому
не приходится подрезать значение перед set, чтобы оно не вышло за рамки, и ни
один экран не покажет того, чего нет в файле.
Как его написать
Заголовок раздела «Как его написать»Правило — голая fn: берёт значение и контекст и ничего не возвращает.
fn a_size_that_renders(size: &mut u8, _cx: &RuleContext) { *size = (*size).clamp(6, 72);}
fn a_theme_that_is_installed(theme: &mut String, cx: &RuleContext) { let installed = cx.require::<InstalledThemes>();
if !installed.0.contains(&theme.as_str()) { *theme = "dark".to_string(); }}
#[amethystate(prefix = "ruled_ui")]pub struct Ui { #[amestate(default = 14u8, rule = a_size_that_renders)] pub font_size: u8,
#[amestate(default = "dark".to_string(), rule = a_theme_that_is_installed)] pub theme: String,}Контекст нужен потому, что правило — голая fn: оно ничего не захватывает.
Мир, по которому она судит значение, — какие есть мониторы, какие темы
установлены, — передают store при открытии.
let store = StoreBuilder::new(settings) .context(InstalledThemes(installed)) .build()?;По одному значению на тип. Спрашивают через cx.get::<T>() — он отвечает
None, если ничего не передали, — или через cx.require::<T>(), который тогда
паникует. Store, собранный без того, что нужно его правилам, собран неправильно
одинаково при каждом запуске, а паника называет запрошенный тип и перечисляет,
что передали вместо него.
Запись, которую правило поправило
Заголовок раздела «Запись, которую правило поправило»Запись ложится такой, какой её оставило правило, — и в поле, и в store.
ui.font_size().set(200);
assert_eq!(ui.font_size().get(), 72);assert_eq!(store.get::<u8>(["ruled_ui", "font_size"])?, Some(72));Где правило выполняется, а где нет
Заголовок раздела «Где правило выполняется, а где нет»| значение | правило поля |
|---|---|
| прочитано, пока строится структура | выполняется; поправка записывается обратно |
записано через поле: set, update, modify |
выполняется; запись ложится такой, какой её оставило правило |
записано в обход поля: Store::set по пути |
выполняется, когда значение приходит; поправка записывается обратно |
| правка извне процесса | выполняется, когда значение приходит, так же |
load_with |
выполняется; поправка записывается обратно |
save у загруженной структуры |
выполняется на всех полях до первой записи |
| шаг миграции | не выполняется |
Запись в обход поля поправляют уже после того, как store её принял. Поле узнаёт о ней позже store, поэтому на миг в файле лежит то, что пришло. Потом поправку записывают поверх, и в файле оказывается то же, что держит поле.
Поправка, которую store не взял, — это расхождение. Правило может
превратить значение в такое, какое движок не удержит, — например, в
бесконечность для движка, который пишет JSON. Тогда поле держит то, что
сделало правило, store — то, что ему дали, а try_get отвечает
Reason::NotWrittenBack, пока не ляжет следующее изменение.
save берёт структуру по &mut и проводит каждое поле через его правило,
прежде чем что-нибудь записать. Что правило поправило, оно поправило и в
структуре в руках, так что после сохранения в ней то же, что в файле.
Куда правило ставят, а куда нет
Заголовок раздела «Куда правило ставят, а куда нет»Правило берёт и volatile-поле. Из store в него ничего не приходит, зато
каждая запись идёт через правило, и оно держит в рамках значение, которое
процесс хранит в памяти и никуда не кладёт.
Два поля правила не примут вовсе, и макрос скажет об этом на компиляции:
nested-поле. Правило поля судит одно значение по одному пути. Вложенная структура — не значение, а поддерево путей, и звать правило не с чем. Правила вешают на поля самой вложенной структуры.- Карта. Правило объявляют один раз и про одно значение, а записи карты —
данные: они приходят и уходят на ходу, и объявленного пути, на который
повесить правило, у них нет. Что карта делает с записью, которую не может
прочитать, решает
unreadable_entries— это на странице Поля, а не здесь.
Не примет правила и структура. Структура — место, где лежат поля, а не значение само по себе, поэтому инвариант между двумя полями — наименьшая ширина не больше наибольшей — поправляют там, где структуру открывают: Как открыть структуру по-своему.