1#![doc = include_str!("../README.md")]
2#![no_std]
3
4use embedded_io::{ErrorType, Read, ReadReady, Write};
5use heapless::String;
6use miniconf::{
7 NodeIter, Path, SerdeError, TreeDeserializeOwned, TreeSchema,
8 TreeSerialize, ValueError, json_core, postcard,
9};
10
11mod interface;
12pub use interface::BestEffortInterface;
13
14pub trait Settings:
17 TreeSchema + TreeSerialize + TreeDeserializeOwned + Clone
18{
19 fn reset(&mut self) {}
21}
22
23pub trait Platform {
31 type Interface: embedded_io::Read
33 + embedded_io::ReadReady
34 + embedded_io::Write;
35
36 type Error: core::fmt::Debug;
37
38 type Settings: Settings;
39
40 fn fetch<'a>(
42 &mut self,
43 buf: &'a mut [u8],
44 key: &[u8],
45 ) -> Result<Option<&'a [u8]>, Self::Error>;
46
47 fn store(
49 &mut self,
50 buf: &mut [u8],
51 key: &[u8],
52 value: &[u8],
53 ) -> Result<(), Self::Error>;
54
55 fn clear(&mut self, buf: &mut [u8], key: &[u8]) -> Result<(), Self::Error>;
57
58 fn cmd(&mut self, cmd: &str);
60
61 fn interface_mut(&mut self) -> &mut Self::Interface;
63}
64
65struct Interface<'a, P> {
66 platform: P,
67 buffer: &'a mut [u8],
68 updated: bool,
69}
70
71impl<'a, P: Platform> Interface<'a, P> {
72 fn handle_platform(
73 _menu: &menu::Menu<Self, P::Settings>,
74 item: &menu::Item<Self, P::Settings>,
75 args: &[&str],
76 interface: &mut Self,
77 _settings: &mut P::Settings,
78 ) {
79 let key = menu::argument_finder(item, args, "cmd").unwrap().unwrap();
80 interface.platform.cmd(key)
81 }
82
83 fn iter_root<F>(
84 key: Option<&str>,
85 interface: &mut Self,
86 settings: &mut P::Settings,
87 mut func: F,
88 ) where
89 F: FnMut(
90 Path<&str, '/'>,
91 &mut Self,
92 &mut P::Settings,
93 &mut P::Settings,
94 ),
95 {
96 let iter = if let Some(key) = key {
97 match NodeIter::with_root(P::Settings::SCHEMA, Path::<_, '/'>(key))
98 {
99 Ok(it) => it,
100 Err(e) => {
101 writeln!(interface, "Failed to locate `{key}`: {e}")
102 .unwrap();
103 return;
104 }
105 }
106 } else {
107 NodeIter::<Path<String<128>, '/'>, MAX_DEPTH>::new(
108 P::Settings::SCHEMA,
109 )
110 };
111
112 let mut defaults = settings.clone();
113 defaults.reset();
114 for key in iter {
115 match key {
116 Ok(key) => func(
117 Path(key.0.as_str()),
118 interface,
119 settings,
120 &mut defaults,
121 ),
122 Err(depth) => {
123 writeln!(
124 interface,
125 "Failed to build path: no space at depth {depth}"
126 )
127 .unwrap();
128 }
129 }
130 }
131 }
132
133 fn handle_get(
134 _menu: &menu::Menu<Self, P::Settings>,
135 item: &menu::Item<Self, P::Settings>,
136 args: &[&str],
137 interface: &mut Self,
138 settings: &mut P::Settings,
139 ) {
140 let key = menu::argument_finder(item, args, "path").unwrap();
141 Self::iter_root(
142 key,
143 interface,
144 settings,
145 |key, interface, settings, defaults| {
146 let check = match json_core::get_by_key(
148 settings,
149 key,
150 interface.buffer,
151 ) {
152 Err(SerdeError::Value(ValueError::Absent)) => {
153 return;
154 }
155 Err(e) => {
156 writeln!(interface, "Failed to get `{}`: {e}", key.0)
157 .unwrap();
158 return;
159 }
160 Ok(len) => {
161 write!(
162 interface.platform.interface_mut(),
163 "{}: {}",
164 key.0,
165 core::str::from_utf8(&interface.buffer[..len])
166 .unwrap()
167 )
168 .unwrap();
169 yafnv::fnv1a::<u32>(&interface.buffer[..len])
170 }
171 };
172
173 match json_core::get_by_key(defaults, key, interface.buffer) {
175 Err(SerdeError::Value(ValueError::Absent)) => {
176 write!(interface, " [default: absent]")
177 }
178 Err(e) => {
179 write!(interface, " [default serialization error: {e}]")
180 }
181 Ok(len) => {
182 if yafnv::fnv1a::<u32>(&interface.buffer[..len])
183 != check
184 {
185 write!(
186 interface.platform.interface_mut(),
187 " [default: {}]",
188 core::str::from_utf8(&interface.buffer[..len])
189 .unwrap()
190 )
191 } else {
192 write!(interface, " [default]")
193 }
194 }
195 }
196 .unwrap();
197
198 match interface
200 .platform
201 .fetch(interface.buffer, key.0.as_bytes())
202 {
203 Err(e) => write!(interface, " [fetch error: {e:?}]"),
204 Ok(None) => write!(interface, " [not stored]"),
205 Ok(Some(stored)) => {
206 let slic = ::postcard::de_flavors::Slice::new(stored);
207 match postcard::set_by_key(defaults, key, slic) {
209 Err(e) => write!(
210 interface,
211 " [stored deserialize error: {e}]"
212 ),
213 Ok(_rest) => match json_core::get_by_key(
214 defaults,
215 key,
216 interface.buffer,
217 ) {
218 Err(e) => write!(
219 interface,
220 " [stored serialization error: {e}]"
221 ),
222 Ok(len) => {
223 if yafnv::fnv1a::<u32>(
224 &interface.buffer[..len],
225 ) != check
226 {
227 write!(
228 interface.platform.interface_mut(),
229 " [stored: {}]",
230 core::str::from_utf8(
231 &interface.buffer[..len]
232 )
233 .unwrap()
234 )
235 } else {
236 write!(interface, " [stored]")
237 }
238 }
239 },
240 }
241 }
242 }
243 .unwrap();
244 writeln!(interface).unwrap();
245 },
246 );
247 }
248
249 fn handle_clear(
250 _menu: &menu::Menu<Self, P::Settings>,
251 item: &menu::Item<Self, P::Settings>,
252 args: &[&str],
253 interface: &mut Self,
254 settings: &mut P::Settings,
255 ) {
256 let key = menu::argument_finder(item, args, "path").unwrap();
257 Self::iter_root(
258 key,
259 interface,
260 settings,
261 |key, interface, settings, defaults| {
262 let slic =
264 ::postcard::ser_flavors::Slice::new(interface.buffer);
265 let check = match postcard::get_by_key(settings, key, slic) {
266 Err(SerdeError::Value(ValueError::Absent)) => {
267 return;
268 }
269 Err(e) => {
270 writeln!(interface, "Failed to get {}: {e:?}", key.0)
271 .unwrap();
272 return;
273 }
274 Ok(slic) => yafnv::fnv1a::<u32>(slic),
275 };
276
277 let slic =
279 ::postcard::ser_flavors::Slice::new(interface.buffer);
280 let slic = match postcard::get_by_key(defaults, key, slic) {
281 Err(SerdeError::Value(ValueError::Absent)) => {
282 log::warn!(
283 "Can't clear. Default is absent: `{}`",
284 key.0
285 );
286 None
287 }
288 Err(e) => {
289 writeln!(
290 interface,
291 "Failed to get default `{}`: {e}",
292 key.0
293 )
294 .unwrap();
295 return;
296 }
297 Ok(slic) => {
298 if yafnv::fnv1a::<u32>(slic) != check {
299 Some(slic)
300 } else {
301 None
302 }
303 }
304 };
305
306 if let Some(slic) = slic {
308 let slic = ::postcard::de_flavors::Slice::new(slic);
309 match postcard::set_by_key(settings, key, slic) {
310 Err(SerdeError::Value(ValueError::Absent)) => {
311 return;
312 }
313 Err(e) => {
314 writeln!(
315 interface,
316 "Failed to set {}: {e:?}",
317 key.0
318 )
319 .unwrap();
320 return;
321 }
322 Ok(_rest) => {
323 interface.updated = true;
324 writeln!(interface, "Cleared current `{}`", key.0)
325 .unwrap()
326 }
327 }
328 }
329
330 match interface
332 .platform
333 .fetch(interface.buffer, key.0.as_bytes())
334 {
335 Err(e) => {
336 writeln!(
337 interface,
338 "Failed to fetch `{}`: {e:?}",
339 key.0
340 )
341 .unwrap();
342 }
343 Ok(None) => {}
344 Ok(Some(_stored)) => match interface
346 .platform
347 .clear(interface.buffer, key.0.as_bytes())
348 {
349 Ok(()) => {
350 writeln!(interface, "Clear stored `{}`", key.0)
351 }
352 Err(e) => {
353 writeln!(
354 interface,
355 "Failed to clear `{}` from storage: {e:?}",
356 key.0
357 )
358 }
359 }
360 .unwrap(),
361 }
362 },
363 );
364 interface.updated = true;
365 writeln!(interface, "Some values may require reboot to become active")
366 .unwrap();
367 }
368
369 fn handle_store(
370 _menu: &menu::Menu<Self, P::Settings>,
371 item: &menu::Item<Self, P::Settings>,
372 args: &[&str],
373 interface: &mut Self,
374 settings: &mut P::Settings,
375 ) {
376 let key = menu::argument_finder(item, args, "path").unwrap();
377 let force = menu::argument_finder(item, args, "force")
378 .unwrap()
379 .is_some();
380 Self::iter_root(
381 key,
382 interface,
383 settings,
384 |key, interface, settings, defaults| {
385 let slic =
387 ::postcard::ser_flavors::Slice::new(interface.buffer);
388 let mut check = match postcard::get_by_key(defaults, key, slic)
389 {
390 Ok(slic) => yafnv::fnv1a::<u32>(slic),
392 Err(SerdeError::Value(ValueError::Absent)) => {
393 log::warn!("Default absent: `{}`", key.0);
394 return;
395 }
396 Err(e) => {
397 writeln!(
398 interface,
399 "Failed to get `{}` default: {e:?}",
400 key.0
401 )
402 .unwrap();
403 return;
404 }
405 };
406
407 match interface
409 .platform
410 .fetch(interface.buffer, key.0.as_bytes())
411 {
412 Ok(None) => {}
413 Ok(Some(stored)) => {
414 let stored = yafnv::fnv1a::<u32>(stored);
415 if stored != check {
416 log::debug!(
417 "Stored differs from default: `{}`",
418 key.0
419 );
420 } else {
421 log::debug!("Stored matches default: `{}`", key.0);
422 }
423 check = stored;
424 }
425 Err(e) => {
426 writeln!(
427 interface,
428 "Failed to fetch `{}`: {e:?}",
429 key.0
430 )
431 .unwrap();
432 }
433 }
434
435 let slic =
437 ::postcard::ser_flavors::Slice::new(interface.buffer);
438 let value = match postcard::get_by_key(settings, key, slic) {
439 Ok(value) => value,
440 Err(SerdeError::Value(ValueError::Absent)) => {
441 return;
442 }
443 Err(e) => {
444 writeln!(interface, "Could not get `{}`: {e}", key.0)
445 .unwrap();
446 return;
447 }
448 };
449
450 if yafnv::fnv1a::<u32>(value) == check && !force {
452 log::debug!(
453 "Not saving matching default/stored `{}`",
454 key.0
455 );
456 return;
457 }
458 let len = value.len();
459 let (value, rest) = interface.buffer.split_at_mut(len);
460
461 match interface.platform.store(rest, key.0.as_bytes(), value) {
463 Ok(_) => writeln!(interface, "`{}` stored", key.0),
464 Err(e) => {
465 writeln!(
466 interface,
467 "Failed to store `{}`: {e:?}",
468 key.0
469 )
470 }
471 }
472 .unwrap();
473 },
474 );
475 writeln!(interface, "Some values may require reboot to become active")
476 .unwrap();
477 }
478
479 fn handle_set(
480 _menu: &menu::Menu<Self, P::Settings>,
481 item: &menu::Item<Self, P::Settings>,
482 args: &[&str],
483 interface: &mut Self,
484 settings: &mut P::Settings,
485 ) {
486 let key = menu::argument_finder(item, args, "path").unwrap().unwrap();
487 let value =
488 menu::argument_finder(item, args, "value").unwrap().unwrap();
489
490 match json_core::set(settings, key, value.as_bytes()) {
492 Ok(_) => {
493 interface.updated = true;
494 writeln!(
495 interface,
496 "Set but not stored. May require store and reboot to activate."
497 )
498 }
499 Err(e) => {
500 writeln!(interface, "Failed to set `{key}`: {e:?}")
501 }
502 }
503 .unwrap();
504 }
505
506 fn menu() -> menu::Menu<'a, Self, P::Settings> {
507 menu::Menu {
508 label: "settings",
509 items: &[
510 &menu::Item {
511 command: "get",
512 help: Some(
513 "List paths and read current, default, and stored values",
514 ),
515 item_type: menu::ItemType::Callback {
516 function: Self::handle_get,
517 parameters: &[menu::Parameter::Optional {
518 parameter_name: "path",
519 help: Some(
520 "The path of the value or subtree to list/read.",
521 ),
522 }],
523 },
524 },
525 &menu::Item {
526 command: "set",
527 help: Some("Update a value"),
528 item_type: menu::ItemType::Callback {
529 function: Self::handle_set,
530 parameters: &[
531 menu::Parameter::Mandatory {
532 parameter_name: "path",
533 help: Some("The path to set"),
534 },
535 menu::Parameter::Mandatory {
536 parameter_name: "value",
537 help: Some(
538 "The value to be written, JSON-encoded",
539 ),
540 },
541 ],
542 },
543 },
544 &menu::Item {
545 command: "store",
546 help: Some("Store values that differ from defaults"),
547 item_type: menu::ItemType::Callback {
548 function: Self::handle_store,
549 parameters: &[
550 menu::Parameter::Named {
551 parameter_name: "force",
552 help: Some(
553 "Also store values that match defaults",
554 ),
555 },
556 menu::Parameter::Optional {
557 parameter_name: "path",
558 help: Some(
559 "The path of the value or subtree to store.",
560 ),
561 },
562 ],
563 },
564 },
565 &menu::Item {
566 command: "clear",
567 help: Some(
568 "Clear active to defaults and remove all stored values",
569 ),
570 item_type: menu::ItemType::Callback {
571 function: Self::handle_clear,
572 parameters: &[menu::Parameter::Optional {
573 parameter_name: "path",
574 help: Some(
575 "The path of the value or subtree to clear",
576 ),
577 }],
578 },
579 },
580 &menu::Item {
581 command: "platform",
582 help: Some("Platform specific commands"),
583 item_type: menu::ItemType::Callback {
584 function: Self::handle_platform,
585 parameters: &[menu::Parameter::Mandatory {
586 parameter_name: "cmd",
587 help: Some(
588 "The name of the command (e.g. `reboot`, `service`, `dfu`).",
589 ),
590 }],
591 },
592 },
593 ],
594 entry: None,
595 exit: None,
596 }
597 }
598}
599
600impl<P: Platform> core::fmt::Write for Interface<'_, P> {
601 fn write_str(&mut self, s: &str) -> core::fmt::Result {
602 self.platform
603 .interface_mut()
604 .write_all(s.as_bytes())
605 .or(Err(core::fmt::Error))
606 }
607}
608
609impl<P: Platform> ErrorType for Interface<'_, P> {
610 type Error = <P::Interface as ErrorType>::Error;
611}
612
613impl<P: Platform> Write for Interface<'_, P> {
614 fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
615 self.platform.interface_mut().write(buf)
616 }
617
618 fn flush(&mut self) -> Result<(), Self::Error> {
619 self.platform.interface_mut().flush()
620 }
621}
622
623pub const MAX_DEPTH: usize = 16;
625
626pub struct Runner<'a, P: Platform>(
628 menu::Runner<'a, Interface<'a, P>, P::Settings, [u8]>,
629);
630
631impl<'a, P: Platform> Runner<'a, P> {
632 pub fn new(
644 platform: P,
645 line_buf: &'a mut [u8],
646 serialize_buf: &'a mut [u8],
647 settings: &mut P::Settings,
648 ) -> Result<Self, P::Error> {
649 assert!(P::Settings::SCHEMA.shape().max_depth <= MAX_DEPTH);
650 Ok(Self(menu::Runner::new(
651 Interface::menu(),
652 line_buf,
653 Interface {
654 platform,
655 buffer: serialize_buf,
656 updated: false,
657 },
658 settings,
659 )))
660 }
661
662 pub fn interface_mut(&mut self) -> &mut P::Interface {
664 self.0.interface.platform.interface_mut()
665 }
666
667 pub fn platform_mut(&mut self) -> &mut P {
668 &mut self.0.interface.platform
669 }
670
671 pub fn platform(&mut self) -> &P {
672 &self.0.interface.platform
673 }
674
675 pub fn poll(
680 &mut self,
681 settings: &mut P::Settings,
682 ) -> Result<bool, <P::Interface as embedded_io::ErrorType>::Error> {
683 self.0.interface.updated = false;
684
685 while self.interface_mut().read_ready()? {
686 let mut buffer = [0u8; 64];
687 let count = self.interface_mut().read(&mut buffer)?;
688 for &value in &buffer[..count] {
689 self.0.input_byte(value, settings);
690 }
691 }
692
693 Ok(self.0.interface.updated)
694 }
695}