#![allow(clippy::result_large_err)]
#![allow(clippy::cast_possible_truncation)]
use std::sync::Arc;
use {
reovim_driver_session::bridges::{BridgeRegistry, ExtensionScope},
reovim_protocol::v2::{
ExtensionInfo, GetExtensionStateRequest, GetExtensionStateResponse, ListExtensionsRequest,
ListExtensionsResponse, extension_service_server::ExtensionService,
},
tonic::{Request, Response, Status},
};
use crate::{
grpc::auth::require_client_id,
session::{ClientId, Session, SessionId, SessionRegistry},
};
pub struct ExtensionServiceImpl {
sessions: Arc<SessionRegistry>,
default_session_id: SessionId,
bridges: Arc<BridgeRegistry>,
}
impl ExtensionServiceImpl {
#[must_use]
pub const fn new(
sessions: Arc<SessionRegistry>,
default_session_id: SessionId,
bridges: Arc<BridgeRegistry>,
) -> Self {
Self {
sessions,
default_session_id,
bridges,
}
}
fn get_session(&self) -> Result<Arc<Session>, Status> {
self.sessions
.get(&self.default_session_id)
.ok_or_else(|| Status::not_found("No active session"))
}
}
#[tonic::async_trait]
impl ExtensionService for ExtensionServiceImpl {
async fn get_state(
&self,
request: Request<GetExtensionStateRequest>,
) -> Result<Response<GetExtensionStateResponse>, Status> {
let token_client_id = request.extensions().get::<ClientId>().copied();
let req = request.into_inner();
let session = self.get_session()?;
let bridge = self
.bridges
.get(&req.kind)
.ok_or_else(|| Status::not_found(format!("Unknown extension kind: {}", req.kind)))?;
let (active, snapshot) = match bridge.scope() {
ExtensionScope::Client => {
let client_id = if req.client_id > 0 {
ClientId::new(req.client_id as usize)
} else {
require_client_id(token_client_id)?
};
session
.with_client_extensions(client_id, |extensions| {
let active = bridge.is_active(extensions);
let snap = bridge.snapshot(extensions);
(active, snap)
})
.unwrap_or((false, None))
}
ExtensionScope::Shared => {
(false, None)
}
};
Ok(Response::new(GetExtensionStateResponse {
active,
data: snapshot.map_or_else(String::new, |v| v.to_string()),
}))
}
async fn list_extensions(
&self,
_request: Request<ListExtensionsRequest>,
) -> Result<Response<ListExtensionsResponse>, Status> {
let extensions = self
.bridges
.kinds()
.into_iter()
.map(|kind| ExtensionInfo {
kind: kind.to_string(),
description: String::new(),
push_supported: true,
query_supported: true,
})
.collect();
let available_kinds = self
.bridges
.available_kinds()
.iter()
.map(|k| (*k).to_string())
.collect();
Ok(Response::new(ListExtensionsResponse {
extensions,
available_kinds,
}))
}
}
#[cfg(test)]
#[path = "extension_tests.rs"]
mod tests;