///|
// List state for list containers.
pub struct Elem {
v : @types.LoroValue
id : @types.IdFull
} derive(Show)
///|
pub struct ListState {
list : Array[Elem]
} derive(Show)
///|
pub fn ListState::new() -> ListState {
ListState::{ list: [] }
}
///|
pub fn ListState::len(self : ListState) -> Int {
self.list.length()
}
///|
pub fn ListState::to_values(self : ListState) -> Array[@types.LoroValue] {
let out : Array[@types.LoroValue] = []
for elem in self.list {
out.push(elem.v)
}
out
}
///|
pub fn ListState::to_value(self : ListState) -> @types.LoroValue {
@types.LoroValue::List(self.to_values())
}
///|
pub fn ListState::elems(self : ListState) -> Array[Elem] {
let out : Array[Elem] = []
for elem in self.list {
out.push(elem)
}
out
}
///|
pub fn ListState::append_elem(
self : ListState,
value : @types.LoroValue,
id : @types.IdFull,
) -> Unit {
self.list.push(Elem::{ v: value, id })
}
///|
pub fn ListState::value_at(
self : ListState,
index : Int,
) -> Result[@types.LoroValue?, @types.LoroError] {
if index < 0 || index >= self.list.length() {
return Err(@types.LoroError::InternalError("index out of range"))
}
Ok(Some(self.list[index].v))
}
///|
pub fn ListState::id_at(
self : ListState,
index : Int,
) -> Result[@types.IdFull, @types.LoroError] {
if index < 0 || index >= self.list.length() {
return Err(@types.LoroError::InternalError("index out of range"))
}
Ok(self.list[index].id)
}
///|
pub fn ListState::ids_in_range(
self : ListState,
start : Int,
end : Int,
) -> Result[Array[@types.IdFull], @types.LoroError] {
if start < 0 || end < start || end > self.list.length() {
return Err(@types.LoroError::InternalError("index out of range"))
}
let out : Array[@types.IdFull] = []
for i = start; i < end; i = i + 1 {
out.push(self.list[i].id)
}
Ok(out)
}
///|
fn ListState::find_index_by_id(self : ListState, elem_id : @types.IdLp) -> Int? {
for i = 0; i < self.list.length(); i = i + 1 {
let id = self.list[i].id
if id.peer == elem_id.peer && id.lamport == elem_id.lamport {
return Some(i)
}
}
None
}
///|
fn list_elem_id_at(base : @types.IdFull, offset : Int) -> @types.IdFull {
@types.IdFull::new(
base.peer,
base.counter + offset,
base.lamport + offset.reinterpret_as_uint(),
)
}
///|
fn id_full_cmp(a : @types.IdFull, b : @types.IdFull) -> Int {
if a.lamport < b.lamport {
-1
} else if a.lamport > b.lamport {
1
} else if a.peer < b.peer {
-1
} else if a.peer > b.peer {
1
} else if a.counter < b.counter {
-1
} else if a.counter > b.counter {
1
} else {
0
}
}
///|
fn list_elem_visible_in_vv(vv : @types.VersionVector, elem : Elem) -> Bool {
vv.includes_id(elem.id.to_id())
}
///|
fn ListState::insert_pos_with_vv(
self : ListState,
pos : Int,
vv : @types.VersionVector,
new_id : @types.IdFull,
) -> Result[Int, @types.LoroError] {
if pos < 0 {
return Err(@types.LoroError::InternalError("index out of range"))
}
let mut visible_count = 0
let mut last_visible = -1
let mut idx = 0
let mut gap_start = 0
let mut gap_end = self.list.length()
let mut found_gap = false
while idx < self.list.length() {
let elem = self.list[idx]
if list_elem_visible_in_vv(vv, elem) {
if visible_count == pos {
gap_start = last_visible + 1
gap_end = idx
found_gap = true
break
}
visible_count = visible_count + 1
last_visible = idx
}
idx = idx + 1
}
if !found_gap {
if visible_count == pos {
gap_start = last_visible + 1
gap_end = self.list.length()
} else {
return Err(@types.LoroError::InternalError("index out of range"))
}
}
let mut insert_idx = gap_start
while insert_idx < gap_end {
let elem = self.list[insert_idx]
if id_full_cmp(elem.id, new_id) < 0 {
insert_idx = insert_idx + 1
} else {
break
}
}
Ok(insert_idx)
}
///|
pub fn ListState::insert(
self : ListState,
index : Int,
value : @types.LoroValue,
id : @types.IdFull,
) -> Result[Unit, @types.LoroError] {
if index < 0 || index > self.list.length() {
return Err(@types.LoroError::InternalError("index out of range"))
}
self.list.insert(index, Elem::{ v: value, id })
Ok(())
}
///|
pub fn ListState::insert_batch(
self : ListState,
index : Int,
values : Array[@types.LoroValue],
base_id : @types.IdFull,
) -> Result[Unit, @types.LoroError] {
if index < 0 || index > self.list.length() {
return Err(@types.LoroError::InternalError("index out of range"))
}
let mut offset = 0
for v in values {
let id = list_elem_id_at(base_id, offset)
self.list.insert(index + offset, Elem::{ v, id })
offset = offset + 1
}
Ok(())
}
///|
pub fn ListState::insert_batch_with_vv(
self : ListState,
index : Int,
values : Array[@types.LoroValue],
base_id : @types.IdFull,
vv : @types.VersionVector,
) -> Result[Unit, @types.LoroError] {
let insert_at = match self.insert_pos_with_vv(index, vv, base_id) {
Ok(value) => value
Err(err) => return Err(err)
}
let mut offset = 0
for v in values {
let id = list_elem_id_at(base_id, offset)
self.list.insert(insert_at + offset, Elem::{ v, id })
offset = offset + 1
}
Ok(())
}
///|
pub fn ListState::set_by_id(
self : ListState,
elem_id : @types.IdLp,
value : @types.LoroValue,
) -> Result[Unit, @types.LoroError] {
match self.find_index_by_id(elem_id) {
Some(idx) => {
let existing = self.list[idx]
self.list[idx] = Elem::{ v: value, id: existing.id }
Ok(())
}
None => Err(@types.LoroError::InternalError("element not found"))
}
}
///|
pub fn ListState::move_by_id(
self : ListState,
elem_id : @types.IdLp,
to : Int,
) -> Result[Unit, @types.LoroError] {
let len = self.list.length()
if to < 0 || to >= len {
return Err(@types.LoroError::InternalError("index out of range"))
}
let idx = match self.find_index_by_id(elem_id) {
Some(value) => value
None => return Err(@types.LoroError::InternalError("element not found"))
}
if idx == to {
return Ok(())
}
let elem = self.list.remove(idx)
self.list.insert(to, elem)
Ok(())
}
///|
pub fn ListState::delete_range(
self : ListState,
start : Int,
end : Int,
) -> Result[Unit, @types.LoroError] {
if start < 0 || end < start || end > self.list.length() {
return Err(@types.LoroError::InternalError("invalid delete range"))
}
let mut idx = start
while idx < end {
ignore(self.list.remove(start))
idx = idx + 1
}
Ok(())
}
///|
pub fn ListState::delete_by_id(
self : ListState,
span : @op.DeleteSpanWithId,
) -> Result[Unit, @types.LoroError] {
let id_start = span.id_start()
let len = span.content_len()
if len <= 0 {
return Ok(())
}
let start_ctr = id_start.counter
let end_ctr = start_ctr + len
let peer = id_start.peer
let mut i = 0
while i < self.list.length() {
let elem = self.list[i]
if elem.id.peer == peer &&
elem.id.counter >= start_ctr &&
elem.id.counter < end_ctr {
ignore(self.list.remove(i))
} else {
i = i + 1
}
}
Ok(())
}
///|
pub fn ListState::apply_inner_op(
self : ListState,
arena : @arena.SharedArena,
op : @op.InnerListOp,
base_id : @types.IdFull,
) -> Result[Unit, @types.LoroError] {
match op {
Insert(slice~, pos~) => {
let values = arena.get_values(slice)
self.insert_batch(pos, values, base_id)
}
Delete(span) => self.delete_by_id(span)
Move(from=_, elem_id~, to~) => self.move_by_id(elem_id, to)
Set(elem_id~, value~) => self.set_by_id(elem_id, value)
_ => Err(@types.LoroError::Unsupported("list op not supported"))
}
}
///|
pub fn ListState::apply_inner_op_with_vv(
self : ListState,
arena : @arena.SharedArena,
op : @op.InnerListOp,
base_id : @types.IdFull,
vv : @types.VersionVector,
) -> Result[Unit, @types.LoroError] {
match op {
Insert(slice~, pos~) => {
let values = arena.get_values(slice)
self.insert_batch_with_vv(pos, values, base_id, vv)
}
Delete(span) => self.delete_by_id(span)
Move(from=_, elem_id~, to~) => self.move_by_id(elem_id, to)
Set(elem_id~, value~) => self.set_by_id(elem_id, value)
_ => Err(@types.LoroError::Unsupported("list op not supported"))
}
}