canic_core/model/memory/state/
app.rs1use crate::{
2 cdk::structures::{DefaultMemoryImpl, cell::Cell, memory::VirtualMemory},
3 eager_static, ic_memory,
4 memory::impl_storable_bounded,
5 model::memory::id::state::APP_STATE_ID,
6};
7use candid::CandidType;
8use derive_more::Display;
9use serde::{Deserialize, Serialize};
10use std::cell::RefCell;
11
12eager_static! {
17 static APP_STATE: RefCell<Cell<AppStateData, VirtualMemory<DefaultMemoryImpl>>> =
18 RefCell::new(Cell::init(
19 ic_memory!(AppState, APP_STATE_ID),
20 AppStateData::default(),
21 ));
22}
23
24#[derive(
31 CandidType, Clone, Copy, Debug, Default, Display, Eq, PartialEq, Serialize, Deserialize,
32)]
33pub enum AppMode {
34 Enabled,
35 Readonly,
36 #[default]
37 Disabled,
38}
39
40#[derive(CandidType, Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
45pub struct AppStateData {
46 pub mode: AppMode,
47}
48
49impl_storable_bounded!(AppStateData, 32, true);
50
51pub struct AppState;
56
57impl AppState {
58 #[must_use]
59 pub(crate) fn get_mode() -> AppMode {
60 APP_STATE.with_borrow(|cell| cell.get().mode)
61 }
62
63 pub(crate) fn set_mode(mode: AppMode) {
64 APP_STATE.with_borrow_mut(|cell| {
65 let mut data = *cell.get();
66 data.mode = mode;
67 cell.set(data);
68 });
69 }
70
71 pub(crate) fn import(data: AppStateData) {
72 APP_STATE.with_borrow_mut(|cell| cell.set(data));
73 }
74
75 #[must_use]
76 pub(crate) fn export() -> AppStateData {
77 APP_STATE.with_borrow(|cell| *cell.get())
78 }
79}
80
81#[cfg(test)]
86mod tests {
87 use super::*;
88
89 fn reset_state(mode: AppMode) {
90 AppState::import(AppStateData { mode });
91 }
92
93 #[test]
94 fn default_mode_is_disabled() {
95 reset_state(AppMode::Disabled);
96 assert_eq!(AppState::get_mode(), AppMode::Disabled);
97 }
98
99 #[test]
100 fn can_set_mode() {
101 reset_state(AppMode::Disabled);
102
103 AppState::set_mode(AppMode::Enabled);
104 assert_eq!(AppState::get_mode(), AppMode::Enabled);
105
106 AppState::set_mode(AppMode::Readonly);
107 assert_eq!(AppState::get_mode(), AppMode::Readonly);
108 }
109
110 #[test]
111 fn import_and_export_state() {
112 reset_state(AppMode::Disabled);
113
114 let data = AppStateData {
115 mode: AppMode::Readonly,
116 };
117 AppState::import(data);
118
119 assert_eq!(AppState::export().mode, AppMode::Readonly);
120
121 let exported = AppState::export();
123 assert_eq!(exported, data);
124 }
125}