1pub(crate) use macros::state_trait;
7
8use hvdef::HvRegisterValue;
9use inspect::Inspect;
10use std::fmt::Debug;
11use thiserror::Error;
12
13pub trait StateElement<C, V>: Sized + Inspect {
14 fn is_present(caps: &C) -> bool;
17
18 fn at_reset(caps: &C, vp: &V) -> Self;
20
21 fn can_compare(_caps: &C) -> bool {
27 true
28 }
29}
30
31pub trait HvRegisterState<T, const COUNT: usize>: Default {
32 fn names(&self) -> &'static [T; COUNT];
33 fn get_values<'a>(&self, it: impl Iterator<Item = &'a mut HvRegisterValue>);
34 fn set_values(&mut self, it: impl Iterator<Item = HvRegisterValue>);
35}
36
37#[derive(Debug, Error)]
38#[error("state access error, phase {phase}")]
39pub struct StateError<T: 'static + Debug + std::error::Error> {
40 pub(crate) phase: &'static str,
41 #[source]
42 pub(crate) err: T,
43}
44
45mod macros {
46 macro_rules! state_trait {
62 ($doc:tt, $trait:ident, $caps:ty, $vp:ty, $save_state:ident, $package:expr,
63 $(($field_num:literal, $id:expr, $get:ident, $set:ident, $ty:ty $(,)?)),* $(,)?
64 ) => {
65 #[doc = $doc]
66 pub trait $trait {
67 type Error: 'static + std::error::Error + Send + Sync;
68
69 fn caps(&self) -> &$caps;
71
72 fn commit(&mut self) -> Result<(), Self::Error>;
74
75 $(
76 fn $get(&mut self) -> Result<$ty, Self::Error>;
78 fn $set(&mut self, value: &$ty) -> Result<(), Self::Error>;
80 )*
81
82 #[allow(unused_mut)]
84 fn save_all(&mut self) -> Result<$save_state, $crate::state::StateError<Self::Error>> {
85 let mut save_state = $save_state::default();
86 $(
87 if <$ty as $crate::state::StateElement<$caps, $vp>>::is_present(self.caps()) {
88 save_state.$get = Some(self.$get().map_err(|err| $crate::state::StateError{phase: concat!("save ", stringify!($id)), err})?);
89 }
90 )*
91
92 Ok(save_state)
93 }
94
95 #[allow(unused_variables)]
97 fn restore_all(&mut self, state: &$save_state) -> Result<(), $crate::state::StateError<Self::Error>> {
98 $(
99 if let Some(value) = state.$get.as_ref() {
100 assert!(<$ty as $crate::state::StateElement<$caps, $vp>>::is_present(self.caps()));
102 self.$set(value).map_err(|err| $crate::state::StateError{phase: concat!("restore ", stringify!($id)), err})?;
103 if cfg!(debug_assertions) {
104 if <$ty as $crate::state::StateElement<$caps, $vp>>::can_compare(self.caps()) {
105 assert_eq!(&self.$get().expect($id), value, "restore state mismatch (actual/expected)");
106 }
107 }
108 }
109 )*
110
111 self.commit().map_err(|err| $crate::state::StateError{phase: "commit restore", err})
112 }
113
114 fn reset_all(&mut self, vp_info: &$vp) -> Result<(), $crate::state::StateError<Self::Error>> {
116 $(
117 if <$ty as $crate::state::StateElement<$caps, $vp>>::is_present(self.caps()) {
118 self.$set(&<$ty as $crate::state::StateElement<$caps, $vp>>::at_reset(self.caps(), vp_info)).map_err(|err| $crate::state::StateError{phase: concat!("reset ", stringify!($id)), err})?;
119 }
120 )*
121
122 if cfg!(debug_assertions) {
123 self.check_reset_all(vp_info);
124 }
125 Ok(())
126 }
127
128 #[allow(unused_variables)]
130 fn check_reset_all(&mut self, vp_info: &$vp) {
131 $(
132 if <$ty as $crate::state::StateElement<$caps, $vp>>::can_compare(self.caps()) && <$ty as $crate::state::StateElement<$caps, $vp>>::is_present(self.caps()) {
133 assert_eq!(self.$get().expect($id), <$ty as $crate::state::StateElement<$caps, $vp>>::at_reset(self.caps(), vp_info), "reset state mismatch (actual/expected)");
134 }
135 )*
136 }
137
138 #[allow(unused_variables, unused_mut)]
139 fn inspect_all(&mut self, req: ::inspect::Request<'_>) {
140 let mut resp = req.respond();
141 $(
142 if <$ty as $crate::state::StateElement<$caps, $vp>>::is_present(self.caps()) {
143 resp.field_with($id, || self.$get().ok());
144 }
145 )*
146 }
147 }
148
149 #[derive(Debug, Default, PartialEq, Eq, mesh_protobuf::Protobuf, vmcore::save_restore::SavedStateRoot)]
151 #[mesh(package = $package)]
152 pub struct $save_state {
153 $(
154 #[mesh($field_num)]
155 $get: Option<$ty>,
156 )*
157 }
158 };
159 }
160
161 pub(crate) use state_trait;
162}