Skip to main content

mcumgr_toolkit/client/
image_run_state.rs

1use crate::commands::image::ImageState;
2
3/// The current run state of the given image.
4#[derive(Clone, Debug, Eq, PartialEq)]
5pub enum ImageRunState<'a> {
6    /// The given slot is likely the current and future
7    /// image on the system.
8    Stable(&'a ImageState),
9    /// The image will change at next boot.
10    Pending {
11        /// Currently running/active image, if known.
12        current: Option<&'a ImageState>,
13        /// The slot that will run after next boot.
14        ///
15        /// Examine `next.permanent` to see whether
16        /// the image will boot in testing or permanent
17        /// mode.
18        next: &'a ImageState,
19    },
20    /// A new image is currently being tested.
21    ///
22    /// Unless it gets confirmed, it will be
23    /// rolled back at next boot.
24    Testing {
25        /// The currently tested image.
26        current: &'a ImageState,
27        /// The image that will get rolled back to.
28        fallback: &'a ImageState,
29    },
30    /// The current state is not easily determinable.
31    ///
32    /// Will contain an educated guess if possible,
33    /// but by no means this guess should be relied upon.
34    Unknown(Option<&'a ImageState>),
35    /// The image state is inconsistent.
36    ///
37    /// Example: The image has multiple slots that are marked 'active'.
38    Inconsistent,
39}
40
41/// Analyze the flags of the image slots with the given image ID and determine the most likely run state.
42pub fn analyze(image_state: &[ImageState], image_id: u32) -> ImageRunState<'_> {
43    fn find_unique(
44        image_id: u32,
45        image_state: &[ImageState],
46        predicate: impl Fn(&ImageState) -> bool,
47    ) -> Option<Option<&'_ ImageState>> {
48        let mut matches = image_state
49            .iter()
50            .filter(|img| img.image == image_id)
51            .filter(|img| predicate(img));
52
53        let first = matches.next();
54
55        match matches.next() {
56            Some(_) => None,     // invalid: duplicate
57            None => Some(first), // valid: zero or one
58        }
59    }
60
61    let active = match find_unique(image_id, image_state, |img| img.active) {
62        Some(value) => value,
63        None => return ImageRunState::Inconsistent,
64    };
65
66    let confirmed = match find_unique(image_id, image_state, |img| img.confirmed) {
67        Some(value) => value,
68        None => return ImageRunState::Inconsistent,
69    };
70
71    let pending = match find_unique(image_id, image_state, |img| img.pending) {
72        Some(value) => value,
73        None => return ImageRunState::Inconsistent,
74    };
75
76    if let Some(pending) = pending {
77        return ImageRunState::Pending {
78            current: active.or(confirmed),
79            next: pending,
80        };
81    }
82
83    match (active, confirmed) {
84        (Some(active), Some(confirmed)) if active.slot == confirmed.slot => {
85            ImageRunState::Stable(active)
86        }
87        (Some(active), Some(confirmed)) => ImageRunState::Testing {
88            current: active,
89            fallback: confirmed,
90        },
91        (Some(active), None) => ImageRunState::Stable(active),
92        (None, Some(confirmed)) => ImageRunState::Stable(confirmed),
93        (None, None) => {
94            // Probably MCUboot with image infos disabled, guess slot 0
95            ImageRunState::Unknown(
96                image_state
97                    .iter()
98                    .filter(|img| img.image == image_id)
99                    .find(|img| img.slot == 0),
100            )
101        }
102    }
103}