canic_core/ops/storage/state/
app.rs1use crate::{
2 Error, ThisError,
3 dto::state::{AppCommand, AppStateView},
4 log,
5 log::Topic,
6 model::memory::state::{AppMode, AppState},
7 ops::{
8 adapter::state::{app_state_from_view, app_state_to_view},
9 storage::state::StateOpsError,
10 },
11};
12
13#[cfg(test)]
14use crate::model::memory::state::AppStateData;
15
16#[derive(Debug, ThisError)]
21pub enum AppStateOpsError {
22 #[error("app is already in {0} mode")]
23 AlreadyInMode(AppMode),
24}
25
26impl From<AppStateOpsError> for Error {
27 fn from(err: AppStateOpsError) -> Self {
28 StateOpsError::from(err).into()
29 }
30}
31
32pub struct AppStateOps;
37
38impl AppStateOps {
39 #[must_use]
41 #[cfg(test)]
42 pub fn get_mode() -> AppMode {
43 AppState::get_mode()
44 }
45
46 pub fn command(cmd: AppCommand) -> Result<(), Error> {
47 let old_mode = AppState::get_mode();
48
49 let new_mode = match cmd {
50 AppCommand::Start => AppMode::Enabled,
51 AppCommand::Readonly => AppMode::Readonly,
52 AppCommand::Stop => AppMode::Disabled,
53 };
54
55 if old_mode == new_mode {
56 return Err(AppStateOpsError::AlreadyInMode(old_mode))?;
57 }
58
59 AppState::set_mode(new_mode);
60
61 log!(Topic::App, Ok, "app: mode changed {old_mode} -> {new_mode}");
62
63 Ok(())
64 }
65
66 #[cfg(test)]
67 pub(crate) fn import(data: AppStateData) {
68 AppState::import(data);
69 }
70
71 pub fn import_view(view: AppStateView) {
73 let data = app_state_from_view(view);
74 AppState::import(data);
75 }
76
77 #[must_use]
80 pub fn export_view() -> AppStateView {
81 let data = AppState::export();
82
83 app_state_to_view(data)
84 }
85}
86
87#[cfg(test)]
92mod tests {
93 use super::*;
94 use crate::config::Config;
95
96 fn reset_state(mode: AppMode) {
97 Config::reset_for_tests();
98 let _ = Config::init_for_tests();
99 AppStateOps::import(AppStateData { mode });
100 }
101
102 #[test]
103 fn command_changes_modes() {
104 reset_state(AppMode::Disabled);
105
106 assert!(AppStateOps::command(AppCommand::Start).is_ok());
107 assert_eq!(AppStateOps::get_mode(), AppMode::Enabled);
108
109 assert!(AppStateOps::command(AppCommand::Readonly).is_ok());
110 assert_eq!(AppStateOps::get_mode(), AppMode::Readonly);
111
112 assert!(AppStateOps::command(AppCommand::Stop).is_ok());
113 assert_eq!(AppStateOps::get_mode(), AppMode::Disabled);
114 }
115
116 #[test]
117 fn duplicate_command_fails() {
118 reset_state(AppMode::Enabled);
119
120 let err = AppStateOps::command(AppCommand::Start)
121 .unwrap_err()
122 .to_string();
123
124 assert!(
125 err.contains("app is already in Enabled mode"),
126 "unexpected error: {err}"
127 );
128 }
129}