use std::any::Any; use std::fmt::{self, Debug}; use std::marker::PhantomData; use std::sync::Arc; use super::{ObjectType, ServerMetadata, ServerPermissions}; use crate::ids::SyncId; #[derive(Clone, Debug, Default)] pub enum ConflictStatus { #[default] NoConflicts, ConflictingChanges { object: Arc, }, } impl ConflictStatus { /// Utility function that allows for a more ergonomic way of figuring out whether there is a /// conflict (for cases where we don't care about the conflict details). pub fn has_conflicts(&self) -> bool { matches!(self, ConflictStatus::ConflictingChanges { .. }) } } /// Common behavior that server-backed models expose to generic server objects. pub trait ServerObjectModel: Debug + Clone + Send + Sync + 'static { /// Returns the object type for this model. fn object_type(&self) -> ObjectType; } /// Common trait for server objects that allows us to use them as trait objects /// and downcast to concrete types when needed. pub trait ServerObject: Debug + Send + Sync { /// Returns the object type of this server object fn object_type(&self) -> ObjectType; /// Returns this object as a ref to the Any type. Needed for typecasts. fn as_any(&self) -> &dyn Any; /// Returns the trait object as a concrete type reference by downcasting it. /// Returns None if the downcast fails. fn as_concrete_type( server_object: &dyn ServerObject, ) -> Option<&GenericServerObject> where Self: Sized, K: 'static, M: 'static, { server_object .as_any() .downcast_ref::>() } /// Returns a cloned boxed version of this server object. /// Note that we can't force the ServerObject trait to derive from Cloned /// directly because that would make the trait not object safe. This /// is a workaround. fn clone_box(&self) -> Box; } /// An object that maps directly to the data returned from the server /// for a given model and id type. pub struct GenericServerObject { pub id: SyncId, pub model: M, pub metadata: ServerMetadata, pub permissions: ServerPermissions, _marker: PhantomData K>, } impl Clone for GenericServerObject where M: Clone, { fn clone(&self) -> Self { Self::new( self.id, self.model.clone(), self.metadata.clone(), self.permissions.clone(), ) } } impl Debug for GenericServerObject where M: Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("GenericServerObject") .field("id", &self.id) .field("model", &self.model) .field("metadata", &self.metadata) .field("permissions", &self.permissions) .finish() } } impl GenericServerObject { /// Constructs a server object from its server-provided parts. pub fn new( id: SyncId, model: M, metadata: ServerMetadata, permissions: ServerPermissions, ) -> Self { Self { id, model, metadata, permissions, _marker: PhantomData, } } } impl<'a, K, M> From<&'a dyn ServerObject> for Option<&'a GenericServerObject> where K: 'static, M: 'static, { fn from(value: &'a dyn ServerObject) -> Self { value.as_any().downcast_ref::>() } } impl<'a, K, M> From<&'a Box> for Option<&'a GenericServerObject> where K: 'static, M: 'static, { fn from(value: &'a Box) -> Self { value.as_ref().into() } } impl ServerObject for GenericServerObject where K: 'static, M: ServerObjectModel, { fn object_type(&self) -> ObjectType { self.model.object_type() } fn as_any(&self) -> &dyn Any { self } fn clone_box(&self) -> Box { Box::new(self.clone()) } }