feat(y-octo): import y-octo monorepo (#11750)

This commit is contained in:
Brooooooklyn
2025-04-21 02:51:15 +00:00
parent e3973538e8
commit 95dbda24fc
127 changed files with 17319 additions and 18 deletions

View File

@@ -0,0 +1,78 @@
use super::*;
#[cfg(test)]
mod tests {
use super::*;
use lib0::encoding::Write;
fn test_var_uint_enc_dec(num: u64) {
let mut buf1 = Vec::new();
write_var_u64(&mut buf1, num).unwrap();
let mut buf2 = Vec::new();
buf2.write_var(num);
{
let (rest, decoded_num) = read_var_u64(&buf1).unwrap();
assert_eq!(num, decoded_num);
assert_eq!(rest.len(), 0);
}
{
let (rest, decoded_num) = read_var_u64(&buf2).unwrap();
assert_eq!(num, decoded_num);
assert_eq!(rest.len(), 0);
}
}
fn test_var_int_enc_dec(num: i32) {
{
let mut buf1: Vec<u8> = Vec::new();
write_var_i32(&mut buf1, num).unwrap();
let (rest, decoded_num) = read_var_i32(&buf1).unwrap();
assert_eq!(num, decoded_num);
assert_eq!(rest.len(), 0);
}
{
let mut buf2 = Vec::new();
buf2.write_var(num);
let (rest, decoded_num) = read_var_i32(&buf2).unwrap();
assert_eq!(num, decoded_num);
assert_eq!(rest.len(), 0);
}
}
#[test]
fn test_var_uint_codec() {
test_var_uint_enc_dec(0);
test_var_uint_enc_dec(1);
test_var_uint_enc_dec(127);
test_var_uint_enc_dec(0b1000_0000);
test_var_uint_enc_dec(0b1_0000_0000);
test_var_uint_enc_dec(0b1_1111_1111);
test_var_uint_enc_dec(0b10_0000_0000);
test_var_uint_enc_dec(0b11_1111_1111);
test_var_uint_enc_dec(0x7fff_ffff_ffff_ffff);
test_var_uint_enc_dec(u64::max_value());
}
#[test]
fn test_var_int() {
test_var_int_enc_dec(0);
test_var_int_enc_dec(1);
test_var_int_enc_dec(-1);
test_var_int_enc_dec(63);
test_var_int_enc_dec(-63);
test_var_int_enc_dec(64);
test_var_int_enc_dec(-64);
test_var_int_enc_dec(i32::MAX);
test_var_int_enc_dec(i32::MIN);
test_var_int_enc_dec(((1 << 20) - 1) * 8);
test_var_int_enc_dec(-((1 << 20) - 1) * 8);
}
}

View File

@@ -0,0 +1,21 @@
#[cfg(test)]
mod tests {
use y_octo::Doc;
use yrs::{Map, Transact};
#[test]
fn test_basic_yrs_binary_compatibility() {
let yrs_doc = yrs::Doc::new();
let map = yrs_doc.get_or_insert_map("abc");
let mut trx = yrs_doc.transact_mut();
map.insert(&mut trx, "a", 1);
let binary_from_yrs = trx.encode_update_v1();
let doc = Doc::try_from_binary_v1(&binary_from_yrs).unwrap();
let binary = doc.encode_update_v1().unwrap();
assert_eq!(binary_from_yrs, binary);
}
}

View File

@@ -0,0 +1,5 @@
pub mod types;
pub mod yrs_op;
pub use types::*;
pub use yrs_op::*;

View File

@@ -0,0 +1,63 @@
use yrs::{ArrayRef, MapRef, TextRef, XmlFragmentRef, XmlTextRef};
pub const NEST_DATA_INSERT: &str = "insert";
pub const NEST_DATA_DELETE: &str = "delete";
pub const NEST_DATA_CLEAR: &str = "clear";
#[derive(Hash, PartialEq, Eq, Clone, Debug, arbitrary::Arbitrary)]
pub enum OpType {
HandleCurrent,
CreateCRDTNestType,
}
#[derive(Hash, PartialEq, Eq, Clone, Debug, arbitrary::Arbitrary)]
pub enum NestDataOpType {
Insert,
Delete,
Clear,
}
#[derive(PartialEq, Clone, Debug, arbitrary::Arbitrary)]
pub struct CRDTParam {
pub op_type: OpType,
pub new_nest_type: CRDTNestType,
pub manipulate_source: ManipulateSource,
pub insert_pos: InsertPos,
pub key: String,
pub value: String,
pub nest_data_op_type: NestDataOpType,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, arbitrary::Arbitrary)]
pub enum CRDTNestType {
Array,
Map,
Text,
XMLElement,
XMLFragment,
XMLText,
}
#[derive(Debug, Clone, PartialEq, arbitrary::Arbitrary)]
pub enum ManipulateSource {
NewNestTypeFromYDocRoot,
CurrentNestType,
NewNestTypeFromCurrent,
}
#[derive(Debug, Clone, PartialEq, arbitrary::Arbitrary)]
pub enum InsertPos {
BEGIN,
MID,
END,
}
#[derive(Clone)]
pub enum YrsNestType {
ArrayType(ArrayRef),
MapType(MapRef),
TextType(TextRef),
XMLElementType(XmlFragmentRef),
XMLFragmentType(XmlFragmentRef),
XMLTextType(XmlTextRef),
}

