1use yrs::{Any, Map, MapRef, TransactionMut};
2
3use crate::error::ChatResult;
4use crate::history::types::ChatMessage;
5
6pub fn append_message(
7 txn: &mut TransactionMut,
8 map: &MapRef,
9 msg: &ChatMessage,
10) -> ChatResult<bool> {
11 if map.get(txn, &msg.id).is_some() {
12 return Ok(false);
13 }
14
15 let json = serde_json::to_value(msg)?;
16 let any = json_to_yrs_any(json);
17 map.insert(txn, msg.id.clone(), any);
18
19 Ok(true)
20}
21
22fn json_to_yrs_any(value: serde_json::Value) -> Any {
23 match value {
24 serde_json::Value::Null => Any::Null,
25 serde_json::Value::Bool(b) => Any::Bool(b),
26 serde_json::Value::Number(n) => Any::Number(n.as_f64().unwrap_or(0.0)),
27 serde_json::Value::String(s) => Any::String(s.into()),
28 serde_json::Value::Array(arr) => {
29 let items: Vec<Any> = arr.into_iter().map(json_to_yrs_any).collect();
30 Any::Array(items.into())
31 }
32 serde_json::Value::Object(map) => {
33 let mut result = std::collections::HashMap::new();
34 for (k, v) in map {
35 result.insert(k, json_to_yrs_any(v));
36 }
37 Any::Map(result.into())
38 }
39 }
40}
41
42use yrs::Out;
43
44pub fn message_from_any(out: &Out) -> ChatResult<ChatMessage> {
45 let any = out_to_any(out);
46 let json = yrs_any_to_json(&any);
47 let msg = serde_json::from_value(json)?;
48 Ok(msg)
49}
50
51fn out_to_any(out: &Out) -> Any {
52 match out {
53 Out::Any(any) => any.clone(),
54 Out::YXmlText(_) | Out::YXmlElement(_) | Out::YXmlFragment(_) | Out::YDoc(_) => Any::Null,
55 Out::YArray(_) => Any::Null,
56 Out::YMap(_) => Any::Null,
57 Out::YText(_) => Any::Null,
58 Out::UndefinedRef(_) => Any::Null,
59 }
60}
61
62fn yrs_any_to_json(any: &Any) -> serde_json::Value {
63 match any {
64 Any::Null => serde_json::Value::Null,
65 Any::Undefined => serde_json::Value::Null,
66 Any::Bool(b) => serde_json::Value::Bool(*b),
67 Any::Number(n) => serde_json::Value::Number(
68 serde_json::Number::from_f64(*n).unwrap_or(serde_json::Number::from(0)),
69 ),
70 Any::BigInt(n) => serde_json::Value::Number(serde_json::Number::from(*n)),
71 Any::String(s) => serde_json::Value::String(s.to_string()),
72 Any::Array(arr) => {
73 let items: Vec<serde_json::Value> = arr.iter().map(yrs_any_to_json).collect();
74 serde_json::Value::Array(items)
75 }
76 Any::Map(map) => {
77 let mut obj = serde_json::Map::new();
78 for (k, v) in map.iter() {
79 obj.insert(k.to_string(), yrs_any_to_json(v));
80 }
81 serde_json::Value::Object(obj)
82 }
83 Any::Buffer(buf) => serde_json::Value::Array(
84 buf.iter().map(|b| serde_json::Value::Number(serde_json::Number::from(*b))).collect(),
85 ),
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92 use crate::history::types::ChatMessage;
93 use yrs::{Doc, Map, Transact};
94
95 fn create_message(id: &str, content: &str) -> ChatMessage {
96 ChatMessage {
97 id: id.into(),
98 peer_id: "peerA".into(),
99 stable_sender_id: "sender-a".into(),
100 nickname: "alice".into(),
101 content: content.into(),
102 timestamp: 1000.0,
103 seq: 0.0,
104 parent_ids: vec![],
105 circuit_address: None,
106 web_rtc_address: None,
107 }
108 }
109
110 #[test]
111 fn adds_new_message_to_map() {
112 let doc = Doc::new();
113 let map = doc.get_or_insert_map("messages");
114 let mut txn = doc.transact_mut();
115
116 let msg = create_message("msg1", "hello");
117 let inserted = append_message(&mut txn, &map, &msg).unwrap();
118 assert!(inserted);
119 assert_eq!(map.len(&txn), 1);
120
121 let stored_any = map.get(&txn, "msg1").unwrap();
122 let stored = message_from_any(&stored_any).unwrap();
123 assert_eq!(stored.content, "hello");
124 assert_eq!(stored.stable_sender_id, "sender-a");
125 }
126
127 #[test]
128 fn is_no_op_for_duplicate_id() {
129 let doc = Doc::new();
130 let map = doc.get_or_insert_map("messages");
131
132 let msg1 = create_message("msg1", "hello");
133 {
134 let mut txn = doc.transact_mut();
135 append_message(&mut txn, &map, &msg1).unwrap();
136 }
137
138 let msg2 = ChatMessage {
139 id: "msg1".into(),
140 peer_id: "peerB".into(),
141 stable_sender_id: "sender-b".into(),
142 nickname: "bob".into(),
143 content: "world".into(),
144 timestamp: 2000.0,
145 seq: 1.0,
146 parent_ids: vec![],
147 circuit_address: None,
148 web_rtc_address: None,
149 };
150 {
151 let mut txn = doc.transact_mut();
152 let inserted = append_message(&mut txn, &map, &msg2).unwrap();
153 assert!(!inserted);
154 }
155
156 let txn = doc.transact();
157 assert_eq!(map.len(&txn), 1);
158 let stored_any = map.get(&txn, "msg1").unwrap();
159 let stored = message_from_any(&stored_any).unwrap();
160 assert_eq!(stored.content, "hello");
161 assert_eq!(stored.stable_sender_id, "sender-a");
162 }
163
164 #[test]
165 fn stores_exact_message_fields() {
166 let doc = Doc::new();
167 let map = doc.get_or_insert_map("messages");
168 let mut txn = doc.transact_mut();
169
170 let msg = ChatMessage {
171 id: "msg2".into(),
172 peer_id: "peerA".into(),
173 stable_sender_id: "sender-a".into(),
174 nickname: "alice".into(),
175 content: "test".into(),
176 timestamp: 1000.0,
177 seq: 5.0,
178 parent_ids: vec!["msg1".into()],
179 circuit_address: None,
180 web_rtc_address: None,
181 };
182 append_message(&mut txn, &map, &msg).unwrap();
183
184 let stored_any = map.get(&txn, "msg2").unwrap();
185 let stored = message_from_any(&stored_any).unwrap();
186 assert_eq!(stored.seq, 5.0);
187 assert_eq!(stored.parent_ids, vec!["msg1"]);
188 }
189}