use serde::{
Deserialize,
Serialize,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct ClientId(u64);
impl ClientId {
pub const INTERNAL: ClientId = ClientId(0);
pub(crate) fn from_raw(id: u64) -> Self {
Self(id)
}
#[must_use]
pub const fn as_u64(self) -> u64 {
self.0
}
}
impl std::fmt::Display for ClientId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "client-{}", self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ClientKind {
Ide,
Cli,
Mcp,
}
impl std::fmt::Display for ClientKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
| ClientKind::Ide => write!(f, "ide"),
| ClientKind::Cli => write!(f, "cli"),
| ClientKind::Mcp => write!(f, "mcp"),
}
}
}
pub fn wants_lsp_notifications(kind: ClientKind) -> bool {
!matches!(kind, ClientKind::Mcp)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn client_id_from_raw() {
let id1 = ClientId::from_raw(1);
let id2 = ClientId::from_raw(2);
let id3 = ClientId::from_raw(3);
assert_ne!(id1, id2);
assert_ne!(id2, id3);
assert_ne!(id1, id3);
}
#[test]
fn client_id_internal_is_zero() {
assert_eq!(ClientId::INTERNAL.as_u64(), 0);
}
#[test]
fn client_id_display() {
let id = ClientId::from_raw(42);
let display = format!("{}", id);
assert_eq!(display, "client-42");
}
#[test]
fn client_kind_display() {
assert_eq!(format!("{}", ClientKind::Ide), "ide");
assert_eq!(format!("{}", ClientKind::Cli), "cli");
assert_eq!(format!("{}", ClientKind::Mcp), "mcp");
}
#[test]
fn client_id_serde_transparent() {
let id = ClientId::from_raw(42);
let serialized = serde_json::to_string(&id).unwrap();
assert_eq!(serialized, "42");
let deserialized: ClientId = serde_json::from_str("42").unwrap();
assert_eq!(deserialized, id);
}
}