View File

@@ -0,0 +1,172 @@
use phf::phf_map;
use super::*;
fn insert_op(doc: &yrs::Doc, nest_input: &YrsNestType, params: CRDTParam) {
let array = match nest_input {
YrsNestType::ArrayType(array) => array,
_ => unreachable!(),
};
let mut trx = doc.transact_mut();
let len = array.len(&trx);
let index = random_pick_num(len, &params.insert_pos);
array.insert(&mut trx, index, params.value);
}
fn delete_op(doc: &yrs::Doc, nest_input: &YrsNestType, params: CRDTParam) {
let array = match nest_input {
YrsNestType::ArrayType(array) => array,
_ => unreachable!(),
};
let mut trx = doc.transact_mut();
let len = array.len(&trx);
if len >= 1 {
let index = random_pick_num(len - 1, &params.insert_pos);
array.remove(&mut trx, index);
}
}
fn clear_op(doc: &yrs::Doc, nest_input: &YrsNestType, _params: CRDTParam) {
let array = match nest_input {
YrsNestType::ArrayType(array) => array,
_ => unreachable!(),
};
let mut trx = doc.transact_mut();
let len = array.len(&trx);
for _ in 0..len {
array.remove(&mut trx, 0);
}
}
pub static ARRAY_OPS: TestOps = phf_map! {
"insert" => insert_op,
"delete" => delete_op,
"clear" => clear_op,
};
pub fn yrs_create_array_from_nest_type(
doc: &yrs::Doc,
current: &mut YrsNestType,
insert_pos: &InsertPos,
key: String,
) -> Option<ArrayRef> {
let cal_index = |len: u32| -> u32 {
match insert_pos {
InsertPos::BEGIN => 0,
InsertPos::MID => len / 2,
InsertPos::END => len,
}
};
let mut trx = doc.transact_mut();
let array_prelim = ArrayPrelim::default();
match current {
YrsNestType::ArrayType(array) => {
let index = cal_index(array.len(&trx));
Some(array.insert(&mut trx, index, array_prelim))
}
YrsNestType::MapType(map) => Some(map.insert(&mut trx, key, array_prelim)),
YrsNestType::TextType(text) => {
let str = text.get_string(&trx);
let len = str.chars().fold(0, |acc, _| acc + 1);
let index = random_pick_num(len, insert_pos) as usize;
let byte_start_offset = str
.chars()
.take(index)
.fold(0, |acc, ch| acc + ch.len_utf8());
Some(text.insert_embed(&mut trx, byte_start_offset as u32, array_prelim))
}
YrsNestType::XMLTextType(xml_text) => {
let str = xml_text.get_string(&trx);
let len = str.chars().fold(0, |acc, _| acc + 1);
let index = random_pick_num(len, insert_pos) as usize;
let byte_start_offset = str
.chars()
.take(index)
.fold(0, |acc, ch| acc + ch.len_utf8());
Some(xml_text.insert_embed(&mut trx, byte_start_offset as u32, array_prelim))
}
_ => None,
}
}
#[cfg(test)]
mod tests {
use yrs::Doc;
use super::*;
#[test]
fn test_gen_array_ref_ops() {
let doc = Doc::new();
let array_ref = doc.get_or_insert_array("test_array");
let ops_registry = OpsRegistry::new();
let mut params = CRDTParam {
op_type: OpType::CreateCRDTNestType,
new_nest_type: CRDTNestType::Array,
manipulate_source: ManipulateSource::NewNestTypeFromYDocRoot,
insert_pos: InsertPos::BEGIN,
key: String::from("test_key"),
value: String::from("test_value"),
nest_data_op_type: NestDataOpType::Insert,
};
ops_registry.operate_yrs_nest_type(
&doc,
YrsNestType::ArrayType(array_ref.clone()),
params.clone(),
);
assert_eq!(array_ref.len(&doc.transact()), 1);
params.nest_data_op_type = NestDataOpType::Delete;
ops_registry.operate_yrs_nest_type(
&doc,
YrsNestType::ArrayType(array_ref.clone()),
params.clone(),
);
assert_eq!(array_ref.len(&doc.transact()), 0);
params.nest_data_op_type = NestDataOpType::Clear;
ops_registry.operate_yrs_nest_type(
&doc,
YrsNestType::ArrayType(array_ref.clone()),
params.clone(),
);
assert_eq!(array_ref.len(&doc.transact()), 0);
}
#[test]
fn test_yrs_create_array_from_nest_type() {
let doc = Doc::new();
let array_ref = doc.get_or_insert_array("test_array");
let key = String::from("test_key");
let new_array_ref = yrs_create_array_from_nest_type(
&doc,
&mut YrsNestType::ArrayType(array_ref.clone()),
&InsertPos::BEGIN,
key.clone(),
);
assert!(new_array_ref.is_some());
let map_ref = doc.get_or_insert_map("test_map");
let new_array_ref = yrs_create_array_from_nest_type(
&doc,
&mut YrsNestType::MapType(map_ref.clone()),
&InsertPos::BEGIN,
key.clone(),
);
assert!(new_array_ref.is_some());
let text_ref = doc.get_or_insert_text("test_text");
let new_array_ref = yrs_create_array_from_nest_type(
&doc,
&mut YrsNestType::TextType(text_ref.clone()),
&InsertPos::BEGIN,
key.clone(),
);
assert!(new_array_ref.is_some());
}
}

