gglib_core/services/
server_service.rs1use crate::ports::{CoreError, ProcessHandle, ProcessRunner, ServerConfig, ServerHealth};
4use std::sync::Arc;
5
6pub struct ServerService {
8 runner: Arc<dyn ProcessRunner>,
9}
10
11impl ServerService {
12 pub fn new(runner: Arc<dyn ProcessRunner>) -> Self {
14 Self { runner }
15 }
16
17 pub async fn start(&self, config: ServerConfig) -> Result<ProcessHandle, CoreError> {
19 self.runner.start(config).await.map_err(CoreError::from)
20 }
21
22 pub async fn stop(&self, handle: &ProcessHandle) -> Result<(), CoreError> {
24 self.runner.stop(handle).await.map_err(CoreError::from)
25 }
26
27 pub async fn is_running(&self, handle: &ProcessHandle) -> bool {
29 self.runner.is_running(handle).await
30 }
31
32 pub async fn health(&self, handle: &ProcessHandle) -> Result<ServerHealth, CoreError> {
34 self.runner.health(handle).await.map_err(CoreError::from)
35 }
36
37 pub async fn list_running(&self) -> Result<Vec<ProcessHandle>, CoreError> {
39 self.runner.list_running().await.map_err(CoreError::from)
40 }
41}
42
43#[cfg(test)]
44mod tests {
45 use super::*;
46 use crate::ports::{ProcessError, ProcessRunner};
47 use async_trait::async_trait;
48 use std::path::PathBuf;
49 use std::sync::Mutex;
50
51 struct MockRunner {
52 handles: Mutex<Vec<ProcessHandle>>,
53 next_port: Mutex<u16>,
54 }
55
56 impl MockRunner {
57 fn new(base_port: u16) -> Self {
58 Self {
59 handles: Mutex::new(vec![]),
60 next_port: Mutex::new(base_port),
61 }
62 }
63 }
64
65 #[async_trait]
66 impl ProcessRunner for MockRunner {
67 async fn start(&self, config: ServerConfig) -> Result<ProcessHandle, ProcessError> {
68 let port = {
69 let mut next = self.next_port.lock().unwrap();
70 let port = *next;
71 *next += 1;
72 port
73 };
74 let handle = ProcessHandle::new(
75 config.model_id,
76 config.model_name,
77 Some(12345),
78 port,
79 0, );
81 self.handles.lock().unwrap().push(handle.clone());
82 Ok(handle)
83 }
84
85 async fn stop(&self, handle: &ProcessHandle) -> Result<(), ProcessError> {
86 self.handles
87 .lock()
88 .unwrap()
89 .retain(|h| h.port != handle.port);
90 Ok(())
91 }
92
93 async fn is_running(&self, handle: &ProcessHandle) -> bool {
94 self.handles
95 .lock()
96 .unwrap()
97 .iter()
98 .any(|h| h.port == handle.port)
99 }
100
101 async fn health(&self, handle: &ProcessHandle) -> Result<ServerHealth, ProcessError> {
102 let handles = self.handles.lock().unwrap();
103 if handles.iter().any(|h| h.port == handle.port) {
104 Ok(ServerHealth::healthy())
105 } else {
106 Err(ProcessError::NotRunning(format!("port={}", handle.port)))
107 }
108 }
109
110 async fn list_running(&self) -> Result<Vec<ProcessHandle>, ProcessError> {
111 Ok(self.handles.lock().unwrap().clone())
112 }
113 }
114
115 #[tokio::test]
116 async fn test_start_and_stop() {
117 let runner = Arc::new(MockRunner::new(9000));
118 let service = ServerService::new(runner);
119
120 let config = ServerConfig::new(
121 1,
122 "test-model".to_string(),
123 PathBuf::from("/path/to/model.gguf"),
124 9000,
125 );
126
127 let handle = service.start(config).await.unwrap();
128 assert_eq!(handle.port, 9000);
129
130 let running = service.list_running().await.unwrap();
131 assert_eq!(running.len(), 1);
132
133 service.stop(&handle).await.unwrap();
134 let running = service.list_running().await.unwrap();
135 assert!(running.is_empty());
136 }
137}