| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606 |
- use std::{cmp::Ordering, io};
- use darkfi_serial::{Decodable, Encodable, SerialDecodable, SerialEncodable, VarInt};
- use dryoc::constants::CRYPTO_SECRETBOX_NONCEBYTES;
- use serde::{Deserialize, Serialize};
- use darkfi::util::{
- cli::{fg_green, fg_red},
- time::Timestamp,
- };
- use crate::util::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<OpMethod>);
- #[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
- pub struct EncryptedPatch {
- pub nonce: [u8; CRYPTO_SECRETBOX_NONCEBYTES],
- pub ciphertext: Vec<u8>,
- }
- #[derive(PartialEq, Eq, SerialEncodable, SerialDecodable, Serialize, Deserialize, Clone, Debug)]
- pub struct Patch {
- pub path: String,
- pub author: String,
- pub id: String,
- pub base: String,
- pub timestamp: Timestamp,
- pub workspace: String,
- 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(path: &str, id: &str, author: &str, workspace: &str) -> Self {
- Self {
- path: path.to_string(),
- id: id.to_string(),
- ops: OpMethods(vec![]),
- base: String::new(),
- workspace: workspace.to_string(),
- 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.path, &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.path, &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::<String>();
- 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 fg_green(&self.base)
- }
- let mut st = vec![];
- st.extend(str_to_chars(&self.base));
- let st = &mut st.iter();
- let mut colorized_str: Vec<String> = 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(fg_red(&deleted_part.join("")));
- }
- OpMethod::Insert(insert) => {
- let chars = str_to_chars(insert);
- colorized_str.push(fg_green(&chars.join("")))
- }
- }
- }
- colorized_str.join("")
- }
- }
- impl Decodable for OpMethod {
- fn decode<D: io::Read>(mut d: D) -> core::result::Result<Self, io::Error> {
- 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(io::Error::new(io::ErrorKind::Other, "Parse OpMethod failed")),
- }
- }
- }
- impl Encodable for OpMethod {
- fn encode<S: io::Write>(&self, mut s: S) -> core::result::Result<usize, io::Error> {
- 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<S: io::Write>(&self, mut s: S) -> core::result::Result<usize, io::Error> {
- 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<D: io::Read>(mut d: D) -> core::result::Result<Self, io::Error> {
- 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;
- use darkfi_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;
- 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;
- 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;
- 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);
- }
- }
|