Skip to content

Commit

Permalink
feat: add some compatibility for yjs's api
Browse files Browse the repository at this point in the history
  • Loading branch information
darkskygit committed Jul 8, 2024
1 parent bc4ceee commit c9fb934
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 21 deletions.
12 changes: 9 additions & 3 deletions y-octo-node/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,12 @@ export class Doc {
}
export class YMap {
get length(): number
get size(): number
get isEmpty(): boolean
get<T = unknown>(key: string): T
set(key: string, value: YArray | YMap | YText | boolean | number | string | Record<string, any> | null | undefined): void
remove(key: string): void
set<T = YArray | YMap | YText | boolean | number | string | Record<string, any> | null | undefined>(key: string, value: T): T
delete(key: string): void
clear(): void
toJson(): object
entries(): YMapEntriesIterator
keys(): YMapKeyIterator
Expand All @@ -75,9 +77,13 @@ export class YText {
get len(): number
get isEmpty(): boolean
insert(index: number, str: string): void
remove(index: number, len: number): void
delete(index: number, len: number): void
get length(): number
applyDelta(delta: JsArray): void
toDelta(): JsArray
toString(): string
observe(callback: (...args: any[]) => any): void
observeDeep(callback: (...args: any[]) => any): void
}
export type YStore = Store
export class Store { }
Expand Down
78 changes: 66 additions & 12 deletions y-octo-node/src/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ impl YMap {
self.map.len() as i64
}

#[napi(getter)]
pub fn size(&self) -> i64 {
self.length()
}

#[napi(getter)]
pub fn is_empty(&self) -> bool {
self.map.is_empty()
Expand All @@ -41,25 +46,52 @@ impl YMap {
}

#[napi(
ts_args_type = "key: string, value: YArray | YMap | YText | boolean | number | string | Record<string, any> | \
null | undefined"
ts_generic_types = "T = YArray | YMap | YText | boolean | number | string | Record<string, any> | null | undefined",
ts_args_type = "key: string, value: T",
ts_return_type = "T"
)]
pub fn set(&mut self, key: String, value: MixedRefYType) -> Result<()> {
pub fn set(&mut self, env: Env, key: String, value: MixedRefYType) -> Result<MixedYType> {
match value {
MixedRefYType::A(array) => self.map.insert(key, array.array.clone()).map_err(anyhow::Error::from),
MixedRefYType::B(map) => self.map.insert(key, map.map.clone()).map_err(anyhow::Error::from),
MixedRefYType::C(text) => self.map.insert(key, text.text.clone()).map_err(anyhow::Error::from),
MixedRefYType::A(array) => {
self.map.insert(key, array.array.clone()).map_err(anyhow::Error::from)?;
Ok(MixedYType::A(YArray::inner_new(array.array.clone())))
}
MixedRefYType::B(map) => {
self.map.insert(key, map.map.clone()).map_err(anyhow::Error::from)?;
Ok(MixedYType::B(YMap::inner_new(map.map.clone())))
}
MixedRefYType::C(text) => {
self.map.insert(key, text.text.clone()).map_err(anyhow::Error::from)?;
Ok(MixedYType::C(YText::inner_new(text.text.clone())))
}
MixedRefYType::D(unknown) => match unknown.get_type() {
Ok(value_type) => match value_type {
ValueType::Undefined | ValueType::Null => {
self.map.insert(key, Any::Null).map_err(anyhow::Error::from)
self.map.insert(key, Any::Null).map_err(anyhow::Error::from)?;
Ok(MixedYType::D(
env.get_null().map(|v| v.into_unknown()).map_err(anyhow::Error::from)?,
))
}
ValueType::Boolean => match unknown.coerce_to_bool().and_then(|v| v.get_value()) {
Ok(boolean) => self.map.insert(key, boolean).map_err(anyhow::Error::from),
Ok(boolean) => {
self.map.insert(key, boolean).map_err(anyhow::Error::from)?;
Ok(MixedYType::D(
env.get_boolean(boolean)
.map(|v| v.into_unknown())
.map_err(anyhow::Error::from)?,
))
}
Err(e) => Err(anyhow::Error::from(e).context("Failed to coerce value to boolean")),
},
ValueType::Number => match unknown.coerce_to_number().and_then(|v| v.get_double()) {
Ok(number) => self.map.insert(key, number).map_err(anyhow::Error::from),
Ok(number) => {
self.map.insert(key, number).map_err(anyhow::Error::from)?;
Ok(MixedYType::D(
env.create_double(number)
.map(|v| v.into_unknown())
.map_err(anyhow::Error::from)?,
))
}
Err(e) => Err(anyhow::Error::from(e).context("Failed to coerce value to number")),
},
ValueType::String => {
Expand All @@ -68,12 +100,26 @@ impl YMap {
.and_then(|v| v.into_utf8())
.and_then(|s| s.as_str().map(|s| s.to_string()))
{
Ok(string) => self.map.insert(key, string).map_err(anyhow::Error::from),
Ok(string) => {
self.map.insert(key, string.clone()).map_err(anyhow::Error::from)?;
Ok(MixedYType::D(
env.create_string(&string)
.map(|v| v.into_unknown())
.map_err(anyhow::Error::from)?,
))
}
Err(e) => Err(anyhow::Error::from(e).context("Failed to coerce value to string")),
}
}
ValueType::Object => match unknown.coerce_to_object().and_then(get_any_from_js_object) {
Ok(any) => self.map.insert(key, Value::Any(any)).map_err(anyhow::Error::from),
Ok(any) => {
self.map
.insert(key, Value::Any(any.clone()))
.map_err(anyhow::Error::from)?;
Ok(MixedYType::D(
get_js_unknown_from_any(env, any).map_err(anyhow::Error::from)?.into(),
))
}
Err(e) => Err(anyhow::Error::from(e).context("Failed to coerce value to object")),
},
ValueType::Symbol => Err(anyhow::Error::msg("Symbol values are not supported")),
Expand All @@ -87,10 +133,18 @@ impl YMap {
}

#[napi]
pub fn remove(&mut self, key: String) {
pub fn delete(&mut self, key: String) {
self.map.remove(&key);
}

#[napi]
pub fn clear(&mut self) {
let keys = self.map.keys().map(ToOwned::to_owned).collect::<Vec<_>>();
for key in keys {
self.map.remove(&key);
}
}

#[napi]
pub fn to_json(&self, env: Env) -> Result<JsObject> {
let mut js_object = env.create_object()?;
Expand Down
27 changes: 25 additions & 2 deletions y-octo-node/src/text.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use napi::{bindgen_prelude::Array as JsArray, Env, JsFunction};
use y_octo::Text;

use super::*;
Expand Down Expand Up @@ -35,7 +36,7 @@ impl YText {
}

#[napi]
pub fn remove(&mut self, index: i64, len: i64) -> Result<()> {
pub fn delete(&mut self, index: i64, len: i64) -> Result<()> {
self.text.remove(index as u64, len as u64).map_err(anyhow::Error::from)
}

Expand All @@ -44,11 +45,33 @@ impl YText {
self.text.len() as i64
}

#[napi]
pub fn apply_delta(&mut self, env: Env, _delta: JsArray) -> Result<()> {
unimplemented!()
}

#[napi]
pub fn to_delta(&self, env: Env) -> Result<JsArray> {
unimplemented!()
}

#[allow(clippy::inherent_to_string)]
#[napi]
pub fn to_string(&self) -> String {
self.text.to_string()
}

// TODO(@darkskygit): impl type based observe
#[napi]
pub fn observe(&mut self, _callback: JsFunction) -> Result<()> {
Ok(())
}

// TODO(@darkskygit): impl type based observe
#[napi]
pub fn observe_deep(&mut self, _callback: JsFunction) -> Result<()> {
Ok(())
}
}

#[cfg(test)]
Expand All @@ -70,7 +93,7 @@ mod tests {
assert_eq!(text.to_string(), "hello");
text.insert(5, " world".into()).unwrap();
assert_eq!(text.to_string(), "hello world");
text.remove(5, 6).unwrap();
text.delete(5, 6).unwrap();
assert_eq!(text.to_string(), "hello");
}
}
2 changes: 1 addition & 1 deletion y-octo-node/tests/map.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ test("map editing", (t) => {
t.is(map.get("c"), 1);
t.is(map.get("d"), "hello world");
t.is(map.length, 4);
map.remove("b");
map.delete("b");
t.is(map.length, 3);
t.is(map.get("d"), "hello world");
});
Expand Down
6 changes: 3 additions & 3 deletions y-octo-node/tests/text.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ test("text editing", (t) => {
text.insert(1, "b");
text.insert(2, "c");
t.is(text.toString(), "abc");
text.remove(0, 1);
text.delete(0, 1);
t.is(text.toString(), "bc");
text.remove(1, 1);
text.delete(1, 1);
t.is(text.toString(), "b");
text.remove(0, 1);
text.delete(0, 1);
t.is(text.toString(), "");
});

Expand Down

0 comments on commit c9fb934

Please sign in to comment.