View File

@@ -0,0 +1,168 @@
use phf::phf_map;
use super::*;
fn insert_op(doc: &yrs::Doc, nest_input: &YrsNestType, params: CRDTParam) {
let map = match nest_input {
YrsNestType::MapType(map) => map,
_ => unreachable!(),
};
let mut trx = doc.transact_mut();
map.insert(&mut trx, params.key, params.value);
}
fn remove_op(doc: &yrs::Doc, nest_input: &YrsNestType, params: CRDTParam) {
let map = match nest_input {
YrsNestType::MapType(map) => map,
_ => unreachable!(),
};
let rand_key = {
let trx = doc.transact_mut();
let mut iter = map.iter(&trx);
let len = map.len(&trx) as usize;
let skip_step = if len <= 1 {
0
} else {
random_pick_num((len - 1) as u32, &params.insert_pos)
};
iter
.nth(skip_step as usize)
.map(|(key, _value)| key.to_string())
};
if let Some(key) = rand_key {
let mut trx = doc.transact_mut();
map.remove(&mut trx, &key).unwrap();
}
}
fn clear_op(doc: &yrs::Doc, nest_input: &YrsNestType, _params: CRDTParam) {
let map = match nest_input {
YrsNestType::MapType(map) => map,
_ => unreachable!(),
};
let mut trx = doc.transact_mut();
map.clear(&mut trx);
}
pub static MAP_OPS: TestOps = phf_map! {
"insert" => insert_op,
"delete" => remove_op,
"clear" => clear_op,
};
pub fn yrs_create_map_from_nest_type(
doc: &yrs::Doc,
current: &mut YrsNestType,
insert_pos: &InsertPos,
key: String,
) -> Option<MapRef> {
let cal_index = |len: u32| -> u32 {
match insert_pos {
InsertPos::BEGIN => 0,
InsertPos::MID => len / 2,
InsertPos::END => len,
}
};
let mut trx = doc.transact_mut();
let map_prelim = MapPrelim::from([("deepkey".to_owned(), "deepvalue")]);
match current {
YrsNestType::ArrayType(array) => {
let index = cal_index(array.len(&trx));
Some(array.insert(&mut trx, index, map_prelim))
}
YrsNestType::MapType(map) => Some(map.insert(&mut trx, key, map_prelim)),
YrsNestType::TextType(text) => {
let str = text.get_string(&trx);
let len = str.chars().fold(0, |acc, _| acc + 1);
let index = random_pick_num(len, insert_pos) as usize;
let byte_start_offset = str
.chars()
.take(index)
.fold(0, |acc, ch| acc + ch.len_utf8());
Some(text.insert_embed(&mut trx, byte_start_offset as u32, map_prelim))
}
YrsNestType::XMLTextType(xml_text) => {
let str = xml_text.get_string(&trx);
let len = str.chars().fold(0, |acc, _| acc + 1);
let index = random_pick_num(len, insert_pos) as usize;
let byte_start_offset = str
.chars()
.take(index)
.fold(0, |acc, ch| acc + ch.len_utf8());
Some(xml_text.insert_embed(&mut trx, byte_start_offset as u32, map_prelim))
}
_ => None,
}
}
#[cfg(test)]
mod tests {
use yrs::Doc;
use super::*;
#[test]
fn test_gen_map_ref_ops() {
let doc = Doc::new();
let map_ref = doc.get_or_insert_map("test_map");
let ops_registry = OpsRegistry::new();
let mut params = CRDTParam {
op_type: OpType::CreateCRDTNestType,
new_nest_type: CRDTNestType::Map,
manipulate_source: ManipulateSource::NewNestTypeFromYDocRoot,
insert_pos: InsertPos::BEGIN,
key: String::from("test_key"),
value: String::from("test_value"),
nest_data_op_type: NestDataOpType::Insert,
};
ops_registry.operate_yrs_nest_type(&doc, YrsNestType::MapType(map_ref.clone()), params.clone());
assert_eq!(map_ref.len(&doc.transact()), 1);
params.nest_data_op_type = NestDataOpType::Delete;
ops_registry.operate_yrs_nest_type(&doc, YrsNestType::MapType(map_ref.clone()), params.clone());
assert_eq!(map_ref.len(&doc.transact()), 0);
params.nest_data_op_type = NestDataOpType::Clear;
ops_registry.operate_yrs_nest_type(&doc, YrsNestType::MapType(map_ref.clone()), params.clone());
assert_eq!(map_ref.len(&doc.transact()), 0);
}
#[test]
fn test_yrs_create_map_from_nest_type() {
let doc = Doc::new();
let map_ref = doc.get_or_insert_map("test_map");
let key = String::from("test_key");
let new_map_ref = yrs_create_map_from_nest_type(
&doc,
&mut YrsNestType::MapType(map_ref.clone()),
&InsertPos::BEGIN,
key.clone(),
);
assert!(new_map_ref.is_some());
let map_ref = doc.get_or_insert_map("test_map");
let new_map_ref = yrs_create_map_from_nest_type(
&doc,
&mut YrsNestType::MapType(map_ref.clone()),
&InsertPos::BEGIN,
key.clone(),
);
assert!(new_map_ref.is_some());
let text_ref = doc.get_or_insert_text("test_text");
let new_map_ref = yrs_create_map_from_nest_type(
&doc,
&mut YrsNestType::TextType(text_ref.clone()),
&InsertPos::BEGIN,
key.clone(),
);
assert!(new_map_ref.is_some());
}
}

