1use lasso::{Key, Spur, ThreadedRodeo};
18use serde::{Deserialize, Serialize};
19
20use gruel_parser::ast::Ast;
21use gruel_rir::Rir;
22
23#[derive(Debug, Clone, Default, Serialize, Deserialize)]
28pub struct InternerSnapshot {
29 pub strings: Vec<String>,
30}
31
32impl InternerSnapshot {
33 pub fn capture(interner: &ThreadedRodeo) -> Self {
39 let mut pairs: Vec<(usize, String)> = interner
42 .iter()
43 .map(|(spur, s)| (spur.into_usize(), s.to_string()))
44 .collect();
45 pairs.sort_by_key(|(idx, _)| *idx);
46
47 for (expected, (actual, _)) in pairs.iter().enumerate() {
50 debug_assert_eq!(
51 expected, *actual,
52 "ThreadedRodeo Spurs not contiguous starting at 0; \
53 cache assumes lasso's standard packing"
54 );
55 }
56
57 Self {
58 strings: pairs.into_iter().map(|(_, s)| s).collect(),
59 }
60 }
61
62 pub fn restore_into(&self, target: &ThreadedRodeo) -> Vec<Spur> {
69 self.strings
70 .iter()
71 .map(|s| target.get_or_intern(s))
72 .collect()
73 }
74}
75
76#[derive(Debug, Clone, Serialize, Deserialize)]
79pub struct CachedParseOutput {
80 pub interner: InternerSnapshot,
81 pub ast: Ast,
82}
83
84impl CachedParseOutput {
85 pub fn encode(&self) -> Result<Vec<u8>, bincode::error::EncodeError> {
87 bincode::serde::encode_to_vec(self, bincode::config::standard())
88 }
89
90 pub fn decode(bytes: &[u8]) -> Result<Self, bincode::error::DecodeError> {
93 let (out, _read) = bincode::serde::decode_from_slice(bytes, bincode::config::standard())?;
94 Ok(out)
95 }
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize)]
101pub struct CachedRirOutput {
102 pub interner: InternerSnapshot,
103 pub rir: Rir,
104}
105
106impl CachedRirOutput {
107 pub fn encode(&self) -> Result<Vec<u8>, bincode::error::EncodeError> {
108 bincode::serde::encode_to_vec(self, bincode::config::standard())
109 }
110
111 pub fn decode(bytes: &[u8]) -> Result<Self, bincode::error::DecodeError> {
112 let (out, _read) = bincode::serde::decode_from_slice(bytes, bincode::config::standard())?;
113 Ok(out)
114 }
115}
116
117#[cfg(test)]
118mod tests {
119 use super::*;
120
121 #[test]
122 fn snapshot_captures_strings_in_spur_order() {
123 let interner = ThreadedRodeo::new();
124 let s_a = interner.get_or_intern("alpha");
125 let s_b = interner.get_or_intern("beta");
126 let s_c = interner.get_or_intern("gamma");
127
128 let snapshot = InternerSnapshot::capture(&interner);
129 assert_eq!(snapshot.strings.len(), 3);
130 assert_eq!(snapshot.strings[s_a.into_usize()], "alpha");
131 assert_eq!(snapshot.strings[s_b.into_usize()], "beta");
132 assert_eq!(snapshot.strings[s_c.into_usize()], "gamma");
133 }
134
135 #[test]
136 fn snapshot_round_trips_through_bincode() {
137 let interner = ThreadedRodeo::new();
138 interner.get_or_intern("hello");
139 interner.get_or_intern("world");
140 let snap = InternerSnapshot::capture(&interner);
141
142 let encoded = bincode::serde::encode_to_vec(&snap, bincode::config::standard()).unwrap();
143 let (decoded, _): (InternerSnapshot, _) =
144 bincode::serde::decode_from_slice(&encoded, bincode::config::standard()).unwrap();
145 assert_eq!(decoded.strings, snap.strings);
146 }
147
148 #[test]
149 fn restore_reinterns_strings_into_target() {
150 let src = ThreadedRodeo::new();
152 let s_x = src.get_or_intern("x");
153 let s_y = src.get_or_intern("y");
154 let s_z = src.get_or_intern("z");
155 let snap = InternerSnapshot::capture(&src);
156
157 let tgt = ThreadedRodeo::new();
159 let pre_y = tgt.get_or_intern("y");
160
161 let remap = snap.restore_into(&tgt);
162
163 assert_eq!(remap[s_y.into_usize()], pre_y);
165 assert_eq!(tgt.resolve(&remap[s_x.into_usize()]), "x");
170 assert_eq!(tgt.resolve(&remap[s_z.into_usize()]), "z");
171 }
172
173 #[test]
174 fn empty_ast_round_trips() {
175 let cached = CachedParseOutput {
176 interner: InternerSnapshot::default(),
177 ast: Ast {
178 module_doc: None,
179 items: Vec::new(),
180 },
181 };
182 let bytes = cached.encode().unwrap();
183 let decoded = CachedParseOutput::decode(&bytes).unwrap();
184 assert!(decoded.ast.items.is_empty());
185 assert!(decoded.interner.strings.is_empty());
186 }
187}