use std::{cmp::Ordering, io}; use colored::Colorize; use serde::{Deserialize, Serialize}; use darkfi::util::{ serial::{Decodable, Encodable, SerialDecodable, SerialEncodable, VarInt}, Timestamp, }; use crate::str_to_chars; #[derive(PartialEq, Eq, Serialize, Deserialize, Clone, Debug)] pub enum OpMethod { Delete(u64), Insert(String), Retain(u64), } #[derive(PartialEq, Eq, Serialize, Deserialize, Clone, Debug)] pub struct OpMethods(pub Vec); #[derive(PartialEq, Eq, SerialEncodable, SerialDecodable, Serialize, Deserialize, Clone, Debug)] pub struct Patch { pub title: String, pub author: String, pub id: String, pub base: String, pub timestamp: Timestamp, ops: OpMethods, } impl std::string::ToString for Patch { fn to_string(&self) -> String { if self.ops.0.is_empty() { return self.base.clone() } let mut st = vec![]; st.extend(str_to_chars(&self.base)); let st = &mut st.iter(); let mut new_st: Vec<&str> = vec![]; for op in self.ops.0.iter() { match op { OpMethod::Retain(n) => { for c in st.take(*n as usize) { new_st.push(c); } } OpMethod::Delete(n) => { for _ in 0..*n { st.next(); } } OpMethod::Insert(insert) => { let chars = str_to_chars(insert); new_st.extend(chars); } } } new_st.join("") } } impl Patch { pub fn new(title: &str, id: &str, author: &str) -> Self { Self { title: title.to_string(), id: id.to_string(), ops: OpMethods(vec![]), base: String::new(), author: author.to_string(), timestamp: Timestamp::current_time(), } } pub fn add_op(&mut self, method: &OpMethod) { match method { OpMethod::Delete(n) => { if *n == 0 { return } if let Some(OpMethod::Delete(i)) = self.ops.0.last_mut() { *i += n; } else { self.ops.0.push(method.to_owned()); } } OpMethod::Insert(insert) => { if insert.is_empty() { return } if let Some(OpMethod::Insert(s)) = self.ops.0.last_mut() { *s += insert; } else { self.ops.0.push(OpMethod::Insert(insert.to_owned())); } } OpMethod::Retain(n) => { if *n == 0 { return } if let Some(OpMethod::Retain(i)) = self.ops.0.last_mut() { *i += n; } else { self.ops.0.push(method.to_owned()); } } } } fn insert(&mut self, st: &str) { self.add_op(&OpMethod::Insert(st.into())); } fn retain(&mut self, n: u64) { self.add_op(&OpMethod::Retain(n)); } fn delete(&mut self, n: u64) { self.add_op(&OpMethod::Delete(n)); } pub fn set_ops(&mut self, ops: OpMethods) { self.ops = ops; } pub fn extend_ops(&mut self, ops: OpMethods) { self.ops.0.extend(ops.0); } pub fn ops(&self) -> OpMethods { self.ops.clone() } // // these two functions are imported from this library // https://github.com/spebern/operational-transform-rs // with some major modification // // TODO need more work to get better performance with iterators pub fn transform(&self, other: &Self) -> Self { let mut new_patch = Self::new(&self.title, &self.id, &self.author); new_patch.base = self.base.clone(); let mut ops1 = self.ops.0.iter().cloned(); let mut ops2 = other.ops.0.iter().cloned(); let mut op1 = ops1.next(); let mut op2 = ops2.next(); loop { match (&op1, &op2) { (None, None) => break, (None, Some(op)) => { new_patch.add_op(op); op2 = ops2.next(); continue } (Some(op), None) => { new_patch.add_op(op); op1 = ops1.next(); continue } _ => {} } match (op1.as_ref().unwrap(), op2.as_ref().unwrap()) { (OpMethod::Insert(s), _) => { new_patch.retain(str_to_chars(s).len() as _); op1 = ops1.next(); } (_, OpMethod::Insert(s)) => { new_patch.insert(s); op2 = ops2.next(); } (OpMethod::Retain(i), OpMethod::Retain(j)) => match i.cmp(j) { Ordering::Less => { new_patch.retain(*i); op2 = Some(OpMethod::Retain(j - *i)); op1 = ops1.next(); } Ordering::Greater => { new_patch.retain(*j); op1 = Some(OpMethod::Retain(i - j)); op2 = ops2.next(); } Ordering::Equal => { new_patch.retain(*i); op1 = ops1.next(); op2 = ops2.next(); } }, (OpMethod::Delete(i), OpMethod::Delete(j)) => match i.cmp(j) { Ordering::Less => { op2 = Some(OpMethod::Delete(j - *i)); op1 = ops1.next(); } Ordering::Greater => { op1 = Some(OpMethod::Delete(i - j)); op2 = ops2.next(); } Ordering::Equal => { op1 = ops1.next(); op2 = ops2.next(); } }, (OpMethod::Delete(i), OpMethod::Retain(j)) => match i.cmp(j) { Ordering::Less => { op2 = Some(OpMethod::Retain(j - *i)); op1 = ops1.next(); } Ordering::Greater => { op1 = Some(OpMethod::Delete(i - j)); op2 = ops2.next(); } Ordering::Equal => { op1 = ops1.next(); op2 = ops2.next(); } }, (OpMethod::Retain(i), OpMethod::Delete(j)) => match i.cmp(j) { Ordering::Less => { new_patch.delete(*i); op2 = Some(OpMethod::Delete(j - i)); op1 = ops1.next(); } Ordering::Greater => { new_patch.delete(*j); op1 = Some(OpMethod::Retain(i - j)); op2 = ops2.next(); } Ordering::Equal => { new_patch.delete(*i); op1 = ops1.next(); op2 = ops2.next(); } }, } } new_patch } // TODO need more work to get better performance with iterators pub fn merge(&mut self, other: &Self) -> Self { let ops1 = self.ops.0.clone(); let mut ops1 = ops1.iter().cloned(); let mut ops2 = other.ops.0.iter().cloned(); let mut new_patch = Self::new(&self.title, &self.id, &self.author); new_patch.base = self.base.clone(); let mut op1 = ops1.next(); let mut op2 = ops2.next(); loop { match (&op1, &op2) { (None, None) => break, (None, Some(op)) => { new_patch.add_op(op); op2 = ops2.next(); continue } (Some(op), None) => { new_patch.add_op(op); op1 = ops1.next(); continue } _ => {} } match (op1.as_ref().unwrap(), op2.as_ref().unwrap()) { (OpMethod::Delete(i), _) => { new_patch.delete(*i); op1 = ops1.next(); } (_, OpMethod::Insert(s)) => { new_patch.insert(s); op2 = ops2.next(); } (OpMethod::Retain(i), OpMethod::Retain(j)) => match i.cmp(j) { Ordering::Less => { new_patch.retain(*i); op2 = Some(OpMethod::Retain(*j - i)); op1 = ops1.next(); } Ordering::Greater => { new_patch.retain(*j); op1 = Some(OpMethod::Retain(i - *j)); op2 = ops2.next(); } Ordering::Equal => { new_patch.retain(*i); op1 = ops1.next(); op2 = ops2.next(); } }, (OpMethod::Insert(s), OpMethod::Delete(j)) => { let chars = str_to_chars(s); let chars_len = chars.len() as u64; match chars_len.cmp(j) { Ordering::Less => { op1 = ops1.next(); op2 = Some(OpMethod::Delete(j - chars_len)); } Ordering::Greater => { let st = chars.into_iter().skip(*j as usize).collect(); op1 = Some(OpMethod::Insert(st)); op2 = ops2.next(); } Ordering::Equal => { op1 = ops1.next(); op2 = ops2.next(); } } } (OpMethod::Insert(s), OpMethod::Retain(j)) => { let chars = str_to_chars(s); let chars_len = chars.len() as u64; match chars_len.cmp(j) { Ordering::Less => { new_patch.insert(s); op1 = ops1.next(); op2 = Some(OpMethod::Retain(*j - chars_len)); } Ordering::Greater => { let st = chars.into_iter().take(*j as usize).collect::(); new_patch.insert(&st); op1 = Some(OpMethod::Insert(st)); op2 = ops2.next(); } Ordering::Equal => { new_patch.insert(s); op1 = ops1.next(); op2 = ops2.next(); } } } (OpMethod::Retain(i), OpMethod::Delete(j)) => match i.cmp(j) { Ordering::Less => { new_patch.delete(*i); op2 = Some(OpMethod::Delete(*j - *i)); op1 = ops1.next(); } Ordering::Greater => { new_patch.delete(*j); op1 = Some(OpMethod::Retain(*i - *j)); op2 = ops2.next(); } Ordering::Equal => { new_patch.delete(*j); op1 = ops1.next(); op2 = ops2.next(); } }, }; } new_patch } pub fn colorize(&self) -> String { if self.ops.0.is_empty() { return format!("{}", self.base.green()) } let mut st = vec![]; st.extend(str_to_chars(&self.base)); let st = &mut st.iter(); let mut colorized_str: Vec = vec![]; for op in self.ops.0.iter() { match op { OpMethod::Retain(n) => { for c in st.take(*n as usize) { colorized_str.push(c.to_string()); } } OpMethod::Delete(n) => { let mut deleted_part = vec![]; for _ in 0..*n { let s = st.next(); if let Some(s) = s { deleted_part.push(s.to_string()); } } colorized_str.push(format!("{}", deleted_part.join("").red())); } OpMethod::Insert(insert) => { let chars = str_to_chars(insert); colorized_str.push(format!("{}", chars.join("").green())); } } } colorized_str.join("") } } impl Decodable for OpMethod { fn decode(mut d: D) -> darkfi::Result { let com: u8 = Decodable::decode(&mut d)?; match com { 0 => { let i: u64 = Decodable::decode(&mut d)?; Ok(Self::Delete(i)) } 1 => { let t: String = Decodable::decode(d)?; Ok(Self::Insert(t)) } 2 => { let i: u64 = Decodable::decode(&mut d)?; Ok(Self::Retain(i)) } _ => Err(darkfi::Error::ParseFailed("Parse OpMethod failed")), } } } impl Encodable for OpMethod { fn encode(&self, mut s: S) -> darkfi::Result { let len: usize = match self { Self::Delete(i) => (0_u8).encode(&mut s)? + i.encode(&mut s)?, Self::Insert(t) => (1_u8).encode(&mut s)? + t.encode(&mut s)?, Self::Retain(i) => (2_u8).encode(&mut s)? + i.encode(&mut s)?, }; Ok(len) } } impl Encodable for OpMethods { fn encode(&self, mut s: S) -> darkfi::Result { let mut len = 0; len += VarInt(self.0.len() as u64).encode(&mut s)?; for c in self.0.iter() { len += c.encode(&mut s)?; } Ok(len) } } impl Decodable for OpMethods { fn decode(mut d: D) -> darkfi::Result { let len = VarInt::decode(&mut d)?.0; let mut ret = Vec::with_capacity(len as usize); for _ in 0..len { ret.push(Decodable::decode(&mut d)?); } Ok(Self(ret)) } } #[cfg(test)] mod tests { use super::*; use darkfi::util::{ gen_id, serial::{deserialize, serialize}, }; #[test] fn test_to_string() { let mut patch = Patch::new("", &gen_id(30), ""); patch.base = "text example\n hello".to_string(); patch.retain(14); patch.delete(5); patch.insert("hey"); assert_eq!(patch.to_string(), "text example\n hey"); } #[test] fn test_merge() { let mut patch_init = Patch::new("", &gen_id(30), ""); let base = "text example\n hello"; patch_init.base = base.to_string(); let mut patch1 = patch_init.clone(); patch1.retain(14); patch1.delete(5); patch1.insert("hey"); let mut patch2 = patch_init.clone(); patch2.retain(14); patch2.delete(5); patch2.insert("test"); patch1.merge(&patch2); let patch3 = patch1.merge(&patch2); assert_eq!(patch3.to_string(), "text example\n test"); let mut patch1 = patch_init.clone(); patch1.retain(5); patch1.delete(7); patch1.insert("ex"); patch1.retain(7); let mut patch2 = patch_init.clone(); patch2.delete(4); patch2.insert("new"); patch2.retain(13); let patch3 = patch1.merge(&patch2); assert_eq!(patch3.to_string(), "new ex\n hello"); } #[test] fn test_transform() { let mut patch_init = Patch::new("", &gen_id(30), ""); let base = "text example\n hello"; patch_init.base = base.to_string(); let mut patch1 = patch_init.clone(); patch1.retain(14); patch1.delete(5); patch1.insert("hey"); let mut patch2 = patch_init.clone(); patch2.retain(14); patch2.delete(5); patch2.insert("test"); let patch3 = patch1.transform(&patch2); let patch4 = patch1.merge(&patch3); assert_eq!(patch4.to_string(), "text example\n heytest"); let mut patch1 = patch_init.clone(); patch1.retain(5); patch1.delete(7); patch1.insert("ex"); patch1.retain(7); let mut patch2 = patch_init.clone(); patch2.delete(4); patch2.insert("new"); patch2.retain(13); let patch3 = patch1.transform(&patch2); let patch4 = patch1.merge(&patch3); assert_eq!(patch4.to_string(), "new ex\n hello"); } #[test] fn test_transform2() { let mut patch_init = Patch::new("", &gen_id(30), ""); let base = "#hello\n hello"; patch_init.base = base.to_string(); let mut patch1 = patch_init.clone(); patch1.retain(13); patch1.insert(" world"); let mut patch2 = patch_init.clone(); patch2.retain(1); patch2.delete(5); patch2.insert("this is the title"); patch2.retain(7); patch2.insert("\n this is the content"); let patch3 = patch1.transform(&patch2); let patch4 = patch1.merge(&patch3); assert_eq!(patch4.to_string(), "#this is the title\n hello world\n this is the content"); } #[test] fn test_serialize() { // serialize & deserialize OpMethod let op_method = OpMethod::Delete(3); let op_method_ser = serialize(&op_method); let op_method_deser = deserialize(&op_method_ser).unwrap(); assert_eq!(op_method, op_method_deser); // serialize & deserialize Patch let mut patch = Patch::new("", &gen_id(30), ""); patch.insert("hello"); patch.delete(2); let patch_ser = serialize(&patch); let patch_deser = deserialize(&patch_ser).unwrap(); assert_eq!(patch, patch_deser); } }