use std::{marker::PhantomData, sync::Arc};
use crate::{
entity::Entity,
error::MutationError,
property::{
backend::{PropertyBackend, YrsBackend},
traits::{FromActiveType, FromEntity, InitializeWith, PropertyError},
PropertyName,
},
};
use ankurah_signals::{
signal::{Listener, ListenerGuard},
Signal,
};
#[derive(Debug, Clone)]
pub struct YrsString<Projected> {
pub property_name: PropertyName,
pub backend: Arc<YrsBackend>,
pub entity: Entity,
phantom: PhantomData<Projected>,
}
impl<Projected> YrsString<Projected> {
pub fn new(property_name: PropertyName, backend: Arc<YrsBackend>, entity: Entity) -> Self {
Self { property_name, backend, entity, phantom: PhantomData }
}
pub fn value(&self) -> Option<String> { self.backend.get_string(&self.property_name) }
pub fn insert(&self, index: u32, value: &str) -> Result<(), MutationError> {
if !self.entity.is_writable() {
return Err(PropertyError::TransactionClosed.into());
}
self.backend.insert(&self.property_name, index, value)
}
pub fn delete(&self, index: u32, length: u32) -> Result<(), MutationError> {
if !self.entity.is_writable() {
return Err(PropertyError::TransactionClosed.into());
}
self.backend.delete(&self.property_name, index, length)
}
pub fn overwrite(&self, start: u32, length: u32, value: &str) -> Result<(), MutationError> {
if !self.entity.is_writable() {
return Err(PropertyError::TransactionClosed.into());
}
self.backend.delete(&self.property_name, start, length)?;
self.backend.insert(&self.property_name, start, value)?;
Ok(())
}
pub fn replace(&self, value: &str) -> Result<(), MutationError> {
if !self.entity.is_writable() {
return Err(PropertyError::TransactionClosed.into());
}
self.backend.delete(&self.property_name, 0, self.value().unwrap_or_default().len() as u32)?;
self.backend.insert(&self.property_name, 0, value)?;
Ok(())
}
}
impl<Projected> FromEntity for YrsString<Projected> {
fn from_entity(property_name: PropertyName, entity: &Entity) -> Self {
let backend = entity.get_backend::<YrsBackend>().expect("YrsBackend should exist");
Self::new(property_name, backend, entity.clone())
}
}
impl<Projected, S: FromActiveType<YrsString<Projected>>> FromActiveType<YrsString<Projected>> for Option<S> {
fn from_active(active: YrsString<Projected>) -> Result<Self, PropertyError> {
match S::from_active(active) {
Ok(value) => Ok(Some(value)),
Err(PropertyError::Missing) => Ok(None),
Err(err) => Err(err),
}
}
}
impl<Projected> FromActiveType<YrsString<Projected>> for String {
fn from_active(active: YrsString<Projected>) -> Result<Self, PropertyError> {
match active.value() {
Some(value) => Ok(value),
None => Err(PropertyError::Missing),
}
}
}
impl<'a, Projected> FromActiveType<YrsString<Projected>> for std::borrow::Cow<'a, str> {
fn from_active(active: YrsString<Projected>) -> Result<Self, PropertyError> {
match active.value() {
Some(value) => Ok(Self::from(value)),
None => Err(PropertyError::Missing),
}
}
}
impl<Projected> InitializeWith<String> for YrsString<Projected> {
fn initialize_with(entity: &Entity, property_name: PropertyName, value: &String) -> Self {
let new_string = Self::from_entity(property_name, entity);
new_string.insert(0, value).unwrap();
new_string
}
}
impl<Projected> InitializeWith<Option<String>> for YrsString<Projected> {
fn initialize_with(entity: &Entity, property_name: PropertyName, value: &Option<String>) -> Self {
let new_string = Self::from_entity(property_name, entity);
if let Some(value) = value {
new_string.insert(0, value).unwrap();
}
new_string
}
}
impl<Projected> ankurah_signals::Signal for YrsString<Projected> {
fn listen(&self, listener: Listener) -> ListenerGuard { self.backend.listen_field(&self.property_name, listener) }
fn broadcast_id(&self) -> ankurah_signals::broadcast::BroadcastId { self.backend.field_broadcast_id(&self.property_name) }
}
impl<Projected> ankurah_signals::Subscribe<String> for YrsString<Projected>
where Projected: Clone + Send + Sync + 'static
{
fn subscribe<F>(&self, listener: F) -> ankurah_signals::SubscriptionGuard
where F: ankurah_signals::subscribe::IntoSubscribeListener<String> {
let listener = listener.into_subscribe_listener();
let yrs_string = self.clone();
let subscription = self.listen(Arc::new(move |_| {
if let Some(current_value) = yrs_string.value() {
listener(current_value);
}
}));
ankurah_signals::SubscriptionGuard::new(subscription)
}
}
#[cfg(any(feature = "wasm", feature = "uniffi"))]
pub mod ffi {
use super::*;
#[cfg(feature = "wasm")]
use ::wasm_bindgen::prelude::*;
use ankurah_derive::impl_provided_wrapper_types;
impl_provided_wrapper_types!("src/property/value/yrs.ron");
}
#[cfg(any(feature = "wasm", feature = "uniffi"))]
pub use ffi::*;