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