use crate::config::ReplConfig;
use crate::repl::runner::{ReplClientAdapter, ReplRunner};
use crate::repl::stats::{
show_cache_from_snapshot, show_execution_from_snapshot, show_server_stats,
show_session_summary_from_snapshot, show_subprocess_stats,
};
use crate::repl::terminal::ReplTerminal;
use anyhow::{Context, Result};
use async_trait::async_trait;
use oxur_repl::protocol::{
MessageId, Operation, OperationResult, ReplMode, Request, Response, SessionId,
};
use oxur_repl::transport::{TcpTransport, Transport};
use std::sync::atomic::{AtomicU64, Ordering};
struct TcpAdapter {
transport: TcpTransport,
session_id: SessionId,
msg_counter: AtomicU64,
}
impl TcpAdapter {
fn new(transport: TcpTransport, session_id: SessionId) -> Self {
Self { transport, session_id, msg_counter: AtomicU64::new(1000) }
}
fn next_message_id(&self) -> MessageId {
MessageId::new(self.msg_counter.fetch_add(1, Ordering::SeqCst))
}
async fn request_stats(&mut self, operation: Operation) -> Result<OperationResult> {
let request =
Request { id: self.next_message_id(), session_id: self.session_id.clone(), operation };
self.transport.send_request(&request).await?;
let response = self.transport.recv_response().await?;
Ok(response.result)
}
}
#[async_trait]
impl ReplClientAdapter for TcpAdapter {
async fn send_eval(&mut self, request: Request) -> Result<()> {
self.transport.send_request(&request).await.context("Failed to send request")
}
async fn recv_response(&mut self) -> Result<Response> {
self.transport.recv_response().await.context("Failed to receive response")
}
async fn close(&mut self) -> Result<()> {
self.transport.close().await.context("Failed to close connection")
}
fn current_session(&self) -> &SessionId {
&self.session_id
}
async fn handle_special_command(&mut self, input: &str, color_enabled: bool) -> Option<String> {
if !input.starts_with("(stats") {
return None;
}
match input {
"(stats server)" => match self.request_stats(Operation::GetServerStats).await {
Ok(OperationResult::ServerStats { snapshot }) => {
Some(show_server_stats(&snapshot, color_enabled))
}
Ok(_) => Some("Unexpected response from server".to_string()),
Err(e) => Some(format!("Failed to get server stats: {}", e)),
},
"(stats subprocess)" => match self.request_stats(Operation::GetSubprocessStats).await {
Ok(OperationResult::SubprocessStats { snapshot }) => {
Some(show_subprocess_stats(&snapshot, color_enabled))
}
Ok(_) => Some("Unexpected response from server".to_string()),
Err(e) => Some(format!("Failed to get subprocess stats: {}", e)),
},
"(stats)" => match self.request_stats(Operation::GetSessionStats).await {
Ok(OperationResult::SessionStats { snapshot }) => {
Some(show_session_summary_from_snapshot(&snapshot, color_enabled))
}
Ok(_) => Some("Unexpected response from server".to_string()),
Err(e) => Some(format!("Failed to get session stats: {}", e)),
},
"(stats execution)" => match self.request_stats(Operation::GetSessionStats).await {
Ok(OperationResult::SessionStats { snapshot }) => {
Some(show_execution_from_snapshot(&snapshot, color_enabled))
}
Ok(_) => Some("Unexpected response from server".to_string()),
Err(e) => Some(format!("Failed to get session stats: {}", e)),
},
"(stats cache)" => match self.request_stats(Operation::GetSessionStats).await {
Ok(OperationResult::SessionStats { snapshot }) => {
Some(show_cache_from_snapshot(&snapshot, color_enabled))
}
Ok(_) => Some("Unexpected response from server".to_string()),
Err(e) => Some(format!("Failed to get session stats: {}", e)),
},
"(stats resources)" => {
Some("Resource stats not available in remote mode (requires local filesystem access)".to_string())
}
_ => {
Some(format!(
"Unknown stats command: {}. Try (help stats) for available commands.",
input
))
}
}
}
}
pub async fn run(addr: &str, config: ReplConfig) -> Result<()> {
let system_metadata = oxur_repl::metadata::SystemMetadata::capture();
let transport = TcpTransport::connect(addr)
.await
.context(format!("Failed to connect to REPL server at {}", addr))?;
let session_id = SessionId::new(format!("connect-{}", std::process::id()));
let mut adapter = TcpAdapter::new(transport, session_id.clone());
let create_req = Request {
id: adapter.next_message_id(),
session_id: session_id.clone(),
operation: Operation::CreateSession { mode: ReplMode::Lisp },
};
adapter.send_eval(create_req).await.context("Failed to send create session request")?;
let _response = adapter.recv_response().await.context("Failed to receive create response")?;
let terminal = ReplTerminal::with_config(config.terminal, config.history)
.context("Failed to create terminal")?;
let mut runner = ReplRunner::new(terminal, session_id);
runner.print_banner(&system_metadata);
print_connection_info(addr, runner.terminal().config().color_enabled);
runner.run(&mut adapter).await?;
runner.finish(&mut adapter).await?;
Ok(())
}
fn print_connection_info(addr: &str, color_enabled: bool) {
if color_enabled {
println!("\x1b[36mConnected to {}\x1b[0m\n", addr);
} else {
println!("Connected to {}\n", addr);
}
}