View File

@@ -0,0 +1,193 @@
pub mod array;
pub mod map;
pub mod text;
pub mod xml_element;
pub mod xml_fragment;
pub mod xml_text;
use std::collections::HashMap;
use array::*;
use map::*;
use text::*;
use xml_element::*;
use xml_fragment::*;
use xml_text::*;
use yrs::{
Array, ArrayPrelim, ArrayRef, Doc, GetString, Map, MapPrelim, MapRef, Text, TextPrelim, TextRef,
Transact, XmlFragment, XmlTextPrelim, XmlTextRef,
};
use super::*;
type TestOp = fn(doc: &Doc, nest_input: &YrsNestType, params: CRDTParam) -> ();
type TestOps = phf::Map<&'static str, TestOp>;
pub struct OpsRegistry<'a>(HashMap<CRDTNestType, &'a TestOps>);
impl Default for OpsRegistry<'_> {
fn default() -> Self {
OpsRegistry::new()
}
}
impl OpsRegistry<'_> {
pub fn new() -> Self {
let mut map = HashMap::new();
map.insert(CRDTNestType::Map, &MAP_OPS);
map.insert(CRDTNestType::Array, &ARRAY_OPS);
map.insert(CRDTNestType::Text, &TEXT_OPS);
map.insert(CRDTNestType::XMLElement, &XML_ELEMENT_OPS);
map.insert(CRDTNestType::XMLText, &XML_TEXT_OPS);
map.insert(CRDTNestType::XMLFragment, &XML_FRAGMENT_OPS);
OpsRegistry(map)
}
pub fn get_ops(&self, crdt_nest_type: &CRDTNestType) -> &TestOps {
match crdt_nest_type {
CRDTNestType::Map => self.0.get(&CRDTNestType::Map).unwrap(),
CRDTNestType::Array => self.0.get(&CRDTNestType::Array).unwrap(),
CRDTNestType::Text => self.0.get(&CRDTNestType::Text).unwrap(),
CRDTNestType::XMLElement => self.0.get(&CRDTNestType::XMLElement).unwrap(),
CRDTNestType::XMLFragment => self.0.get(&CRDTNestType::XMLFragment).unwrap(),
CRDTNestType::XMLText => self.0.get(&CRDTNestType::XMLText).unwrap(),
}
}
pub fn get_ops_from_yrs_nest_type(&self, yrs_nest_type: &YrsNestType) -> &TestOps {
match yrs_nest_type {
YrsNestType::MapType(_) => self.get_ops(&CRDTNestType::Map),
YrsNestType::ArrayType(_) => self.get_ops(&CRDTNestType::Array),
YrsNestType::TextType(_) => self.get_ops(&CRDTNestType::Text),
YrsNestType::XMLElementType(_) => self.get_ops(&CRDTNestType::XMLElement),
YrsNestType::XMLTextType(_) => self.get_ops(&CRDTNestType::XMLText),
YrsNestType::XMLFragmentType(_) => self.get_ops(&CRDTNestType::XMLFragment),
}
}
pub fn operate_yrs_nest_type(
&self,
doc: &yrs::Doc,
cur_crdt_nest_type: YrsNestType,
crdt_param: CRDTParam,
) {
let ops = self.get_ops_from_yrs_nest_type(&cur_crdt_nest_type);
ops
.get(match &crdt_param.nest_data_op_type {
NestDataOpType::Insert => NEST_DATA_INSERT,
NestDataOpType::Delete => NEST_DATA_DELETE,
NestDataOpType::Clear => NEST_DATA_CLEAR,
})
.unwrap()(doc, &cur_crdt_nest_type, crdt_param)
}
}
pub fn yrs_create_nest_type_from_root(
doc: &yrs::Doc,
target_type: CRDTNestType,
key: &str,
) -> YrsNestType {
match target_type {
CRDTNestType::Array => YrsNestType::ArrayType(doc.get_or_insert_array(key)),
CRDTNestType::Map => YrsNestType::MapType(doc.get_or_insert_map(key)),
CRDTNestType::Text => YrsNestType::TextType(doc.get_or_insert_text(key)),
CRDTNestType::XMLElement => YrsNestType::XMLElementType(doc.get_or_insert_xml_fragment(key)),
CRDTNestType::XMLFragment => YrsNestType::XMLFragmentType(doc.get_or_insert_xml_fragment(key)),
CRDTNestType::XMLText => {
YrsNestType::XMLTextType((AsRef::<XmlTextRef>::as_ref(&doc.get_or_insert_text(key))).clone())
}
}
}
pub fn gen_nest_type_from_root(doc: &mut Doc, crdt_param: &CRDTParam) -> Option<YrsNestType> {
match crdt_param.new_nest_type {
CRDTNestType::Array => Some(yrs_create_nest_type_from_root(
doc,
CRDTNestType::Array,
crdt_param.key.as_str(),
)),
CRDTNestType::Map => Some(yrs_create_nest_type_from_root(
doc,
CRDTNestType::Map,
crdt_param.key.as_str(),
)),
CRDTNestType::Text => Some(yrs_create_nest_type_from_root(
doc,
CRDTNestType::Text,
crdt_param.key.as_str(),
)),
CRDTNestType::XMLText => Some(yrs_create_nest_type_from_root(
doc,
CRDTNestType::XMLText,
crdt_param.key.as_str(),
)),
CRDTNestType::XMLElement => Some(yrs_create_nest_type_from_root(
doc,
CRDTNestType::XMLElement,
crdt_param.key.as_str(),
)),
CRDTNestType::XMLFragment => Some(yrs_create_nest_type_from_root(
doc,
CRDTNestType::XMLFragment,
crdt_param.key.as_str(),
)),
}
}
pub fn gen_nest_type_from_nest_type(
doc: &mut Doc,
crdt_param: CRDTParam,
nest_type: &mut YrsNestType,
) -> Option<YrsNestType> {
match crdt_param.new_nest_type {
CRDTNestType::Array => {
yrs_create_array_from_nest_type(doc, nest_type, &crdt_param.insert_pos, crdt_param.key)
.map(YrsNestType::ArrayType)
}
CRDTNestType::Map => {
yrs_create_map_from_nest_type(doc, nest_type, &crdt_param.insert_pos, crdt_param.key)
.map(YrsNestType::MapType)
}
CRDTNestType::Text => {
yrs_create_text_from_nest_type(doc, nest_type, &crdt_param.insert_pos, crdt_param.key)
.map(YrsNestType::TextType)
}
_ => None,
}
}
pub fn random_pick_num(len: u32, insert_pos: &InsertPos) -> u32 {
match insert_pos {
InsertPos::BEGIN => 0,
InsertPos::MID => len / 2,
InsertPos::END => len,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ops_registry_new() {
let ops_registry = OpsRegistry::new();
assert_eq!(ops_registry.0.len(), 6);
}
#[test]
fn test_ops_registry_get_ops() {
let ops_registry = OpsRegistry::new();
let ops = ops_registry.get_ops(&CRDTNestType::Array);
assert!(!ops.is_empty());
}
#[test]
fn test_ops_registry_get_ops_from_yrs_nest_type() {
let doc = yrs::Doc::new();
let array = doc.get_or_insert_array("array");
let ops_registry = OpsRegistry::new();
let ops = ops_registry.get_ops_from_yrs_nest_type(&YrsNestType::ArrayType(array));
assert!(!ops.is_empty());
}
}

View File

@@ -0,0 +1,180 @@
use phf::phf_map;
use super::*;
fn insert_op(doc: &yrs::Doc, nest_input: &YrsNestType, params: CRDTParam) {
let text = match nest_input {
YrsNestType::TextType(text) => text,
_ => unreachable!(),
};
let mut trx = doc.transact_mut();
let str = text.get_string(&trx);
let len = str.chars().fold(0, |acc, _| acc + 1);
let index = random_pick_num(len, &params.insert_pos) as usize;
let byte_start_offset = str
.chars()
.take(index)
.fold(0, |acc, ch| acc + ch.len_utf8());
text.insert(&mut trx, byte_start_offset as u32, &params.value);
}
fn remove_op(doc: &yrs::Doc, nest_input: &YrsNestType, params: CRDTParam) {
let text = match nest_input {
YrsNestType::TextType(text) => text,
_ => unreachable!(),
};
let mut trx = doc.transact_mut();
let str = text.get_string(&trx);
let len = str.chars().fold(0, |acc, _| acc + 1);
if len < 1 {
return;
}
let index = random_pick_num(len - 1, &params.insert_pos) as usize;
let byte_start_offset = str
.chars()
.take(index)
.fold(0, |acc, ch| acc + ch.len_utf8());
let char_byte_len = str.chars().nth(index).unwrap().len_utf8();
text.remove_range(&mut trx, byte_start_offset as u32, char_byte_len as u32);
}
fn clear_op(doc: &yrs::Doc, nest_input: &YrsNestType, _params: CRDTParam) {
let text = match nest_input {
YrsNestType::TextType(text) => text,
_ => unreachable!(),
};
let mut trx = doc.transact_mut();
let str = text.get_string(&trx);
let byte_len = str.chars().fold(0, |acc, ch| acc + ch.len_utf8());
text.remove_range(&mut trx, 0, byte_len as u32);
}
pub static TEXT_OPS: TestOps = phf_map! {
"insert" => insert_op,
"delete" => remove_op,
"clear" => clear_op,
};
pub fn yrs_create_text_from_nest_type(
doc: &yrs::Doc,
current: &mut YrsNestType,
insert_pos: &InsertPos,
key: String,
) -> Option<TextRef> {
let cal_index_closure = |len: u32| -> u32 { random_pick_num(len, insert_pos) };
let mut trx = doc.transact_mut();
let text_prelim = TextPrelim::new("");
match current {
YrsNestType::ArrayType(array) => {
let index = cal_index_closure(array.len(&trx));
Some(array.insert(&mut trx, index, text_prelim))
}
YrsNestType::MapType(map) => Some(map.insert(&mut trx, key, text_prelim)),
YrsNestType::TextType(text) => {
let str = text.get_string(&trx);
let len = str.chars().fold(0, |acc, _| acc + 1);
let index = random_pick_num(len, insert_pos) as usize;
let byte_start_offset = str
.chars()
.take(index)
.fold(0, |acc, ch| acc + ch.len_utf8());
Some(text.insert_embed(&mut trx, byte_start_offset as u32, text_prelim))
}
YrsNestType::XMLTextType(xml_text) => {
let str = xml_text.get_string(&trx);
let len = str.chars().fold(0, |acc, _| acc + 1);
let index = random_pick_num(len, insert_pos) as usize;
let byte_start_offset = str
.chars()
.take(index)
.fold(0, |acc, ch| acc + ch.len_utf8());
Some(xml_text.insert_embed(&mut trx, byte_start_offset as u32, text_prelim))
}
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_gen_array_ref_ops() {
let doc = Doc::new();
let text_ref = doc.get_or_insert_text("test_text");
let ops_registry = OpsRegistry::new();
let mut params = CRDTParam {
op_type: OpType::CreateCRDTNestType,
new_nest_type: CRDTNestType::Text,
manipulate_source: ManipulateSource::NewNestTypeFromYDocRoot,
insert_pos: InsertPos::BEGIN,
key: String::from("test_key"),
value: String::from("test_value"),
nest_data_op_type: NestDataOpType::Insert,
};
ops_registry.operate_yrs_nest_type(
&doc,
YrsNestType::TextType(text_ref.clone()),
params.clone(),
);
assert_eq!(text_ref.len(&doc.transact()), 10);
params.nest_data_op_type = NestDataOpType::Delete;
ops_registry.operate_yrs_nest_type(
&doc,
YrsNestType::TextType(text_ref.clone()),
params.clone(),
);
assert_eq!(text_ref.len(&doc.transact()), 9);
params.nest_data_op_type = NestDataOpType::Clear;
ops_registry.operate_yrs_nest_type(
&doc,
YrsNestType::TextType(text_ref.clone()),
params.clone(),
);
assert_eq!(text_ref.len(&doc.transact()), 0);
}
#[test]
fn test_yrs_create_array_from_nest_type() {
let doc = Doc::new();
let array_ref = doc.get_or_insert_array("test_array");
let key = String::from("test_key");
let next_text_ref = yrs_create_text_from_nest_type(
&doc,
&mut YrsNestType::ArrayType(array_ref.clone()),
&InsertPos::BEGIN,
key.clone(),
);
assert!(next_text_ref.is_some());
let map_ref = doc.get_or_insert_map("test_map");
let next_text_ref = yrs_create_text_from_nest_type(
&doc,
&mut YrsNestType::MapType(map_ref.clone()),
&InsertPos::BEGIN,
key.clone(),
);
assert!(next_text_ref.is_some());
let text_ref = doc.get_or_insert_text("test_text");
let next_text_ref = yrs_create_text_from_nest_type(
&doc,
&mut YrsNestType::TextType(text_ref.clone()),
&InsertPos::BEGIN,
key.clone(),
);
assert!(next_text_ref.is_some());
}
}

View File

@@ -0,0 +1,45 @@
use phf::phf_map;
use super::*;
fn insert_op(doc: &yrs::Doc, nest_input: &YrsNestType, params: CRDTParam) {
let xml_element = match nest_input {
YrsNestType::XMLElementType(xml_element) => xml_element,
_ => unreachable!(),
};
let mut trx = doc.transact_mut();
let len = xml_element.len(&trx);
let index = random_pick_num(len, &params.insert_pos);
xml_element.insert(&mut trx, index, XmlTextPrelim::new(params.value));
}
fn remove_op(doc: &yrs::Doc, nest_input: &YrsNestType, params: CRDTParam) {
let xml_element = match nest_input {
YrsNestType::XMLElementType(xml_element) => xml_element,
_ => unreachable!(),
};
let mut trx = doc.transact_mut();
let len = xml_element.len(&trx);
if len >= 1 {
let index = random_pick_num(len - 1, &params.insert_pos);
xml_element.remove_range(&mut trx, index, 1);
}
}
fn clear_op(doc: &yrs::Doc, nest_input: &YrsNestType, _params: CRDTParam) {
let xml_element = match nest_input {
YrsNestType::XMLElementType(xml_element) => xml_element,
_ => unreachable!(),
};
let mut trx = doc.transact_mut();
let len = xml_element.len(&trx);
for _ in 0..len {
xml_element.remove_range(&mut trx, 0, 1);
}
}
pub static XML_ELEMENT_OPS: TestOps = phf_map! {
"insert" => insert_op,
"delete" => remove_op,
"clear" => clear_op,
};

View File

@@ -0,0 +1,45 @@
use phf::phf_map;
use super::*;
fn insert_op(doc: &yrs::Doc, nest_input: &YrsNestType, params: CRDTParam) {
let xml_fragment = match nest_input {
YrsNestType::XMLFragmentType(xml_fragment) => xml_fragment,
_ => unreachable!(),
};
let mut trx = doc.transact_mut();
let len = xml_fragment.len(&trx);
let index = random_pick_num(len, &params.insert_pos);
xml_fragment.insert(&mut trx, index, XmlTextPrelim::new(params.value));
}
fn remove_op(doc: &yrs::Doc, nest_input: &YrsNestType, params: CRDTParam) {
let xml_fragment = match nest_input {
YrsNestType::XMLFragmentType(xml_fragment) => xml_fragment,
_ => unreachable!(),
};
let mut trx = doc.transact_mut();
let len = xml_fragment.len(&trx);
if len >= 1 {
let index = random_pick_num(len - 1, &params.insert_pos);
xml_fragment.remove_range(&mut trx, index, 1);
}
}
fn clear_op(doc: &yrs::Doc, nest_input: &YrsNestType, _params: CRDTParam) {
let xml_fragment = match nest_input {
YrsNestType::XMLFragmentType(xml_fragment) => xml_fragment,
_ => unreachable!(),
};
let mut trx = doc.transact_mut();
let len = xml_fragment.len(&trx);
for _ in 0..len {
xml_fragment.remove_range(&mut trx, 0, 1);
}
}
pub static XML_FRAGMENT_OPS: TestOps = phf_map! {
"insert" => insert_op,
"delete" => remove_op,
"clear" => clear_op,
};

View File

@@ -0,0 +1,62 @@
use phf::phf_map;
use super::*;
fn insert_op(doc: &yrs::Doc, nest_input: &YrsNestType, params: CRDTParam) {
let xml_text = match nest_input {
YrsNestType::XMLTextType(xml_text) => xml_text,
_ => unreachable!(),
};
let mut trx = doc.transact_mut();
let str = xml_text.get_string(&trx);
let len = str.chars().fold(0, |acc, _| acc + 1);
let index = random_pick_num(len, &params.insert_pos) as usize;
let byte_start_offset = str
.chars()
.take(index)
.fold(0, |acc, ch| acc + ch.len_utf8());
xml_text.insert(&mut trx, byte_start_offset as u32, &params.value);
}
fn remove_op(doc: &yrs::Doc, nest_input: &YrsNestType, params: CRDTParam) {
let xml_text = match nest_input {
YrsNestType::XMLTextType(xml_text) => xml_text,
_ => unreachable!(),
};
let mut trx = doc.transact_mut();
let str = xml_text.get_string(&trx);
let len = str.chars().fold(0, |acc, _| acc + 1);
if len < 1 {
return;
}
let index = random_pick_num(len - 1, &params.insert_pos) as usize;
let byte_start_offset = str
.chars()
.take(index)
.fold(0, |acc, ch| acc + ch.len_utf8());
let char_byte_len = str.chars().nth(index).unwrap().len_utf8();
xml_text.remove_range(&mut trx, byte_start_offset as u32, char_byte_len as u32);
}
fn clear_op(doc: &yrs::Doc, nest_input: &YrsNestType, _params: CRDTParam) {
let xml_text = match nest_input {
YrsNestType::XMLTextType(xml_text) => xml_text,
_ => unreachable!(),
};
let mut trx = doc.transact_mut();
let str = xml_text.get_string(&trx);
let byte_len = str.chars().fold(0, |acc, ch| acc + ch.len_utf8());
xml_text.remove_range(&mut trx, 0, byte_len as u32);
}
pub static XML_TEXT_OPS: TestOps = phf_map! {
"insert" => insert_op,
"delete" => remove_op,
"clear" => clear_op,
};

View File

@@ -0,0 +1,7 @@
mod doc;
#[cfg(feature = "fuzz")]
pub mod doc_operation;
#[cfg(feature = "fuzz")]
pub use doc_operation::*;