mirror of
https://github.com/loro-dev/loro.git
synced 2025-02-05 20:17:13 +00:00
feat: basic wasm interface
This commit is contained in:
parent
3a0c00fdec
commit
e0a472fd1a
16 changed files with 263 additions and 96 deletions
3
.vscode/extensions.json
vendored
3
.vscode/extensions.json
vendored
|
@ -2,6 +2,7 @@
|
|||
"recommendations": [
|
||||
"skellock.just",
|
||||
"Gruntfuggly.todo-tree",
|
||||
"rust-lang.rust-analyzer"
|
||||
"rust-lang.rust-analyzer",
|
||||
"denoland.vscode-deno"
|
||||
]
|
||||
}
|
||||
|
|
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
@ -19,5 +19,6 @@
|
|||
"explorer.fileNesting.patterns": {
|
||||
"*.rs": "${capture}.excalidraw"
|
||||
},
|
||||
"excalidraw.theme": "dark"
|
||||
"excalidraw.theme": "dark",
|
||||
"deno.enable": true
|
||||
}
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
use std::{
|
||||
ops::{Deref, DerefMut},
|
||||
ptr::NonNull,
|
||||
sync::RwLockWriteGuard,
|
||||
};
|
||||
|
||||
use enum_as_inner::EnumAsInner;
|
||||
use fxhash::FxHashMap;
|
||||
use owning_ref::OwningRefMut;
|
||||
use owning_ref::{OwningRef, OwningRefMut};
|
||||
|
||||
use crate::{isomorph::IsoRefMut, log_store::LogStoreWeakRef, span::IdSpan, LogStore};
|
||||
use crate::{
|
||||
isomorph::{IsoRef, IsoRefMut},
|
||||
log_store::LogStoreWeakRef,
|
||||
span::IdSpan,
|
||||
LogStore, LoroError,
|
||||
};
|
||||
|
||||
use super::{
|
||||
map::MapContainer, text::text_container::TextContainer, Container, ContainerID, ContainerType,
|
||||
|
@ -96,7 +100,9 @@ impl ContainerManager {
|
|||
log_store: LogStoreWeakRef,
|
||||
) -> ContainerInstance {
|
||||
match container_type {
|
||||
ContainerType::Map => ContainerInstance::Map(Box::new(MapContainer::new(id))),
|
||||
ContainerType::Map => {
|
||||
ContainerInstance::Map(Box::new(MapContainer::new(id, log_store)))
|
||||
}
|
||||
ContainerType::Text => {
|
||||
ContainerInstance::Text(Box::new(TextContainer::new(id, log_store)))
|
||||
}
|
||||
|
@ -123,27 +129,46 @@ impl ContainerManager {
|
|||
&mut self,
|
||||
id: &ContainerID,
|
||||
log_store: LogStoreWeakRef,
|
||||
) -> &mut ContainerInstance {
|
||||
) -> Result<&mut ContainerInstance, LoroError> {
|
||||
if !self.containers.contains_key(id) {
|
||||
let container = self.create(id.clone(), id.container_type(), log_store);
|
||||
self.insert(id.clone(), container);
|
||||
}
|
||||
|
||||
self.get_mut(id).unwrap()
|
||||
let container = self.get_mut(id).unwrap();
|
||||
if container.type_() != id.container_type() {
|
||||
Err(LoroError::ContainerTypeError {
|
||||
id: id.clone(),
|
||||
actual_type: container.type_(),
|
||||
expected_type: id.container_type(),
|
||||
})
|
||||
} else {
|
||||
Ok(container)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ContainerRef<'a, T> {
|
||||
pub struct ContainerRefMut<'a, T> {
|
||||
value: OwningRefMut<IsoRefMut<'a, ContainerManager>, Box<T>>,
|
||||
}
|
||||
|
||||
impl<'a, T> From<OwningRefMut<IsoRefMut<'a, ContainerManager>, Box<T>>> for ContainerRef<'a, T> {
|
||||
pub struct ContainerRef<'a, T> {
|
||||
value: OwningRef<IsoRef<'a, ContainerManager>, Box<T>>,
|
||||
}
|
||||
|
||||
impl<'a, T> From<OwningRefMut<IsoRefMut<'a, ContainerManager>, Box<T>>> for ContainerRefMut<'a, T> {
|
||||
fn from(value: OwningRefMut<IsoRefMut<'a, ContainerManager>, Box<T>>) -> Self {
|
||||
ContainerRefMut { value }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> From<OwningRef<IsoRef<'a, ContainerManager>, Box<T>>> for ContainerRef<'a, T> {
|
||||
fn from(value: OwningRef<IsoRef<'a, ContainerManager>, Box<T>>) -> Self {
|
||||
ContainerRef { value }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Deref for ContainerRef<'a, T> {
|
||||
impl<'a, T> Deref for ContainerRefMut<'a, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
|
@ -151,7 +176,7 @@ impl<'a, T> Deref for ContainerRef<'a, T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, T> DerefMut for ContainerRef<'a, T> {
|
||||
impl<'a, T> DerefMut for ContainerRefMut<'a, T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.value.deref_mut()
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ pub struct MapContainer {
|
|||
id: ContainerID,
|
||||
state: FxHashMap<InternalString, ValueSlot>,
|
||||
value: Option<LoroValue>,
|
||||
store: LogStoreWeakRef,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -33,23 +34,18 @@ struct ValueSlot {
|
|||
|
||||
impl MapContainer {
|
||||
#[inline]
|
||||
pub fn new(id: ContainerID) -> Self {
|
||||
pub(crate) fn new(id: ContainerID, store: LogStoreWeakRef) -> Self {
|
||||
MapContainer {
|
||||
id,
|
||||
store,
|
||||
state: FxHashMap::default(),
|
||||
value: None,
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: keep store in the struct
|
||||
pub(crate) fn insert(
|
||||
&mut self,
|
||||
key: InternalString,
|
||||
value: InsertValue,
|
||||
store: LogStoreWeakRef,
|
||||
) {
|
||||
pub fn insert(&mut self, key: InternalString, value: InsertValue) {
|
||||
let self_id = self.id.clone();
|
||||
let m = store.upgrade().unwrap();
|
||||
let m = self.store.upgrade().unwrap();
|
||||
let mut store = m.write();
|
||||
let client_id = store.this_client_id;
|
||||
let order = TotalOrderStamp {
|
||||
|
@ -89,10 +85,9 @@ impl MapContainer {
|
|||
);
|
||||
}
|
||||
|
||||
// FIXME: keep store in the struct
|
||||
#[inline]
|
||||
pub(crate) fn delete(&mut self, key: InternalString, store: LogStoreWeakRef) {
|
||||
self.insert(key, InsertValue::Null, store);
|
||||
pub fn delete(&mut self, key: InternalString) {
|
||||
self.insert(key, InsertValue::Null);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,15 @@
|
|||
use thiserror::Error;
|
||||
|
||||
use crate::{container::ContainerID, ContainerType};
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum LoroError {
|
||||
#[error("Expect container with the id of {id:?} has type {expected_type:?} but the actual type is {actual_type:?}.")]
|
||||
ContainerTypeError {
|
||||
id: ContainerID,
|
||||
actual_type: ContainerType,
|
||||
expected_type: ContainerType,
|
||||
},
|
||||
// #[error("the data for key `{0}` is not available")]
|
||||
// Redaction(String),
|
||||
// #[error("invalid header (expected {expected:?}, found {found:?})")]
|
||||
|
@ -9,3 +17,16 @@ pub enum LoroError {
|
|||
// #[error("unknown data store error")]
|
||||
// Unknown,
|
||||
}
|
||||
|
||||
#[cfg(feature = "wasm")]
|
||||
pub mod wasm {
|
||||
use wasm_bindgen::JsValue;
|
||||
|
||||
use crate::LoroError;
|
||||
|
||||
impl From<LoroError> for JsValue {
|
||||
fn from(value: LoroError) -> Self {
|
||||
JsValue::from_str(&value.to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -177,12 +177,14 @@ impl Actionable for Vec<LoroCore> {
|
|||
match action {
|
||||
Action::Ins { content, pos, site } => {
|
||||
self[*site as usize]
|
||||
.get_or_create_text_container_mut("text".into())
|
||||
.get_or_create_text_container("text".into())
|
||||
.unwrap()
|
||||
.insert(*pos, content);
|
||||
}
|
||||
Action::Del { pos, len, site } => {
|
||||
self[*site as usize]
|
||||
.get_or_create_text_container_mut("text".into())
|
||||
.get_or_create_text_container("text".into())
|
||||
.unwrap()
|
||||
.delete(*pos, *len);
|
||||
}
|
||||
Action::Sync { from, to } => {
|
||||
|
@ -198,7 +200,9 @@ impl Actionable for Vec<LoroCore> {
|
|||
match action {
|
||||
Action::Ins { pos, site, .. } => {
|
||||
*site %= self.len() as u8;
|
||||
let mut text = self[*site as usize].get_or_create_text_container_mut("text".into());
|
||||
let mut text = self[*site as usize]
|
||||
.get_or_create_text_container("text".into())
|
||||
.unwrap();
|
||||
let value = text.get_value().as_string().unwrap();
|
||||
*pos %= value.len() + 1;
|
||||
while !value.is_char_boundary(*pos) {
|
||||
|
@ -207,7 +211,9 @@ impl Actionable for Vec<LoroCore> {
|
|||
}
|
||||
Action::Del { pos, len, site } => {
|
||||
*site %= self.len() as u8;
|
||||
let mut text = self[*site as usize].get_or_create_text_container_mut("text".into());
|
||||
let mut text = self[*site as usize]
|
||||
.get_or_create_text_container("text".into())
|
||||
.unwrap();
|
||||
if text.text_len() == 0 {
|
||||
*len = 0;
|
||||
*pos = 0;
|
||||
|
@ -235,8 +241,8 @@ impl Actionable for Vec<LoroCore> {
|
|||
}
|
||||
|
||||
fn check_eq(site_a: &mut LoroCore, site_b: &mut LoroCore) {
|
||||
let mut a = site_a.get_or_create_text_container_mut("text".into());
|
||||
let mut b = site_b.get_or_create_text_container_mut("text".into());
|
||||
let mut a = site_a.get_or_create_text_container("text".into()).unwrap();
|
||||
let mut b = site_b.get_or_create_text_container("text".into()).unwrap();
|
||||
let value_a = a.get_value();
|
||||
let value_b = b.get_value();
|
||||
assert_eq!(value_a.as_string().unwrap(), value_b.as_string().unwrap());
|
||||
|
@ -259,7 +265,7 @@ fn check_synced(sites: &mut [LoroCore]) {
|
|||
|
||||
pub fn test_single_client(mut actions: Vec<Action>) {
|
||||
let mut store = LoroCore::new(Default::default(), Some(1));
|
||||
let mut text_container = store.get_or_create_text_container_mut("haha".into());
|
||||
let mut text_container = store.get_or_create_text_container("haha".into()).unwrap();
|
||||
let mut ground_truth = String::new();
|
||||
let mut applied = Vec::new();
|
||||
for action in actions
|
||||
|
|
|
@ -28,6 +28,7 @@ pub mod tests;
|
|||
|
||||
mod value;
|
||||
|
||||
pub use error::LoroError;
|
||||
pub(crate) mod macros;
|
||||
pub(crate) use change::{Lamport, Timestamp};
|
||||
pub(crate) use id::{ClientID, ID};
|
||||
|
@ -35,10 +36,10 @@ pub(crate) use op::{ContentType, InsertContentTrait, Op, OpType};
|
|||
|
||||
pub(crate) type InternalString = DefaultAtom;
|
||||
|
||||
pub use container::ContainerType;
|
||||
pub use container::{Container, ContainerType};
|
||||
pub use log_store::LogStore;
|
||||
pub use loro::*;
|
||||
pub use value::LoroValue;
|
||||
pub use value::{InsertValue, LoroValue};
|
||||
pub use version::VersionVector;
|
||||
|
||||
use string_cache::DefaultAtom;
|
||||
|
|
|
@ -2,9 +2,7 @@
|
|||
//!
|
||||
//!
|
||||
mod iter;
|
||||
use std::{
|
||||
marker::PhantomPinned,
|
||||
};
|
||||
use std::marker::PhantomPinned;
|
||||
|
||||
use fxhash::{FxHashMap, FxHashSet};
|
||||
|
||||
|
@ -149,7 +147,9 @@ impl LogStore {
|
|||
change: &mut Change,
|
||||
) {
|
||||
for op in change.ops.vec_mut().iter_mut() {
|
||||
let container = container_manager.get_or_create(&op.container, self.to_self.clone());
|
||||
let container = container_manager
|
||||
.get_or_create(&op.container, self.to_self.clone())
|
||||
.unwrap();
|
||||
container.to_import(op);
|
||||
}
|
||||
}
|
||||
|
@ -282,7 +282,9 @@ impl LogStore {
|
|||
}
|
||||
|
||||
for container in set {
|
||||
let container = container_manager.get_or_create(container, self.to_self.clone());
|
||||
let container = container_manager
|
||||
.get_or_create(container, self.to_self.clone())
|
||||
.unwrap();
|
||||
container.apply(change.id_span(), self);
|
||||
}
|
||||
|
||||
|
|
|
@ -6,14 +6,14 @@ use crate::{
|
|||
change::Change,
|
||||
configure::Configure,
|
||||
container::{
|
||||
manager::{ContainerInstance, ContainerManager, ContainerRef},
|
||||
manager::{ContainerManager, ContainerRef, ContainerRefMut},
|
||||
map::MapContainer,
|
||||
text::text_container::TextContainer,
|
||||
ContainerID, ContainerType,
|
||||
},
|
||||
id::ClientID,
|
||||
isomorph::{Irc, IsoRefMut, IsoRw},
|
||||
LogStore, VersionVector,
|
||||
LogStore, LoroError, VersionVector,
|
||||
};
|
||||
|
||||
pub struct LoroCore {
|
||||
|
@ -44,59 +44,67 @@ impl LoroCore {
|
|||
self.log_store.read().get_vv().clone()
|
||||
}
|
||||
|
||||
pub fn get_container(
|
||||
#[inline(always)]
|
||||
pub fn get_or_create_map_container(
|
||||
&mut self,
|
||||
name: &str,
|
||||
container: ContainerType,
|
||||
) -> OwningRefMut<IsoRefMut<ContainerManager>, ContainerInstance> {
|
||||
let a = OwningRefMut::new(self.container.write());
|
||||
a.map_mut(|x| {
|
||||
x.get_or_create(
|
||||
&ContainerID::new_root(name, container),
|
||||
Irc::downgrade(&self.log_store),
|
||||
)
|
||||
})
|
||||
) -> Result<ContainerRefMut<MapContainer>, LoroError> {
|
||||
let mut a = OwningRefMut::new(self.container.write());
|
||||
let id = ContainerID::new_root(name, ContainerType::Map);
|
||||
let ptr = Irc::downgrade(&self.log_store);
|
||||
a.get_or_create(&id, ptr)?;
|
||||
Ok(
|
||||
a.map_mut(move |x| x.get_mut(&id).unwrap().as_map_mut().unwrap())
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_map_container(
|
||||
#[inline(always)]
|
||||
pub fn get_or_create_text_container(
|
||||
&mut self,
|
||||
name: &str,
|
||||
) -> OwningRefMut<IsoRefMut<ContainerManager>, Box<MapContainer>> {
|
||||
let a = OwningRefMut::new(self.container.write());
|
||||
a.map_mut(|x| {
|
||||
x.get_or_create(
|
||||
&ContainerID::new_root(name, ContainerType::Map),
|
||||
Irc::downgrade(&self.log_store),
|
||||
)
|
||||
.as_map_mut()
|
||||
.unwrap()
|
||||
})
|
||||
) -> Result<ContainerRefMut<TextContainer>, LoroError> {
|
||||
let mut a = OwningRefMut::new(self.container.write());
|
||||
let id = ContainerID::new_root(name, ContainerType::Text);
|
||||
let ptr = Irc::downgrade(&self.log_store);
|
||||
a.get_or_create(&id, ptr)?;
|
||||
Ok(
|
||||
a.map_mut(move |x| x.get_mut(&id).unwrap().as_text_mut().unwrap())
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_or_create_text_container_mut(&mut self, name: &str) -> ContainerRef<TextContainer> {
|
||||
#[inline(always)]
|
||||
pub fn get_map_container_mut(
|
||||
&mut self,
|
||||
id: &ContainerID,
|
||||
) -> Result<ContainerRefMut<MapContainer>, LoroError> {
|
||||
let a = OwningRefMut::new(self.container.write());
|
||||
a.map_mut(|x| {
|
||||
x.get_or_create(
|
||||
&ContainerID::new_root(name, ContainerType::Text),
|
||||
Irc::downgrade(&self.log_store),
|
||||
)
|
||||
.as_text_mut()
|
||||
.unwrap()
|
||||
})
|
||||
.into()
|
||||
Ok(
|
||||
a.map_mut(move |x| x.get_mut(id).unwrap().as_map_mut().unwrap())
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn get_text_container_mut(
|
||||
&mut self,
|
||||
id: &ContainerID,
|
||||
) -> Result<ContainerRefMut<TextContainer>, LoroError> {
|
||||
let a = OwningRefMut::new(self.container.write());
|
||||
Ok(
|
||||
a.map_mut(move |x| x.get_mut(id).unwrap().as_text_mut().unwrap())
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn get_text_container(
|
||||
&self,
|
||||
name: &str,
|
||||
) -> OwningRef<IsoRefMut<ContainerManager>, Box<TextContainer>> {
|
||||
let a = OwningRef::new(self.container.write());
|
||||
a.map(|x| {
|
||||
x.get(&ContainerID::new_root(name, ContainerType::Text))
|
||||
.unwrap()
|
||||
.as_text()
|
||||
.unwrap()
|
||||
})
|
||||
id: &ContainerID,
|
||||
) -> Result<ContainerRef<TextContainer>, LoroError> {
|
||||
let a = OwningRef::new(self.container.read());
|
||||
Ok(a.map(move |x| x.get(id).unwrap().as_text().unwrap()).into())
|
||||
}
|
||||
|
||||
pub fn export(&self, remote_vv: VersionVector) -> Vec<Change> {
|
||||
|
|
|
@ -68,6 +68,8 @@ pub mod wasm {
|
|||
|
||||
use crate::LoroValue;
|
||||
|
||||
use super::InsertValue;
|
||||
|
||||
pub fn convert(value: LoroValue) -> JsValue {
|
||||
match value {
|
||||
LoroValue::Null => JsValue::NULL,
|
||||
|
@ -103,6 +105,22 @@ pub mod wasm {
|
|||
convert(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl InsertValue {
|
||||
pub fn try_from_js(value: JsValue) -> Result<InsertValue, JsValue> {
|
||||
if value.is_null() {
|
||||
Ok(InsertValue::Null)
|
||||
} else if value.as_bool().is_some() {
|
||||
Ok(InsertValue::Bool(value.as_bool().unwrap()))
|
||||
} else if value.as_f64().is_some() {
|
||||
Ok(InsertValue::Double(value.as_f64().unwrap()))
|
||||
} else if value.is_string() {
|
||||
Ok(InsertValue::String(value.as_string().unwrap().into()))
|
||||
} else {
|
||||
Err(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -5,7 +5,7 @@ use loro_core::LoroCore;
|
|||
#[test]
|
||||
fn test() {
|
||||
let mut store = LoroCore::new(Default::default(), Some(10));
|
||||
let mut text_container = store.get_or_create_text_container_mut("haha".into());
|
||||
let mut text_container = store.get_or_create_text_container("haha".into());
|
||||
text_container.insert(0, "012");
|
||||
text_container.insert(1, "34");
|
||||
text_container.insert(1, "56");
|
||||
|
@ -17,7 +17,7 @@ fn test() {
|
|||
let mut store_b = LoroCore::new(Default::default(), Some(11));
|
||||
let exported = store.export(Default::default());
|
||||
store_b.import(exported);
|
||||
let mut text_container = store_b.get_or_create_text_container_mut("haha".into());
|
||||
let mut text_container = store_b.get_or_create_text_container("haha".into());
|
||||
text_container.check();
|
||||
let value = text_container.get_value();
|
||||
let value = value.as_string().unwrap();
|
||||
|
@ -31,7 +31,7 @@ fn test() {
|
|||
drop(text_container);
|
||||
|
||||
store.import(store_b.export(store.vv()));
|
||||
let mut text_container = store.get_or_create_text_container_mut("haha".into());
|
||||
let mut text_container = store.get_or_create_text_container("haha".into());
|
||||
let value = text_container.get_value();
|
||||
let value = value.as_string().unwrap();
|
||||
assert_eq!(value.as_str(), "63417892");
|
||||
|
@ -43,7 +43,7 @@ fn test() {
|
|||
drop(text_container);
|
||||
|
||||
store_b.import(store.export(Default::default()));
|
||||
let mut text_container = store_b.get_or_create_text_container_mut("haha".into());
|
||||
let mut text_container = store_b.get_or_create_text_container("haha".into());
|
||||
text_container.check();
|
||||
let value = text_container.get_value();
|
||||
let value = value.as_string().unwrap();
|
||||
|
|
|
@ -8,7 +8,7 @@ crate-type = ["cdylib", "rlib"]
|
|||
|
||||
[dependencies]
|
||||
js-sys = "0.3.60"
|
||||
loro-core = { path = "../loro-core", features = ["wasm"] }
|
||||
loro-core = { path = "../loro-core", features = ["wasm", "slice"] }
|
||||
wasm-bindgen = "0.2.83"
|
||||
|
||||
[profile.release]
|
||||
|
|
4
crates/loro-wasm/deno_test/.vscode/settings.json
vendored
Normal file
4
crates/loro-wasm/deno_test/.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"deno.enable": true,
|
||||
"editor.stickyScroll.enabled": false
|
||||
}
|
10
crates/loro-wasm/deno_test/test.ts
Normal file
10
crates/loro-wasm/deno_test/test.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
import init, { Loro } from "../pkg/loro_wasm.js";
|
||||
const wasm = await Deno.readFile("../pkg/loro_wasm_bg.wasm");
|
||||
|
||||
await init(wasm);
|
||||
const loro = new Loro();
|
||||
const a = loro.get_text_container("ha");
|
||||
a.insert(0, "hello world");
|
||||
a.delete(6, 5);
|
||||
a.insert(6, "everyone");
|
||||
console.log(a.get_value());
|
2
crates/loro-wasm/justfile
Normal file
2
crates/loro-wasm/justfile
Normal file
|
@ -0,0 +1,2 @@
|
|||
build:
|
||||
wasm-pack build --target web .
|
|
@ -1,32 +1,105 @@
|
|||
use std::{
|
||||
cell::RefCell,
|
||||
rc::{Rc, Weak},
|
||||
};
|
||||
|
||||
use loro_core::{
|
||||
configure::Configure,
|
||||
container::{manager::ContainerRef, text::text_container::TextContainer as LoroTextContainer},
|
||||
LoroCore,
|
||||
container::{Container, ContainerID},
|
||||
InsertValue, LoroCore, LoroError,
|
||||
};
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub struct Loro {
|
||||
loro: LoroCore,
|
||||
loro: Rc<RefCell<LoroCore>>,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub struct TextContainer {
|
||||
inner: ContainerRef<'static, LoroTextContainer>,
|
||||
pub struct Text {
|
||||
loro: Weak<RefCell<LoroCore>>,
|
||||
id: ContainerID,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub struct Map {
|
||||
loro: Weak<RefCell<LoroCore>>,
|
||||
id: ContainerID,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
impl Loro {
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new() -> Self {
|
||||
Loro {
|
||||
// TODO: expose the configuration
|
||||
loro: LoroCore::default(),
|
||||
loro: Rc::new(RefCell::new(LoroCore::default())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_text_container(&mut self, name: &str) -> TextContainer {
|
||||
TextContainer {
|
||||
inner: self.loro.get_or_create_text_container_mut(name),
|
||||
}
|
||||
pub fn get_text_container(&mut self, name: &str) -> Result<Text, JsValue> {
|
||||
let mut loro = self.loro.borrow_mut();
|
||||
let text_container = loro.get_or_create_text_container(name)?;
|
||||
Ok(Text {
|
||||
id: text_container.id().clone(),
|
||||
loro: Rc::downgrade(&self.loro),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_map_container(&mut self, name: &str) -> Result<Map, JsValue> {
|
||||
let mut loro = self.loro.borrow_mut();
|
||||
let map = loro.get_or_create_map_container(name)?;
|
||||
Ok(Map {
|
||||
id: map.id().clone(),
|
||||
loro: Rc::downgrade(&self.loro),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
impl Map {
|
||||
pub fn set(&mut self, key: String, value: JsValue) {
|
||||
let loro = self.loro.upgrade().unwrap();
|
||||
let mut loro = loro.borrow_mut();
|
||||
let mut map = loro.get_map_container_mut(&self.id).unwrap();
|
||||
map.insert(key.into(), InsertValue::try_from_js(value).unwrap())
|
||||
}
|
||||
|
||||
pub fn delete(&mut self, key: String) {
|
||||
let loro = self.loro.upgrade().unwrap();
|
||||
let mut loro = loro.borrow_mut();
|
||||
let mut map = loro.get_map_container_mut(&self.id).unwrap();
|
||||
map.delete(key.into())
|
||||
}
|
||||
|
||||
pub fn get_value(&mut self) -> JsValue {
|
||||
let loro = self.loro.upgrade().unwrap();
|
||||
let mut loro = loro.borrow_mut();
|
||||
let mut map = loro.get_map_container_mut(&self.id).unwrap();
|
||||
map.get_value().clone().into()
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
impl Text {
|
||||
pub fn insert(&mut self, index: usize, text: &str) {
|
||||
let loro = self.loro.upgrade().unwrap();
|
||||
let mut loro = loro.borrow_mut();
|
||||
let mut text_container = loro.get_text_container_mut(&self.id).unwrap();
|
||||
text_container.insert(index, text);
|
||||
}
|
||||
|
||||
pub fn delete(&mut self, index: usize, len: usize) {
|
||||
let loro = self.loro.upgrade().unwrap();
|
||||
let mut loro = loro.borrow_mut();
|
||||
let mut text_container = loro.get_text_container_mut(&self.id).unwrap();
|
||||
text_container.delete(index, len);
|
||||
}
|
||||
|
||||
pub fn get_value(&mut self) -> String {
|
||||
let loro = self.loro.upgrade().unwrap();
|
||||
let mut loro = loro.borrow_mut();
|
||||
let mut text_container = loro.get_text_container_mut(&self.id).unwrap();
|
||||
text_container.get_value().as_string().unwrap().to_string()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue