1use std::path::Path;
2
3use loro::LoroDoc;
4
5const STATE_FILE: &str = "meshlet-server.state";
6
7pub struct ServerDoc {
8 doc: LoroDoc,
9}
10
11impl ServerDoc {
12 pub fn load_or_create(dir: &Path) -> Self {
13 let state_path = dir.join(STATE_FILE);
14
15 if let Ok(data) = std::fs::read(&state_path)
16 && let Ok(doc) = LoroDoc::from_snapshot(&data)
17 {
18 return Self { doc };
19 }
20
21 let doc = LoroDoc::new();
22 doc.set_record_timestamp(true);
23 doc.set_peer_id(0).ok();
24 Self { doc }
25 }
26
27 pub fn save(&self, dir: &Path) -> anyhow::Result<()> {
28 let state_path = dir.join(STATE_FILE);
29 let snapshot = self
30 .doc
31 .export(loro::ExportMode::Snapshot)
32 .map_err(|e| anyhow::anyhow!("export failed: {}", e))?;
33
34 std::fs::create_dir_all(dir)?;
35 std::fs::write(&state_path, snapshot)?;
36 tracing::info!("saved server state to {:?}", state_path);
37 Ok(())
38 }
39
40 pub fn import(&self, data: &[u8]) -> anyhow::Result<()> {
41 self.doc
42 .import(data)
43 .map_err(|e| anyhow::anyhow!("import failed: {}", e))?;
44 Ok(())
45 }
46
47 pub fn export_updates_since(
48 &self,
49 vv: &loro::VersionVector,
50 ) -> anyhow::Result<Vec<u8>> {
51 self.doc
52 .export(loro::ExportMode::Updates {
53 from: std::borrow::Cow::Borrowed(vv),
54 })
55 .map_err(|e| anyhow::anyhow!("export failed: {}", e))
56 }
57
58 pub fn oplog_vv(&self) -> loro::VersionVector {
59 self.doc.oplog_vv().clone()
60 }
61}