use {
super::{
ClientId,
ClientKind,
Subscriptions,
},
crate::{connect::ipc::Connection, protocol::lsp::PositionEncodingKind},
std::{
collections::HashMap,
time::{
Instant,
SystemTime,
UNIX_EPOCH,
},
},
};
pub struct ConnectedClient {
id: ClientId,
kind: ClientKind,
connection: Connection,
subscriptions: Subscriptions,
connected_at: Instant,
connected_at_unix: u64,
client_name: Option<String>,
client_version: Option<String>,
metadata: HashMap<String, String>,
position_encoding: PositionEncodingKind,
}
impl ConnectedClient {
pub(super) fn with_id(
id: ClientId,
kind: ClientKind,
connection: Connection,
metadata: HashMap<String, String>,
) -> Self {
let client_name = metadata.get("client_name").cloned();
let client_version = metadata.get("client_version").cloned();
let connected_at_unix = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0);
Self {
id,
kind,
connection,
subscriptions: Subscriptions::new(),
connected_at: Instant::now(),
connected_at_unix,
client_name,
client_version,
metadata,
position_encoding: PositionEncodingKind::DEFAULT,
}
}
#[must_use]
pub const fn id(&self) -> ClientId {
self.id
}
#[must_use]
pub const fn kind(&self) -> ClientKind {
self.kind
}
#[must_use]
pub fn connection(&self) -> &Connection {
&self.connection
}
#[must_use]
pub fn subscriptions(&self) -> &Subscriptions {
&self.subscriptions
}
pub fn subscriptions_mut(&mut self) -> &mut Subscriptions {
&mut self.subscriptions
}
pub fn subscribe(&mut self, topic: impl super::Topic) {
self.subscriptions.subscribe(topic);
}
pub fn unsubscribe(&mut self, topic: impl super::Topic) {
self.subscriptions.unsubscribe(topic);
}
pub fn is_subscribed(&self, topic: impl super::Topic) -> bool {
self.subscriptions.is_subscribed(topic)
}
#[must_use]
pub const fn connected_at(&self) -> Instant {
self.connected_at
}
#[must_use]
pub const fn connected_at_unix(&self) -> u64 {
self.connected_at_unix
}
#[must_use]
pub fn client_name(&self) -> Option<&str> {
self.client_name.as_deref()
}
#[must_use]
pub fn client_version(&self) -> Option<&str> {
self.client_version.as_deref()
}
#[must_use]
pub fn metadata(&self) -> &HashMap<String, String> {
&self.metadata
}
pub fn metadata_mut(&mut self) -> &mut HashMap<String, String> {
&mut self.metadata
}
#[must_use]
pub fn position_encoding(&self) -> &PositionEncodingKind {
&self.position_encoding
}
pub fn set_position_encoding(&mut self, encoding: PositionEncodingKind) {
self.position_encoding = encoding;
}
}
impl std::fmt::Debug for ConnectedClient {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ConnectedClient")
.field("id", &self.id)
.field("kind", &self.kind)
.field("client_name", &self.client_name)
.field("client_version", &self.client_version)
.field("subscriptions", &self.subscriptions)
.field("connected_at", &self.connected_at)
.field("metadata", &self.metadata)
.field("position_encoding", &self.position_encoding)
.finish_non_exhaustive()
}
}
#[cfg(test)]
mod tests {
use {
super::super::ClientRegistry,
super::*,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum TestTopic {
Diagnostics,
}
impl super::super::Topic for TestTopic {
fn name(&self) -> &'static str {
match self {
| TestTopic::Diagnostics => "diagnostics",
}
}
}
#[test]
fn client_new_via_registry() {
let registry = ClientRegistry::new();
let (server_conn, _client_conn) = Connection::memory();
let id = registry.register(ClientKind::Cli, server_conn, HashMap::new());
let client = registry.get(id).unwrap();
assert_eq!(client.kind(), ClientKind::Cli);
assert!(!client.is_subscribed(TestTopic::Diagnostics));
assert!(client.metadata().is_empty());
}
#[test]
fn client_connected_at() {
let start = Instant::now();
let registry = ClientRegistry::new();
let (server_conn, _client_conn) = Connection::memory();
let id = registry.register(ClientKind::Ide, server_conn, HashMap::new());
let client = registry.get(id).unwrap();
assert!(client.connected_at() >= start);
assert!(client.connected_at() <= Instant::now());
}
#[test]
fn client_metadata() {
let registry = ClientRegistry::new();
let (server_conn, _client_conn) = Connection::memory();
let mut metadata = HashMap::new();
metadata.insert("client_name".to_string(), "vscode".to_string());
metadata.insert("version".to_string(), "1.85.0".to_string());
let id = registry.register(ClientKind::Ide, server_conn, metadata);
let client = registry.get(id).unwrap();
assert_eq!(client.metadata().get("client_name"), Some(&"vscode".to_string()));
assert_eq!(
client.metadata().get("version"),
Some(&"1.85.0".to_string())
);
}
#[test]
fn client_metadata_mut() {
let registry = ClientRegistry::new();
let (server_conn, _client_conn) = Connection::memory();
let id = registry.register(ClientKind::Cli, server_conn, HashMap::new());
{
let mut client = registry.get_mut(id).unwrap();
client
.metadata_mut()
.insert("key".to_string(), "value".to_string());
}
let client = registry.get(id).unwrap();
assert_eq!(client.metadata().get("key"), Some(&"value".to_string()));
}
}