use loro_common::{ContainerID, ContainerType, LoroEncodeError, LoroValue, TreeID};
use loro_internal::cursor::PosType;
use loro_internal::loro::ExportMode as InternalExportMode;
use loro_internal::{
handler::counter::CounterHandler, ListHandler, LoroDoc, MapHandler, MovableListHandler,
TextHandler, ToJson, TreeHandler, TreeParentId,
};
use crate::bindings::exports::loro::crdt::document::{
Counter, Document, DocumentBorrow, Error, ExportMode, Guest, GuestCounter, GuestDocument,
GuestList, GuestMap, GuestMovableList, GuestText, GuestTree, GuestTreeNodeId, List, Map,
MovableList, Postype, Text, Tree, TreeNodeId,
};
use crate::bindings::loro::crdt::types::{
ContainerId as WitContainerId, ContainerType as WitContainerType, LoroValue as WitValue,
};
use crate::error::map_error;
/// Empty enum to implement Guest trait on
pub enum Loro {}
impl Guest for Loro {
type Document = LoroDoc;
type Text = TextHandler;
type List = ListHandler;
type Map = MapHandler;
type Tree = TreeHandler;
type MovableList = MovableListHandler;
type Counter = CounterHandler;
type TreeNodeId = TreeID;
}
impl GuestDocument for LoroDoc {
fn new() -> Self {
LoroDoc::new_auto_commit()
}
fn set_peer_id(&self, peer_id: u64) -> Result<(), Error> {
self.set_peer_id(peer_id).map_err(map_error)
}
fn commit(&self, _doc: DocumentBorrow<'_>) -> Result<(), Error> {
self.commit_then_renew();
Ok(())
}
fn export_doc(&self, mode: ExportMode) -> Result<Vec<u8>, Error> {
let mode = match mode {
ExportMode::Snapshot => InternalExportMode::snapshot(),
ExportMode::Updates => InternalExportMode::all_updates(),
ExportMode::UpdatesFromVersion => InternalExportMode::all_updates(),
};
self.export(mode).map_err(map_encode_error)
}
fn import_doc(&self, data: Vec<u8>) -> Result<(), Error> {
self.import(&data).map(|_| ()).map_err(map_error)
}
fn to_json(&self) -> Result<String, Error> {
Ok(self.get_deep_value().to_json())
}
fn fork(&self) -> Result<Document, Error> {
Ok(Document::new(self.fork()))
}
fn get_text(&self, id: String) -> Result<Text, Error> {
Ok(Text::new(self.get_text(id)))
}
fn get_list(&self, id: String) -> Result<List, Error> {
Ok(List::new(self.get_list(id)))
}
fn get_map(&self, id: String) -> Result<Map, Error> {
Ok(Map::new(self.get_map(id)))
}
fn get_tree(&self, id: String) -> Result<Tree, Error> {
Ok(Tree::new(self.get_tree(id)))
}
fn get_movable_list(&self, id: String) -> Result<MovableList, Error> {
Ok(MovableList::new(self.get_movable_list(id)))
}
fn get_counter(&self, id: String) -> Result<Counter, Error> {
Ok(Counter::new(self.get_counter(id)))
}
}
impl GuestText for TextHandler {
fn insert(&self, pos: u32, text: String, postype: Postype) -> Result<(), Error> {
self.insert(pos as usize, &text, PosType::from(postype))
.map_err(map_error)
}
fn delete(&self, pos: u32, len: u32) -> Result<(), Error> {
self.delete_utf16(pos as usize, len as usize)
.map_err(map_error)
}
fn to_string(&self) -> Result<String, Error> {
let value = self.get_richtext_value();
if let LoroValue::String(s) = value {
Ok(s.to_string())
} else {
Ok(value.to_json())
}
}
fn len(&self) -> Result<u32, Error> {
len_to_u32(self.len_utf16())
}
}
impl GuestList for ListHandler {
fn insert(&self, index: u32, value: WitValue) -> Result<(), Error> {
let value = to_internal_value(value)?;
self.insert(index as usize, value).map_err(map_error)
}
fn delete(&self, index: u32, count: u32) -> Result<(), Error> {
self.delete(index as usize, count as usize)
.map_err(map_error)
}
fn get(&self, index: u32) -> Result<Option<WitValue>, Error> {
self.get(index as usize).map(to_wit_value).transpose()
}
fn len(&self) -> Result<u32, Error> {
len_to_u32(self.len())
}
fn push(&self, value: WitValue) -> Result<(), Error> {
let value = to_internal_value(value)?;
self.push(value).map_err(map_error)
}
}
impl GuestMap for MapHandler {
fn insert(&self, key: String, value: WitValue) -> Result<(), Error> {
let value = to_internal_value(value)?;
self.insert(&key, value).map_err(map_error)
}
fn delete(&self, key: String) -> Result<(), Error> {
self.delete(&key).map_err(map_error)
}
fn get(&self, key: String) -> Result<Option<WitValue>, Error> {
self.get(&key).map(to_wit_value).transpose()
}
fn contains(&self, key: String) -> Result<bool, Error> {
Ok(self.get(&key).is_some())
}
fn keys(&self) -> Result<Vec<String>, Error> {
let mut keys = Vec::new();
self.for_each(|k, _| keys.push(k.to_string()));
Ok(keys)
}
}
impl GuestMovableList for MovableListHandler {
fn insert(&self, index: u32, value: WitValue) -> Result<(), Error> {
let value = to_internal_value(value)?;
self.insert(index as usize, value).map_err(map_error)
}
fn delete(&self, index: u32, count: u32) -> Result<(), Error> {
self.delete(index as usize, count as usize)
.map_err(map_error)
}
fn move_item(&self, from_index: u32, to_index: u32) -> Result<(), Error> {
self.mov(from_index as usize, to_index as usize)
.map_err(map_error)
}
fn get(&self, index: u32) -> Result<Option<WitValue>, Error> {
self.get(index as usize).map(to_wit_value).transpose()
}
fn len(&self) -> Result<u32, Error> {
len_to_u32(self.len())
}
}
impl GuestTree for TreeHandler {
fn create(
&self,
parent: Option<crate::bindings::exports::loro::crdt::document::TreeNodeIdBorrow<'_>>,
) -> Result<TreeNodeId, Error> {
let parent_id = parent.map(|p| *p.get::<TreeID>());
self.create(TreeParentId::from(parent_id))
.map(TreeNodeId::new)
.map_err(map_error)
}
fn move_node(
&self,
node: crate::bindings::exports::loro::crdt::document::TreeNodeIdBorrow<'_>,
parent: Option<crate::bindings::exports::loro::crdt::document::TreeNodeIdBorrow<'_>>,
) -> Result<(), Error> {
let node_id = *node.get::<TreeID>();
let parent_id = parent.map(|p| *p.get::<TreeID>());
self.mov(node_id, TreeParentId::from(parent_id))
.map_err(map_error)
}
fn delete(
&self,
node: crate::bindings::exports::loro::crdt::document::TreeNodeIdBorrow<'_>,
) -> Result<(), Error> {
self.delete(*node.get::<TreeID>()).map_err(map_error)
}
}
impl GuestTreeNodeId for TreeID {}
impl GuestCounter for CounterHandler {
fn increment(&self, value: f64) -> Result<(), Error> {
CounterHandler::increment(self, value).map_err(map_error)
}
fn decrement(&self, value: f64) -> Result<(), Error> {
CounterHandler::decrement(self, value).map_err(map_error)
}
fn get_value(&self) -> Result<f64, Error> {
use loro_internal::HandlerTrait;
Ok(HandlerTrait::get_value(self).into_double().unwrap_or(0.0))
}
}
impl From<Postype> for PosType {
fn from(value: Postype) -> Self {
match value {
Postype::Bytes => PosType::Bytes,
Postype::Unicode => PosType::Unicode,
Postype::Utf16 => PosType::Utf16,
Postype::Event => PosType::Event,
Postype::Entity => PosType::Entity,
}
}
}
fn to_internal_container_type(ty: WitContainerType) -> ContainerType {
match ty {
WitContainerType::Text => ContainerType::Text,
WitContainerType::List => ContainerType::List,
WitContainerType::Map => ContainerType::Map,
WitContainerType::Tree => ContainerType::Tree,
WitContainerType::MovableList => ContainerType::MovableList,
WitContainerType::Counter => ContainerType::Counter,
}
}
fn to_wit_container_type(ty: ContainerType) -> Result<WitContainerType, Error> {
match ty {
ContainerType::Text => Ok(WitContainerType::Text),
ContainerType::List => Ok(WitContainerType::List),
ContainerType::Map => Ok(WitContainerType::Map),
ContainerType::Tree => Ok(WitContainerType::Tree),
ContainerType::MovableList => Ok(WitContainerType::MovableList),
ContainerType::Counter => Ok(WitContainerType::Counter),
_ => Err(Error::NotImplemented(format!(
"Unsupported container type for WIT mapping: {:?}",
ty
))),
}
}
fn to_internal_container_id(id: WitContainerId) -> Result<ContainerID, Error> {
match ContainerID::try_from(id.id.as_str()) {
Ok(cid) => Ok(cid),
Err(_err) => {
let container_type = to_internal_container_type(id.container_type);
Ok(ContainerID::new_root(&id.id, container_type))
}
}
}
fn to_wit_container_id(id: &ContainerID) -> Result<WitContainerId, Error> {
Ok(WitContainerId {
id: format!("{}", id),
container_type: to_wit_container_type(id.container_type())?,
})
}
fn to_internal_value(value: WitValue) -> Result<LoroValue, Error> {
match value {
WitValue::Null => Ok(LoroValue::Null),
WitValue::Bool(v) => Ok(LoroValue::Bool(v)),
WitValue::I64(v) => Ok(LoroValue::I64(v)),
WitValue::F64(v) => Ok(LoroValue::Double(v)),
WitValue::String(v) => Ok(LoroValue::String(v.into())),
WitValue::Binary(v) => Ok(LoroValue::Binary(v.into())),
WitValue::Container(c) => to_internal_container_id(c).map(LoroValue::Container),
}
}
fn to_wit_value(value: LoroValue) -> Result<WitValue, Error> {
match value {
LoroValue::Null => Ok(WitValue::Null),
LoroValue::Bool(v) => Ok(WitValue::Bool(v)),
LoroValue::Double(v) => Ok(WitValue::F64(v)),
LoroValue::I64(v) => Ok(WitValue::I64(v)),
LoroValue::Binary(v) => Ok(WitValue::Binary((*v).clone())),
LoroValue::String(v) => Ok(WitValue::String(v.to_string())),
LoroValue::Container(id) => Ok(WitValue::Container(to_wit_container_id(&id)?)),
other => Err(Error::NotImplemented(format!(
"Cannot convert LoroValue variant {:?} to WIT value",
other
))),
}
}
fn len_to_u32(len: usize) -> Result<u32, Error> {
u32::try_from(len).map_err(|_| Error::ArgError("Length exceeds u32".into()))
}
fn map_encode_error(err: LoroEncodeError) -> Error {
Error::Unknown(format!("Encode error: {err}"))
}
Hey folks,
I've been trying to add
witsupport for loro.Since loro is split between the
loro_internalandlorocrates, generating the facade from a wit file works quite well.for instance the following
wit file
can be implemented with
rust file
The public facade in loro maps so well to the types, I could tab tab auto complete all most of it, (which is why this looks like it was vibe coded, whoops, but the point about how mapping a wit generted interfaces to loro_internal types stands).
Now the actual problem I have is this bit of code here
which breaks building things. Would you folks be up for me sending some wit compat patches or would that be out of scope?