lashlang/runtime/
state.rs1use std::sync::Arc;
2
3use super::{
4 ProjectedValue, Record, SnapshotJson, Value, image_from_json_map, record_with_capacity,
5 resource_from_json_map,
6};
7use serde::ser::SerializeStruct;
8use serde::{Deserialize, Deserializer, Serialize, Serializer};
9
10const PROJECTED_SNAPSHOT_TAG: &str = "__lashlang_snapshot_projected__";
11const PROJECTED_SNAPSHOT_NAME: &str = "name";
12const PROJECTED_SNAPSHOT_TYPE_NAME: &str = "type_name";
13const PROJECTED_SNAPSHOT_PROJECTION_REF: &str = "projection_ref";
14const TUPLE_SNAPSHOT_TAG: &str = "__lashlang_snapshot_tuple__";
15const TUPLE_SNAPSHOT_ITEMS: &str = "items";
16
17#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
18pub struct State {
19 pub(super) globals: Record,
20}
21
22impl State {
23 pub fn new() -> Self {
24 Self::default()
25 }
26
27 pub fn globals(&self) -> &Record {
28 &self.globals
29 }
30
31 pub fn snapshot(&self) -> Snapshot {
32 Snapshot {
33 globals: self.globals.clone(),
34 }
35 }
36
37 pub fn from_snapshot(snapshot: Snapshot) -> Self {
38 Self {
39 globals: snapshot.globals,
40 }
41 }
42}
43
44#[derive(Clone, Debug, Default, PartialEq)]
45pub struct Snapshot {
46 pub globals: Record,
47}
48
49impl Serialize for Snapshot {
50 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
51 where
52 S: Serializer,
53 {
54 let mut state = serializer.serialize_struct("Snapshot", 1)?;
55 state.serialize_field(
56 "globals",
57 &SnapshotJson(&Value::Record(Arc::new(self.globals.clone()))),
58 )?;
59 state.end()
60 }
61}
62
63impl<'de> Deserialize<'de> for Snapshot {
64 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
65 where
66 D: Deserializer<'de>,
67 {
68 #[derive(Deserialize)]
69 struct SnapshotWire {
70 globals: serde_json::Map<String, serde_json::Value>,
71 }
72
73 let wire = SnapshotWire::deserialize(deserializer)?;
74 Ok(Self {
75 globals: snapshot_record_from_json(wire.globals),
76 })
77 }
78}
79
80fn snapshot_record_from_json(map: serde_json::Map<String, serde_json::Value>) -> Record {
81 let mut record = record_with_capacity(map.len());
82 for (key, value) in map {
83 record.insert(key, snapshot_value_from_json(value));
84 }
85 record
86}
87
88fn snapshot_value_from_json(value: serde_json::Value) -> Value {
89 match value {
90 serde_json::Value::Null => Value::Null,
91 serde_json::Value::Bool(value) => Value::Bool(value),
92 serde_json::Value::Number(value) => Value::Number(value.as_f64().unwrap_or_default()),
93 serde_json::Value::String(value) => Value::String(value.into()),
94 serde_json::Value::Array(values) => Value::List(
95 values
96 .into_iter()
97 .map(snapshot_value_from_json)
98 .collect::<Vec<_>>()
99 .into(),
100 ),
101 serde_json::Value::Object(map) => {
102 if let Some(items) = tuple_snapshot_marker(&map) {
103 return Value::Tuple(
104 items
105 .into_iter()
106 .map(snapshot_value_from_json)
107 .collect::<Vec<_>>()
108 .into(),
109 );
110 }
111 if let Some((name, type_name, projection_ref)) = projected_snapshot_marker(&map) {
112 return Value::Projected(
113 ProjectedValue::unavailable_after_restore_with_projection_ref(
114 name,
115 type_name,
116 projection_ref,
117 ),
118 );
119 }
120 image_from_json_map(&map)
121 .map(Value::Image)
122 .or_else(|| resource_from_json_map(&map).map(Value::Resource))
123 .unwrap_or_else(|| Value::Record(Arc::new(snapshot_record_from_json(map))))
124 }
125 }
126}
127
128fn tuple_snapshot_marker(
129 map: &serde_json::Map<String, serde_json::Value>,
130) -> Option<Vec<serde_json::Value>> {
131 if map.len() != 2 {
132 return None;
133 }
134 if !map.get(TUPLE_SNAPSHOT_TAG)?.as_bool()? {
135 return None;
136 }
137 let serde_json::Value::Array(items) = map.get(TUPLE_SNAPSHOT_ITEMS)?.clone() else {
138 return None;
139 };
140 Some(items)
141}
142
143fn projected_snapshot_marker(
144 map: &serde_json::Map<String, serde_json::Value>,
145) -> Option<(String, String, Option<serde_json::Value>)> {
146 if map.len() != 3 && map.len() != 4 {
147 return None;
148 }
149 if !map.get(PROJECTED_SNAPSHOT_TAG)?.as_bool()? {
150 return None;
151 }
152 let name = map.get(PROJECTED_SNAPSHOT_NAME)?.as_str()?.to_string();
153 let type_name = map.get(PROJECTED_SNAPSHOT_TYPE_NAME)?.as_str()?.to_string();
154 let projection_ref = map.get(PROJECTED_SNAPSHOT_PROJECTION_REF).cloned();
155 Some((name, type_name, projection_ref))
156}