use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "wasm", derive(tsify_next::Tsify))]
#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
pub enum ClientRelation {
Following {
target: u64,
},
Sharing {
with: u64,
},
}
impl ClientRelation {
#[must_use]
pub const fn following(target: u64) -> Self {
Self::Following { target }
}
#[must_use]
pub const fn sharing(with: u64) -> Self {
Self::Sharing { with }
}
#[must_use]
pub const fn is_following(&self) -> bool {
matches!(self, Self::Following { .. })
}
#[must_use]
pub const fn is_sharing(&self) -> bool {
matches!(self, Self::Sharing { .. })
}
#[must_use]
pub const fn target_id(&self) -> u64 {
match *self {
Self::Following { target } => target,
Self::Sharing { with } => with,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
#[cfg_attr(feature = "wasm", derive(tsify_next::Tsify))]
#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
pub struct ClientViewState {
pub mode: String,
pub cursor: (u32, u32),
pub buffer_id: Option<u64>,
pub selection: Option<((u32, u32), (u32, u32))>,
}
impl ClientViewState {
#[must_use]
pub fn new(mode: impl Into<String>) -> Self {
Self {
mode: mode.into(),
cursor: (0, 0),
buffer_id: None,
selection: None,
}
}
#[must_use]
pub const fn with_cursor(mut self, line: u32, column: u32) -> Self {
self.cursor = (line, column);
self
}
#[must_use]
pub const fn with_buffer(mut self, buffer_id: u64) -> Self {
self.buffer_id = Some(buffer_id);
self
}
#[must_use]
pub const fn with_selection(mut self, start: (u32, u32), end: (u32, u32)) -> Self {
self.selection = Some((start, end));
self
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "wasm", derive(tsify_next::Tsify))]
#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
pub struct ClientMetadata {
pub client_type: String,
pub display_name: String,
pub joined_at_ms: u64,
}
impl ClientMetadata {
#[must_use]
pub fn new(client_type: impl Into<String>, display_name: impl Into<String>) -> Self {
Self {
client_type: client_type.into(),
display_name: display_name.into(),
joined_at_ms: 0,
}
}
#[must_use]
pub const fn with_joined_at(mut self, joined_at_ms: u64) -> Self {
self.joined_at_ms = joined_at_ms;
self
}
}
impl Default for ClientMetadata {
fn default() -> Self {
Self::new("unknown", "unknown")
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "wasm", derive(tsify_next::Tsify))]
#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
pub struct ClientInfo {
pub id: u64,
pub relation: Option<ClientRelation>,
pub view: ClientViewState,
pub metadata: ClientMetadata,
}
impl ClientInfo {
#[must_use]
pub fn new(id: u64, metadata: ClientMetadata) -> Self {
Self {
id,
relation: None,
view: ClientViewState::default(),
metadata,
}
}
#[must_use]
pub const fn is_independent(&self) -> bool {
self.relation.is_none()
}
#[must_use]
pub const fn is_following(&self) -> bool {
matches!(self.relation, Some(ClientRelation::Following { .. }))
}
#[must_use]
pub const fn is_sharing(&self) -> bool {
matches!(self.relation, Some(ClientRelation::Sharing { .. }))
}
#[must_use]
pub const fn target_id(&self) -> Option<u64> {
match self.relation {
Some(ClientRelation::Following { target }) => Some(target),
Some(ClientRelation::Sharing { with }) => Some(with),
None => None,
}
}
}
#[cfg(test)]
#[path = "client_tests.rs"]
mod tests;