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