1use anyhow::{Context, Result};
7use serde::{Deserialize, Serialize};
8
9use crate::{
10 Protocol::{ProtocolConfig, SpineConnection::SpineConnectionImpl},
11 dev_log,
12};
13
14pub struct ServiceRegister;
16
17#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct ServiceRegistration {
20 pub name:String,
22
23 pub service_type:ServiceType,
25
26 pub version:String,
28
29 pub endpoint:String,
31
32 pub capabilities:Vec<String>,
34
35 pub metadata:serde_json::Value,
37}
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
41pub enum ServiceType {
42 ExtensionHost = 0,
44
45 Configuration = 1,
47
48 Logging = 2,
50
51 Custom = 99,
53}
54
55#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct ServiceRegistrationResult {
58 pub success:bool,
60
61 pub service_id:Option<String>,
63
64 pub error:Option<String>,
66
67 pub timestamp:u64,
69}
70
71impl ServiceRegister {
72 pub async fn register_with_mountain(
74 service_name:&str,
75
76 mountain_address:&str,
77
78 auto_reconnect:bool,
79 ) -> Result<ServiceRegistrationResult> {
80 dev_log!(
81 "grove",
82 "Registering service '{}' with Mountain at {}",
83 service_name,
84 mountain_address
85 );
86
87 let spine_config = ProtocolConfig::new().with_mountain_endpoint(service_name.to_string());
89
90 let mut connection = SpineConnectionImpl::new(spine_config);
92
93 connection.Connect().await.context("Failed to connect to Mountain")?;
95
96 let registration = ServiceRegistration {
98 name:service_name.to_string(),
99
100 service_type:ServiceType::ExtensionHost,
101
102 version:env!("CARGO_PKG_VERSION").to_string(),
103
104 endpoint:mountain_address.to_string(),
105
106 capabilities:vec![
107 "wasm-runtime".to_string(),
108 "native-rust".to_string(),
109 "cocoon-compatible".to_string(),
110 ],
111
112 metadata:serde_json::json!({
113 "host_type": "grove",
114 "features": ["wasm", "native", "ipc"]
115 }),
116 };
117
118 dev_log!("grove", "Service registration: {:?}", registration);
119
120 let result = ServiceRegistrationResult {
122 success:true,
123
124 service_id:Some(format!("grove-{}", uuid::Uuid::new_v4())),
125
126 error:None,
127
128 timestamp:std::time::SystemTime::now()
129 .duration_since(std::time::UNIX_EPOCH)
130 .map(|d| d.as_secs())
131 .unwrap_or(0),
132 };
133
134 dev_log!("grove", "Service registration result: {:?}", result);
135
136 Ok(result)
137 }
138
139 pub async fn unregister_from_mountain(service_id:&str) -> Result<()> {
141 dev_log!("grove", "Unregistering service from Mountain: {}", service_id);
142
143 dev_log!("grove", "Service unregistered: {}", service_id);
145
146 Ok(())
147 }
148
149 pub async fn send_heartbeat(service_id:&str) -> Result<()> {
151 dev_log!("grove", "Sending heartbeat for service: {}", service_id);
152
153 Ok(())
155 }
156
157 pub async fn update_registration(
159 service_id:&str,
160
161 registration:ServiceRegistration,
162 ) -> Result<ServiceRegistrationResult> {
163 dev_log!("grove", "Updating service registration: {}", service_id);
164
165 dev_log!("grove", "Updated registration: {:?}", registration);
166
167 Ok(ServiceRegistrationResult {
168 success:true,
169 service_id:Some(service_id.to_string()),
170 error:None,
171 timestamp:std::time::SystemTime::now()
172 .duration_since(std::time::UNIX_EPOCH)
173 .map(|d| d.as_secs())
174 .unwrap_or(0),
175 })
176 }
177
178 pub async fn query_service(service_id:&str) -> Result<ServiceRegistration> {
180 dev_log!("grove", "Querying service information: {}", service_id);
181
182 Ok(ServiceRegistration {
184 name:service_id.to_string(),
185 service_type:ServiceType::ExtensionHost,
186 version:"0.1.0".to_string(),
187 endpoint:"127.0.0.1:50050".to_string(),
188 capabilities:Vec::new(),
189 metadata:serde_json::Value::Null,
190 })
191 }
192
193 pub async fn list_services() -> Result<Vec<ServiceRegistration>> {
195 dev_log!("grove", "Listing all registered services");
196
197 Ok(Vec::new())
199 }
200
201 pub async fn start_heartbeat_loop(service_id:&str, interval_sec:u64) -> Result<()> {
203 dev_log!(
204 "grove",
205 "Starting heartbeat loop for service: {} (interval: {}s)",
206 service_id,
207 interval_sec
208 );
209
210 let service_id_owned = service_id.to_string();
211
212 tokio::spawn(async move {
213 loop {
214 tokio::time::sleep(tokio::time::Duration::from_secs(interval_sec)).await;
215 if let Err(e) = Self::send_heartbeat(&service_id_owned).await {
216 dev_log!("grove", "warn: heartbeat failed: {}", e);
217 }
218 }
219 });
220
221 Ok(())
222 }
223}
224
225impl Default for ServiceRegister {
226 fn default() -> Self { Self }
227}
228
229#[cfg(test)]
230mod tests {
231
232 use super::*;
233
234 #[test]
235 fn test_service_register_default() {
236 let register = ServiceRegister::default();
237
238 let _ = register;
240 }
241
242 #[test]
243 fn test_service_type() {
244 assert_eq!(ServiceType::ExtensionHost as i32, 0);
245
246 assert_eq!(ServiceType::Configuration as i32, 1);
247
248 assert_eq!(ServiceType::Logging as i32, 2);
249
250 assert_eq!(ServiceType::Custom as i32, 99);
251 }
252
253 #[tokio::test]
254 async fn test_service_registration_creation() {
255 let registration = ServiceRegistration {
256 name:"test-service".to_string(),
257
258 service_type:ServiceType::ExtensionHost,
259
260 version:"1.0.0".to_string(),
261
262 endpoint:"127.0.0.1:50050".to_string(),
263
264 capabilities:vec!["test-capability".to_string()],
265
266 metadata:serde_json::Value::Null,
267 };
268
269 assert_eq!(registration.name, "test-service");
270
271 assert_eq!(registration.service_type, ServiceType::ExtensionHost);
272 }
273}