incular_core/
restoration.rs1use std::{error::Error, fmt, rc::Rc};
8
9use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
10use serde_json::Value;
11
12#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
19pub struct RestorationKey(String);
20
21impl RestorationKey {
22 pub fn new(segment: impl Into<String>) -> Result<Self, RestorationKeyError> {
24 let segment = segment.into();
25 if segment.is_empty() {
26 return Err(RestorationKeyError::Empty);
27 }
28 if segment.contains('\0') {
29 return Err(RestorationKeyError::ContainsNul);
30 }
31 Ok(Self(segment))
32 }
33
34 #[must_use]
36 pub fn as_str(&self) -> &str {
37 &self.0
38 }
39
40 #[must_use]
42 pub fn into_inner(self) -> String {
43 self.0
44 }
45}
46
47impl TryFrom<String> for RestorationKey {
48 type Error = RestorationKeyError;
49
50 fn try_from(value: String) -> Result<Self, Self::Error> {
51 Self::new(value)
52 }
53}
54
55impl TryFrom<&str> for RestorationKey {
56 type Error = RestorationKeyError;
57
58 fn try_from(value: &str) -> Result<Self, Self::Error> {
59 Self::new(value)
60 }
61}
62
63impl fmt::Display for RestorationKey {
64 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
65 formatter.write_str(self.as_str())
66 }
67}
68
69impl Serialize for RestorationKey {
70 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
71 where
72 S: Serializer,
73 {
74 self.0.serialize(serializer)
75 }
76}
77
78impl<'de> Deserialize<'de> for RestorationKey {
79 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
80 where
81 D: Deserializer<'de>,
82 {
83 String::deserialize(deserializer)
84 .and_then(|segment| Self::new(segment).map_err(de::Error::custom))
85 }
86}
87
88#[derive(Clone, Copy, Debug, PartialEq, Eq)]
90pub enum RestorationKeyError {
91 Empty,
93 ContainsNul,
95}
96
97impl fmt::Display for RestorationKeyError {
98 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
99 match self {
100 Self::Empty => formatter.write_str("a restoration key cannot be empty"),
101 Self::ContainsNul => formatter.write_str("a restoration key cannot contain NUL"),
102 }
103 }
104}
105
106impl Error for RestorationKeyError {}
107
108pub trait RestorationBackend {
115 fn read_value(&self, path: &[RestorationKey]) -> Option<Value>;
117
118 fn write_value(&self, path: &[RestorationKey], value: Value);
120
121 fn remove_value(&self, path: &[RestorationKey]);
123
124 fn note_restoration_outcome(&self, _restored: u64, _invalid: u64) {}
129}
130
131#[derive(Clone)]
137pub struct RestorationScope {
138 backend: Rc<dyn RestorationBackend>,
139 path: Vec<RestorationKey>,
140}
141
142impl fmt::Debug for RestorationScope {
143 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
144 formatter
145 .debug_struct("RestorationScope")
146 .field("path", &self.path)
147 .finish_non_exhaustive()
148 }
149}
150
151impl RestorationScope {
152 #[must_use]
154 pub fn root(backend: Rc<dyn RestorationBackend>) -> Self {
155 Self {
156 backend,
157 path: Vec::new(),
158 }
159 }
160
161 #[must_use]
163 pub fn new(
164 backend: Rc<dyn RestorationBackend>,
165 path: impl IntoIterator<Item = RestorationKey>,
166 ) -> Self {
167 Self {
168 backend,
169 path: path.into_iter().collect(),
170 }
171 }
172
173 #[must_use]
179 pub fn child_unchecked(&self, key: RestorationKey) -> Self {
180 let mut path = self.path.clone();
181 path.push(key);
182 Self {
183 backend: self.backend.clone(),
184 path,
185 }
186 }
187
188 #[must_use]
196 pub fn child(&self, key: RestorationKey) -> Self {
197 self.child_unchecked(key)
198 }
199
200 #[must_use]
201 pub fn is_root(&self) -> bool {
202 self.path.is_empty()
203 }
204
205 #[must_use]
207 pub fn path(&self) -> &[RestorationKey] {
208 &self.path
209 }
210
211 #[must_use]
213 pub fn get_json(&self, key: &RestorationKey) -> Option<Value> {
214 self.backend.read_value(&self.value_path(key))
215 }
216
217 pub fn set_json(&self, key: &RestorationKey, value: Value) {
222 self.backend.write_value(&self.value_path(key), value);
223 }
224
225 pub fn remove(&self, key: &RestorationKey) {
227 self.backend.remove_value(&self.value_path(key));
228 }
229
230 pub fn note_restoration_outcome(&self, restored: u64, invalid: u64) {
234 self.backend.note_restoration_outcome(restored, invalid);
235 }
236
237 fn value_path(&self, key: &RestorationKey) -> Vec<RestorationKey> {
238 let mut path = self.path.clone();
239 path.push(key.clone());
240 path
241 }
242}