diff --git a/humility-doppel/src/lib.rs b/humility-doppel/src/lib.rs index 4992b34e1..f2b5d7e34 100644 --- a/humility-doppel/src/lib.rs +++ b/humility-doppel/src/lib.rs @@ -488,16 +488,44 @@ impl humility::reflect::Load for CountedRingbuf { impl humility::reflect::Load for Counters { fn from_value(v: &Value) -> Result { - let count_struct = v.as_struct()?; - let counts = count_struct - .iter() - .map(|(name, value)| { - let value = CounterVariant::from_value(value) - .with_context(|| format!("failed to read {name}"))?; - Ok((name.to_string(), value)) - }) - .collect::>>()?; - Ok(Self { counts }) + // https://github.com/oxidecomputer/hubris/commit/b6ba19bcf644bba1b22b3ca8e3e88b8e64795eed + // added a manual implementation of counters that are an array of + // `AtomicU32`s, rather than a struct of `AtomicU32`s. We will likely + // hope to support this as a first-class derive option. + let result = if let Ok(count_struct) = v.as_struct() { + // For now, we first try to see if the counter impl is a struct + // containing `AtomicU32`s... + count_struct + .iter() + .map(|(name, value)| { + let value = CounterVariant::from_value(value) + .with_context(|| format!("failed to read {name}"))?; + Ok((name.to_string(), value)) + }) + .collect::>>() + .map(|counts| Self { counts }) + } else if let Ok(arr) = v.as_array() { + // Failing that, we check to see if the counters are instead in an + // array shape. + arr.iter() + .enumerate() + .map(|(idx, value)| { + let value = CounterVariant::from_value(value) + .with_context(|| format!("failed to read {idx}"))?; + Ok((idx.to_string(), value)) + }) + .collect::>>() + .map(|counts| Self { counts }) + } else { + // If it's not a struct or array of purely AtomicU32s, then there's + // not much we can do. Attempt to provide a helpful error. + Err(anyhow::format_err!( + "Attempted to load counters, but encountered an unexpected form\ + that was neither a struct nor an array of AtomicU32s" + )) + }; + + result.with_context(|| "Failed to load counters") } } @@ -581,13 +609,18 @@ impl humility::reflect::Load for CounterVariant { return Ok(Self::Single(0)); } - let counter = value.as_struct()?; - if counter.name().starts_with("AtomicU32") { + // If the value is directly an AtomicU32, we treat it as a single + // counter value. + if let Ok(counter) = value.as_struct() + && counter.name().starts_with("AtomicU32") + { let cell = UnsafeCell::::from_value(&counter["v"]) .context("ringbuf count must be a u32")?; return Ok(Self::Single(cell.value)); } + // If it's not a (single) `AtomicU32`, it may be a struct or array of + // `AtomicU32`s, so treat it as nested and attempt to load it as such. Ok(Self::Nested(Counters::from_value(value)?)) } }