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 #[expect(clippy::allow_attributes)]
67 pub trait $trait {
68 type Error: 'static + std::error::Error + Send + Sync;
69
70 fn caps(&self) -> &$caps;
72
73 fn commit(&mut self) -> Result<(), Self::Error>;
75
76 $(
77 fn $get(&mut self) -> Result<$ty, Self::Error>;
79 fn $set(&mut self, value: &$ty) -> Result<(), Self::Error>;
81 )*
82
83 #[allow(unused_mut)]
85 fn save_all(&mut self) -> Result<$save_state, $crate::state::StateError<Self::Error>> {
86 let mut save_state = $save_state::default();
87 $(
88 if <$ty as $crate::state::StateElement<$caps, $vp>>::is_present(self.caps()) {
89 save_state.$get = Some(self.$get().map_err(|err| $crate::state::StateError{phase: concat!("save ", stringify!($id)), err})?);
90 }
91 )*
92
93 Ok(save_state)
94 }
95
96 #[allow(unused_variables)]
98 fn restore_all(&mut self, state: &$save_state) -> Result<(), $crate::state::StateError<Self::Error>> {
99 $(
100 if let Some(value) = state.$get.as_ref() {
101 assert!(<$ty as $crate::state::StateElement<$caps, $vp>>::is_present(self.caps()));
103 self.$set(value).map_err(|err| $crate::state::StateError{phase: concat!("restore ", stringify!($id)), err})?;
104 if cfg!(debug_assertions) {
105 if <$ty as $crate::state::StateElement<$caps, $vp>>::can_compare(self.caps()) {
106 assert_eq!(&self.$get().expect($id), value, "restore state mismatch (actual/expected)");
107 }
108 }
109 }
110 )*
111
112 self.commit().map_err(|err| $crate::state::StateError{phase: "commit restore", err})
113 }
114
115 fn reset_all(&mut self, vp_info: &$vp) -> Result<(), $crate::state::StateError<Self::Error>> {
117 $(
118 if <$ty as $crate::state::StateElement<$caps, $vp>>::is_present(self.caps()) {
119 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})?;
120 }
121 )*
122
123 if cfg!(debug_assertions) {
124 self.check_reset_all(vp_info);
125 }
126 Ok(())
127 }
128
129 #[allow(unused_variables)]
131 fn check_reset_all(&mut self, vp_info: &$vp) {
132 $(
133 if <$ty as $crate::state::StateElement<$caps, $vp>>::can_compare(self.caps()) && <$ty as $crate::state::StateElement<$caps, $vp>>::is_present(self.caps()) {
134 assert_eq!(self.$get().expect($id), <$ty as $crate::state::StateElement<$caps, $vp>>::at_reset(self.caps(), vp_info), "reset state mismatch (actual/expected)");
135 }
136 )*
137 }
138
139 #[allow(unused_variables, unused_mut)]
140 fn inspect_all(&mut self) -> impl ::inspect::InspectMut {
141 inspect::adhoc_mut(|req| {
142 let mut resp = req.respond();
143 $(
144 if <$ty as $crate::state::StateElement<$caps, $vp>>::is_present(self.caps()) {
145 resp.field_with($id, || self.$get().ok());
146 }
147 )*
148 })
149 }
150 }
151
152 #[derive(Debug, Default, PartialEq, Eq, mesh_protobuf::Protobuf, vmcore::save_restore::SavedStateRoot)]
154 #[mesh(package = $package)]
155 pub struct $save_state {
156 $(
157 #[mesh($field_num)]
158 $get: Option<$ty>,
159 )*
160 }
161 };
162 }
163
164 pub(crate) use state_trait;
165}