mirror of
https://github.com/loro-dev/loro.git
synced 2025-01-23 05:24:51 +00:00
fix: container id should be converted to js string
This commit is contained in:
parent
8f42a0b9d9
commit
f27786fa25
2 changed files with 86 additions and 72 deletions
|
@ -156,9 +156,15 @@ pub trait ContainerTrait: Debug + Any + Unpin + Send + Sync {
|
|||
/// [ContainerID] includes the Op's [ID] and the type. So it's impossible to have
|
||||
/// the same [ContainerID] with conflict [ContainerType].
|
||||
///
|
||||
/// This structure is really cheap to clone
|
||||
/// This structure is really cheap to clone.
|
||||
///
|
||||
/// String representation:
|
||||
///
|
||||
/// - Root Container: `/<name>:<type>`
|
||||
/// - Normal Container: `<counter>@<client>:<type>`
|
||||
///
|
||||
/// Note: It will be encoded into binary format, so the order of its fields should not be changed.
|
||||
#[derive(Hash, PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
|
||||
// Note: It will be encoded into binary format, so the order of its fields should not be changed.
|
||||
pub enum ContainerID {
|
||||
/// Root container does not need an op to create. It can be created implicitly.
|
||||
Root {
|
||||
|
@ -171,6 +177,49 @@ pub enum ContainerID {
|
|||
},
|
||||
}
|
||||
|
||||
impl Display for ContainerID {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ContainerID::Root {
|
||||
name,
|
||||
container_type,
|
||||
} => f.write_fmt(format_args!("/{}:{}", name, container_type))?,
|
||||
ContainerID::Normal { id, container_type } => {
|
||||
f.write_fmt(format_args!("{}:{}", id, container_type))?
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for ContainerID {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||
let mut parts = value.split(':');
|
||||
let id = parts.next().ok_or(())?;
|
||||
let container_type = parts.next().ok_or(())?;
|
||||
let container_type = ContainerType::try_from(container_type).map_err(|_| ())?;
|
||||
if let Some(id) = id.strip_prefix('/') {
|
||||
Ok(ContainerID::Root {
|
||||
name: id.into(),
|
||||
container_type,
|
||||
})
|
||||
} else {
|
||||
let mut parts = id.split('@');
|
||||
let counter = parts.next().ok_or(())?.parse().map_err(|_| ())?;
|
||||
let client = parts.next().ok_or(())?.parse().map_err(|_| ())?;
|
||||
Ok(ContainerID::Normal {
|
||||
id: ID {
|
||||
counter,
|
||||
client_id: client,
|
||||
},
|
||||
container_type,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ContainerIdRaw {
|
||||
Root { name: InternalString },
|
||||
Normal { id: ID },
|
||||
|
@ -258,3 +307,29 @@ impl ContainerID {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn container_id_convert() {
|
||||
let container_id = ContainerID::new_normal(ID::new(12, 12), ContainerType::List);
|
||||
let s = container_id.to_string();
|
||||
assert_eq!(s, "12@12:List");
|
||||
let actual = ContainerID::try_from(s.as_str()).unwrap();
|
||||
assert_eq!(actual, container_id);
|
||||
|
||||
let container_id = ContainerID::new_root("123", ContainerType::Map);
|
||||
let s = container_id.to_string();
|
||||
assert_eq!(s, "/123:Map");
|
||||
let actual = ContainerID::try_from(s.as_str()).unwrap();
|
||||
assert_eq!(actual, container_id);
|
||||
|
||||
let container_id = ContainerID::new_root("kkk", ContainerType::Text);
|
||||
let s = container_id.to_string();
|
||||
assert_eq!(s, "/kkk:Text");
|
||||
let actual = ContainerID::try_from(s.as_str()).unwrap();
|
||||
assert_eq!(actual, container_id);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -404,38 +404,7 @@ pub mod wasm {
|
|||
|
||||
impl From<ContainerID> for JsValue {
|
||||
fn from(id: ContainerID) -> Self {
|
||||
let map = Object::new();
|
||||
match id {
|
||||
ContainerID::Root {
|
||||
name,
|
||||
container_type,
|
||||
} => {
|
||||
js_sys::Reflect::set(
|
||||
&map,
|
||||
&JsValue::from_str("root"),
|
||||
&name.to_string().into(),
|
||||
)
|
||||
.unwrap();
|
||||
js_sys::Reflect::set(
|
||||
&map,
|
||||
&JsValue::from_str("type"),
|
||||
&container_type.to_string().into(),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
ContainerID::Normal { id, container_type } => {
|
||||
js_sys::Reflect::set(&map, &JsValue::from_str("id"), &id.to_string().into())
|
||||
.unwrap();
|
||||
js_sys::Reflect::set(
|
||||
&map,
|
||||
&JsValue::from_str("type"),
|
||||
&container_type.to_string().into(),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
map.into_js_result().unwrap()
|
||||
JsValue::from_str(id.to_string().as_str())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -443,48 +412,18 @@ pub mod wasm {
|
|||
type Error = LoroError;
|
||||
|
||||
fn try_from(value: JsValue) -> Result<Self, Self::Error> {
|
||||
if !value.is_object() {
|
||||
if !value.is_string() {
|
||||
return Err(LoroError::DecodeError(
|
||||
"Given ContainerId is not an object".into(),
|
||||
"Given ContainerId is not string".into(),
|
||||
));
|
||||
}
|
||||
|
||||
let object = value.unchecked_into::<Object>();
|
||||
if js_sys::Reflect::has(&object, &"id".into())? {
|
||||
let Some(id) = js_sys::Reflect::get(&object, &"id".into())
|
||||
.unwrap()
|
||||
.as_string() else {
|
||||
return Err(LoroError::DecodeError("ContainerId is not valid".into()));
|
||||
};
|
||||
let Ok(Some(Ok(container_type))) = js_sys::Reflect::get(&object, &"type".into())
|
||||
.map(|x| {
|
||||
x.as_string().map(|y| ContainerType::try_from(y.as_str()))
|
||||
}) else {
|
||||
return Err(LoroError::DecodeError("ContainerId is not valid".into()));
|
||||
};
|
||||
Ok(ContainerID::Normal {
|
||||
id: ID::try_from(id.as_str())?,
|
||||
container_type,
|
||||
let s = value.as_string().unwrap();
|
||||
ContainerID::try_from(s.as_str()).map_err(|_| {
|
||||
LoroError::DecodeError(
|
||||
format!("Given ContainerId is not a valid ContainerID: {}", s).into(),
|
||||
)
|
||||
})
|
||||
} else if js_sys::Reflect::has(&object, &"root".into())? {
|
||||
let Some(name) = js_sys::Reflect::get(&object, &"root".into())
|
||||
.unwrap()
|
||||
.as_string() else {
|
||||
return Err(LoroError::DecodeError("ContainerId is not valid".into()));
|
||||
};
|
||||
let Ok(Some(Ok(container_type))) = js_sys::Reflect::get(&object, &"type".into())
|
||||
.map(|x| {
|
||||
x.as_string().map(|y| ContainerType::try_from(y.as_str()))
|
||||
}) else {
|
||||
return Err(LoroError::DecodeError("ContainerId is not valid".into()));
|
||||
};
|
||||
Ok(ContainerID::Root {
|
||||
name: name.into(),
|
||||
container_type,
|
||||
})
|
||||
} else {
|
||||
Err(LoroError::DecodeError("ContainerId is not valid".into()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue