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