use crate::yjs_compat::lib0;
use crate::Text;
const TEXT_TYPE_NAME: &str = "abyo";
const INFO_KEEP: u8 = 0;
const INFO_HAS_ORIGIN: u8 = 0b1000_0000;
#[allow(dead_code)]
const INFO_HAS_RIGHT_ORIGIN: u8 = 0b0100_0000;
const INFO_HAS_PARENT_INFO: u8 = 0b0010_0000;
#[allow(dead_code)]
const INFO_HAS_PARENT_SUB: u8 = 0b0001_0000;
const CONTENT_STRING: u8 = 4;
#[must_use]
pub fn snapshot_text_to_yjs_update(text: &Text) -> Vec<u8> {
let s: String = text.iter_with_marks().map(|(c, _)| c).collect();
snapshot_string_to_yjs_update(&s, text.replica_id())
}
#[must_use]
pub fn snapshot_string_to_yjs_update(s: &str, client_id: u64) -> Vec<u8> {
let mut out = Vec::with_capacity(s.len() * 4 + 32);
let chars: Vec<char> = s.chars().collect();
let num_clients: u64 = u64::from(!chars.is_empty());
lib0::write_var_uint(&mut out, num_clients);
if !chars.is_empty() {
lib0::write_var_uint(&mut out, client_id);
lib0::write_var_uint(&mut out, chars.len() as u64);
for (i, ch) in chars.iter().enumerate() {
let mut info = INFO_KEEP | CONTENT_STRING;
if i > 0 {
info |= INFO_HAS_ORIGIN;
} else {
info |= INFO_HAS_PARENT_INFO;
}
out.push(info);
if i > 0 {
lib0::write_var_uint(&mut out, client_id);
lib0::write_var_uint(&mut out, (i - 1) as u64);
}
if i == 0 {
out.push(1u8);
lib0::write_var_string(&mut out, TEXT_TYPE_NAME);
}
let mut buf = [0u8; 4];
let ch_str = ch.encode_utf8(&mut buf);
lib0::write_var_string(&mut out, ch_str);
}
}
lib0::write_var_uint(&mut out, 0);
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_text_encodes_to_empty_update() {
let text = Text::new(1);
let bytes = snapshot_text_to_yjs_update(&text);
assert_eq!(bytes, vec![0, 0]);
}
#[test]
fn single_char_encoding_smoke() {
let bytes = snapshot_string_to_yjs_update("a", 42);
assert_eq!(bytes[0], 1, "num_clients");
assert_eq!(bytes[1], 42, "client_id");
assert_eq!(bytes[2], 1, "num_structs");
assert_eq!(bytes[3], INFO_HAS_PARENT_INFO | CONTENT_STRING, "info byte");
assert_eq!(bytes[4], 1);
assert_eq!(&bytes[5..10], &[4, b'a', b'b', b'y', b'o']);
assert_eq!(&bytes[10..12], &[1, b'a']);
assert_eq!(bytes[12], 0);
assert_eq!(bytes.len(), 13);
}
#[test]
fn multi_char_chain() {
let bytes = snapshot_string_to_yjs_update("abc", 1);
assert!(bytes.len() > 10);
assert_eq!(bytes[0], 1);
assert_eq!(bytes[1], 1); assert_eq!(bytes[2], 3); }
#[test]
fn snapshot_text_uses_replica_id_as_client() {
let mut text = Text::new(7);
text.insert_str(0, "hi");
let bytes = snapshot_text_to_yjs_update(&text);
assert_eq!(bytes[0], 1);
assert_eq!(bytes[1], 7);
assert_eq!(bytes[2], 2);
}
}