Skip to main content

fakecloud_iotdata/
state.rs

1//! Account-partitioned, serializable state for the AWS IoT Data Plane
2//! (`iotdata`).
3//!
4//! Two stores per account:
5//!
6//! * `shadows` — device shadows, keyed by `thingName`. Each thing owns a map
7//!   of `shadowName` -> shadow document, where the empty string `""` is the
8//!   classic (unnamed) shadow and any other key is a named shadow. Every key
9//!   is a plain `String`, so the snapshot never depends on the tuple-key serde
10//!   adapter.
11//! * `retained` — retained MQTT messages, keyed by `topic`. The stored value
12//!   carries the base64 payload plus `qos` / `lastModifiedTime`.
13
14use std::collections::BTreeMap;
15use std::sync::Arc;
16
17use parking_lot::RwLock;
18use serde::{Deserialize, Serialize};
19use serde_json::Value;
20
21use fakecloud_core::multi_account::{AccountState, MultiAccountState};
22
23pub const IOTDATA_SNAPSHOT_SCHEMA_VERSION: u32 = 1;
24
25/// The classic (unnamed) shadow is stored under this reserved shadow-name key.
26pub const CLASSIC_SHADOW_KEY: &str = "";
27
28/// Per-account IoT Data Plane state.
29#[derive(Debug, Clone, Default, Serialize, Deserialize)]
30pub struct IotDataData {
31    /// Device shadows keyed by `thingName`, then by `shadowName` (with the
32    /// empty string denoting the classic shadow). The stored value is the full
33    /// internal shadow document (`state` / `metadata` / `version` /
34    /// `timestamp`).
35    #[serde(default)]
36    pub shadows: BTreeMap<String, BTreeMap<String, Value>>,
37    /// Retained MQTT messages keyed by `topic`. The stored value carries the
38    /// base64 `payload`, `qos`, and `lastModifiedTime` (epoch millis).
39    #[serde(default)]
40    pub retained: BTreeMap<String, Value>,
41}
42
43impl AccountState for IotDataData {
44    fn new_for_account(_account_id: &str, _region: &str, _endpoint: &str) -> Self {
45        Self::default()
46    }
47}
48
49pub type SharedIotDataState = Arc<RwLock<MultiAccountState<IotDataData>>>;
50
51#[derive(Debug, Serialize, Deserialize)]
52pub struct IotDataSnapshot {
53    pub schema_version: u32,
54    pub accounts: MultiAccountState<IotDataData>,
55}
56
57#[cfg(test)]
58mod tests {
59    use super::*;
60
61    #[test]
62    fn new_account_is_empty() {
63        let data = IotDataData::new_for_account("000000000000", "us-east-1", "");
64        assert!(data.shadows.is_empty());
65        assert!(data.retained.is_empty());
66    }
67}