use delta::array_vec::ArrayVec;
use delta::DeltaRope;
use enum_as_inner::EnumAsInner;
use loro_common::IdLp;
use loro_internal::container::ContainerID;
pub use loro_internal::delta::TreeDiff;
use loro_internal::delta::{ResolvedMapDelta, ResolvedMapValue};
use loro_internal::event::{EventTriggerKind, ListDeltaMeta};
use loro_internal::handler::{TextDelta, ValueOrHandler};
use loro_internal::undo::DiffBatch as InnerDiffBatch;
use loro_internal::{
event::{Diff as DiffInner, Index},
ContainerDiff as ContainerDiffInner, DiffEvent as DiffEventInner,
};
use loro_internal::{FxHashMap, ListDiffInsertItem};
use std::borrow::Cow;
use std::ops::Deref;
use std::sync::Arc;
use crate::ValueOrContainer;
#[allow(clippy::unused_unit)]
pub type Subscriber = Arc<dyn for<'a> Fn(DiffEvent<'a>) + Send + Sync>;
#[derive(Debug)]
pub struct DiffEvent<'a> {
pub triggered_by: EventTriggerKind,
pub origin: &'a str,
pub current_target: Option<ContainerID>,
pub events: Vec<ContainerDiff<'a>>,
}
#[derive(Debug)]
pub struct ContainerDiff<'a> {
pub target: &'a ContainerID,
pub path: &'a [(ContainerID, Index)],
pub is_unknown: bool,
pub diff: Diff<'a>,
}
#[derive(Debug, EnumAsInner, Clone)]
pub enum Diff<'a> {
List(Vec<ListDiffItem>),
Text(Vec<TextDelta>),
Map(MapDelta<'a>),
Tree(Cow<'a, TreeDiff>),
#[cfg(feature = "counter")]
Counter(f64),
Unknown,
}
#[derive(Debug, Clone)]
pub enum ListDiffItem {
Insert {
insert: Vec<ValueOrContainer>,
is_move: bool,
},
Delete {
delete: usize,
},
Retain {
retain: usize,
},
}
#[derive(Debug, Clone)]
pub struct MapDelta<'a> {
pub updated: FxHashMap<Cow<'a, str>, Option<ValueOrContainer>>,
}
impl<'a> From<DiffEventInner<'a>> for DiffEvent<'a> {
fn from(value: DiffEventInner<'a>) -> Self {
DiffEvent {
triggered_by: value.event_meta.by,
origin: &value.event_meta.origin,
current_target: value.current_target,
events: value.events.iter().map(|&diff| diff.into()).collect(),
}
}
}
impl<'a> From<&'a ContainerDiffInner> for ContainerDiff<'a> {
fn from(value: &'a ContainerDiffInner) -> Self {
ContainerDiff {
target: &value.id,
path: &value.path,
is_unknown: value.is_unknown,
diff: (&value.diff).into(),
}
}
}
impl<'a> From<&'a DiffInner> for Diff<'a> {
fn from(value: &'a DiffInner) -> Self {
match value {
DiffInner::List(l) => {
let mut ans = Vec::new();
for item in l.iter() {
match item {
delta::DeltaItem::Retain { len, .. } => {
ans.push(ListDiffItem::Retain { retain: *len });
}
delta::DeltaItem::Replace {
value,
delete,
attr,
} => {
if !value.is_empty() {
ans.push(ListDiffItem::Insert {
insert: value
.iter()
.map(|v| ValueOrContainer::from(v.clone()))
.collect(),
is_move: attr.from_move,
});
}
if *delete > 0 {
ans.push(ListDiffItem::Delete { delete: *delete });
}
}
}
}
Diff::List(ans)
}
DiffInner::Map(m) => Diff::Map(MapDelta {
updated: m
.updated
.iter()
.map(|(k, v)| (Cow::Borrowed(k.as_str()), v.value.clone().map(|v| v.into())))
.collect(),
}),
DiffInner::Text(t) => {
let text = TextDelta::from_text_diff(t.iter());
Diff::Text(text)
}
DiffInner::Tree(t) => Diff::Tree(Cow::Borrowed(t)),
#[cfg(feature = "counter")]
DiffInner::Counter(c) => Diff::Counter(*c),
DiffInner::Unknown => Diff::Unknown,
_ => todo!(),
}
}
}
impl From<DiffInner> for Diff<'static> {
fn from(value: DiffInner) -> Self {
match value {
DiffInner::List(l) => {
let mut ans = Vec::new();
for item in l.iter() {
match item {
delta::DeltaItem::Retain { len, .. } => {
ans.push(ListDiffItem::Retain { retain: *len });
}
delta::DeltaItem::Replace {
value,
delete,
attr,
} => {
if !value.is_empty() {
ans.push(ListDiffItem::Insert {
insert: value
.iter()
.map(|v| ValueOrContainer::from(v.clone()))
.collect(),
is_move: attr.from_move,
});
}
if *delete > 0 {
ans.push(ListDiffItem::Delete { delete: *delete });
}
}
}
}
Diff::List(ans)
}
DiffInner::Map(m) => Diff::Map(MapDelta {
updated: m
.updated
.iter()
.map(|(k, v)| (Cow::Owned(k.to_string()), v.value.clone().map(|v| v.into())))
.collect(),
}),
DiffInner::Text(t) => {
let text = TextDelta::from_text_diff(t.iter());
Diff::Text(text)
}
DiffInner::Tree(t) => Diff::Tree(Cow::Owned(t.clone())),
#[cfg(feature = "counter")]
DiffInner::Counter(c) => Diff::Counter(c),
DiffInner::Unknown => Diff::Unknown,
_ => todo!(),
}
}
}
impl From<ValueOrHandler> for ValueOrContainer {
fn from(value: ValueOrHandler) -> Self {
match value {
ValueOrHandler::Value(v) => ValueOrContainer::Value(v),
ValueOrHandler::Handler(h) => ValueOrContainer::Container(h.into()),
}
}
}
#[derive(Default, Clone)]
pub struct DiffBatch {
cid_to_events: FxHashMap<ContainerID, Diff<'static>>,
order: Vec<ContainerID>,
}
impl std::fmt::Debug for DiffBatch {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let v: Vec<(&ContainerID, &Diff<'static>)> = self.iter().collect();
write!(f, "{:#?}", v)
}
}
impl DiffBatch {
pub fn iter(&self) -> impl Iterator<Item = (&ContainerID, &Diff<'static>)> {
self.order.iter().map(|id| (id, &self.cid_to_events[id]))
}
pub fn push(&mut self, cid: ContainerID, diff: Diff<'static>) -> Result<(), Diff<'static>> {
match self.cid_to_events.entry(cid.clone()) {
std::collections::hash_map::Entry::Occupied(_) => Err(diff),
std::collections::hash_map::Entry::Vacant(vacant_entry) => {
vacant_entry.insert(diff);
self.order.push(cid);
Ok(())
}
}
}
}
impl From<InnerDiffBatch> for DiffBatch {
fn from(value: InnerDiffBatch) -> Self {
let mut map =
FxHashMap::with_capacity_and_hasher(value.cid_to_events.len(), Default::default());
for (id, diff) in value.cid_to_events.into_iter() {
map.insert(id.clone(), diff.into());
}
DiffBatch {
cid_to_events: map,
order: value.order,
}
}
}
impl From<Diff<'static>> for DiffInner {
fn from(value: Diff<'static>) -> Self {
match value {
Diff::List(vec) => {
let mut ans: DeltaRope<ListDiffInsertItem, ListDeltaMeta> = DeltaRope::new();
for item in vec.iter() {
match item {
ListDiffItem::Insert { insert, is_move } => {
for item in ArrayVec::from_many(
insert.iter().map(|v| v.clone().into_value_or_handler()),
) {
ans.push_insert(
item,
ListDeltaMeta {
from_move: *is_move,
},
);
}
}
ListDiffItem::Delete { delete } => {
ans.push_delete(*delete);
}
ListDiffItem::Retain { retain } => {
ans.push_retain(*retain, ListDeltaMeta { from_move: false });
}
}
}
DiffInner::List(ans)
}
Diff::Text(t) => {
let text = TextDelta::into_text_diff(t.into_iter());
DiffInner::Text(text)
}
Diff::Map(map_delta) => DiffInner::Map(ResolvedMapDelta {
updated: map_delta
.updated
.into_iter()
.map(|(k, v)| {
(
k.deref().into(),
ResolvedMapValue {
value: v.map(|v| v.into_value_or_handler()),
idlp: IdLp::new(0, 0),
},
)
})
.collect(),
}),
Diff::Tree(cow) => DiffInner::Tree(cow.into_owned()),
#[cfg(feature = "counter")]
Diff::Counter(c) => DiffInner::Counter(c),
Diff::Unknown => DiffInner::Unknown,
}
}
}
impl From<DiffBatch> for InnerDiffBatch {
fn from(value: DiffBatch) -> Self {
let mut map =
FxHashMap::with_capacity_and_hasher(value.cid_to_events.len(), Default::default());
for (id, diff) in value.cid_to_events.into_iter() {
map.insert(id.clone(), diff.into());
}
InnerDiffBatch {
cid_to_events: map,
order: value.order,
}
}
}