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 pub service_type:ServiceType,
24 pub version:String,
26 pub endpoint:String,
28 pub capabilities:Vec<String>,
30 pub metadata:serde_json::Value,
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
36pub enum ServiceType {
37 ExtensionHost = 0,
39 Configuration = 1,
41 Logging = 2,
43 Custom = 99,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct ServiceRegistrationResult {
50 pub success:bool,
52 pub service_id:Option<String>,
54 pub error:Option<String>,
56 pub timestamp:u64,
58}
59
60impl ServiceRegister {
61 pub async fn register_with_mountain(
63 service_name:&str,
64 mountain_address:&str,
65 auto_reconnect:bool,
66 ) -> Result<ServiceRegistrationResult> {
67 dev_log!(
68 "grove",
69 "Registering service '{}' with Mountain at {}",
70 service_name,
71 mountain_address
72 );
73
74 let spine_config = ProtocolConfig::new().with_mountain_endpoint(service_name.to_string());
76
77 let mut connection = SpineConnectionImpl::new(spine_config);
79
80 connection.Connect().await.context("Failed to connect to Mountain")?;
82
83 let registration = ServiceRegistration {
85 name:service_name.to_string(),
86 service_type:ServiceType::ExtensionHost,
87 version:env!("CARGO_PKG_VERSION").to_string(),
88 endpoint:mountain_address.to_string(),
89 capabilities:vec![
90 "wasm-runtime".to_string(),
91 "native-rust".to_string(),
92 "cocoon-compatible".to_string(),
93 ],
94 metadata:serde_json::json!({
95 "host_type": "grove",
96 "features": ["wasm", "native", "ipc"]
97 }),
98 };
99
100 dev_log!("grove", "Service registration: {:?}", registration);
101
102 let result = ServiceRegistrationResult {
104 success:true,
105 service_id:Some(format!("grove-{}", uuid::Uuid::new_v4())),
106 error:None,
107 timestamp:std::time::SystemTime::now()
108 .duration_since(std::time::UNIX_EPOCH)
109 .map(|d| d.as_secs())
110 .unwrap_or(0),
111 };
112
113 dev_log!("grove", "Service registration result: {:?}", result);
114
115 Ok(result)
116 }
117
118 pub async fn unregister_from_mountain(service_id:&str) -> Result<()> {
120 dev_log!("grove", "Unregistering service from Mountain: {}", service_id);
121
122 dev_log!("grove", "Service unregistered: {}", service_id);
124
125 Ok(())
126 }
127
128 pub async fn send_heartbeat(service_id:&str) -> Result<()> {
130 dev_log!("grove", "Sending heartbeat for service: {}", service_id);
131
132 Ok(())
134 }
135
136 pub async fn update_registration(
138 service_id:&str,
139 registration:ServiceRegistration,
140 ) -> Result<ServiceRegistrationResult> {
141 dev_log!("grove", "Updating service registration: {}", service_id);
142
143 dev_log!("grove", "Updated registration: {:?}", registration);
144
145 Ok(ServiceRegistrationResult {
146 success:true,
147 service_id:Some(service_id.to_string()),
148 error:None,
149 timestamp:std::time::SystemTime::now()
150 .duration_since(std::time::UNIX_EPOCH)
151 .map(|d| d.as_secs())
152 .unwrap_or(0),
153 })
154 }
155
156 pub async fn query_service(service_id:&str) -> Result<ServiceRegistration> {
158 dev_log!("grove", "Querying service information: {}", service_id);
159
160 Ok(ServiceRegistration {
162 name:service_id.to_string(),
163 service_type:ServiceType::ExtensionHost,
164 version:"0.1.0".to_string(),
165 endpoint:"127.0.0.1:50050".to_string(),
166 capabilities:Vec::new(),
167 metadata:serde_json::Value::Null,
168 })
169 }
170
171 pub async fn list_services() -> Result<Vec<ServiceRegistration>> {
173 dev_log!("grove", "Listing all registered services");
174
175 Ok(Vec::new())
177 }
178
179 pub async fn start_heartbeat_loop(service_id:&str, interval_sec:u64) -> Result<()> {
181 dev_log!(
182 "grove",
183 "Starting heartbeat loop for service: {} (interval: {}s)",
184 service_id,
185 interval_sec
186 );
187
188 let service_id_owned = service_id.to_string();
189 tokio::spawn(async move {
190 loop {
191 tokio::time::sleep(tokio::time::Duration::from_secs(interval_sec)).await;
192 if let Err(e) = Self::send_heartbeat(&service_id_owned).await {
193 dev_log!("grove", "warn: heartbeat failed: {}", e);
194 }
195 }
196 });
197
198 Ok(())
199 }
200}
201
202impl Default for ServiceRegister {
203 fn default() -> Self { Self }
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209
210 #[test]
211 fn test_service_register_default() {
212 let register = ServiceRegister::default();
213 let _ = register;
215 }
216
217 #[test]
218 fn test_service_type() {
219 assert_eq!(ServiceType::ExtensionHost as i32, 0);
220 assert_eq!(ServiceType::Configuration as i32, 1);
221 assert_eq!(ServiceType::Logging as i32, 2);
222 assert_eq!(ServiceType::Custom as i32, 99);
223 }
224
225 #[tokio::test]
226 async fn test_service_registration_creation() {
227 let registration = ServiceRegistration {
228 name:"test-service".to_string(),
229 service_type:ServiceType::ExtensionHost,
230 version:"1.0.0".to_string(),
231 endpoint:"127.0.0.1:50050".to_string(),
232 capabilities:vec!["test-capability".to_string()],
233 metadata:serde_json::Value::Null,
234 };
235
236 assert_eq!(registration.name, "test-service");
237 assert_eq!(registration.service_type, ServiceType::ExtensionHost);
238 }
239}