Skip to main content

Grove/Binary/Build/
ServiceRegister.rs

1//! Service Register Module
2//!
3//! Handles service registration with Mountain.
4//! Provides gRPC-based service discovery and registration.
5
6use anyhow::{Context, Result};
7use serde::{Deserialize, Serialize};
8use crate::dev_log;
9
10use crate::Protocol::{ProtocolConfig, SpineConnection::SpineConnectionImpl};
11
12/// Service register for managing Grove's registration with Mountain
13pub struct ServiceRegister;
14
15/// Service registration information
16#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct ServiceRegistration {
18	/// Service name
19	pub name:String,
20	/// Service type
21	pub service_type:ServiceType,
22	/// Service version
23	pub version:String,
24	/// Service endpoint
25	pub endpoint:String,
26	/// Service capabilities
27	pub capabilities:Vec<String>,
28	/// Metadata
29	pub metadata:serde_json::Value,
30}
31
32/// Service type
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
34pub enum ServiceType {
35	/// Extension host service
36	ExtensionHost = 0,
37	/// Configuration service
38	Configuration = 1,
39	/// Logging service
40	Logging = 2,
41	/// Custom service
42	Custom = 99,
43}
44
45/// Service registration result
46#[derive(Debug, Clone, Serialize, Deserialize)]
47pub struct ServiceRegistrationResult {
48	/// Registration success
49	pub success:bool,
50	/// Service ID assigned by Mountain
51	pub service_id:Option<String>,
52	/// Error message if registration failed
53	pub error:Option<String>,
54	/// Timestamp
55	pub timestamp:u64,
56}
57
58impl ServiceRegister {
59	/// Register Grove with Mountain
60	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		// Create Spine configuration
68		let spine_config = ProtocolConfig::new().with_mountain_endpoint(service_name.to_string());
69
70		// Create Spine connection
71		let mut connection = SpineConnectionImpl::new(spine_config);
72
73		// Connect to Mountain
74		connection.Connect().await.context("Failed to connect to Mountain")?;
75
76		// Prepare registration information
77		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		// Send registration request (placeholder - in real implementation, use gRPC)
96		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	/// Unregister Grove from Mountain
112	pub async fn unregister_from_mountain(service_id:&str) -> Result<()> {
113		dev_log!("grove", "Unregistering service from Mountain: {}", service_id);
114
115		// Placeholder - in real implementation, call Mountain's unregister service
116		dev_log!("grove", "Service unregistered: {}", service_id);
117
118		Ok(())
119	}
120
121	/// Heartbeat to keep service alive
122	pub async fn send_heartbeat(service_id:&str) -> Result<()> {
123		dev_log!("grove", "Sending heartbeat for service: {}", service_id);
124
125		// Placeholder - in real implementation, send heartbeat to Mountain
126		Ok(())
127	}
128
129	/// Update service information
130	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	/// Query service information
150	pub async fn query_service(service_id:&str) -> Result<ServiceRegistration> {
151		dev_log!("grove", "Querying service information: {}", service_id);
152
153		// Placeholder - in real implementation, query Mountain for service info
154		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	/// List all registered services
165	pub async fn list_services() -> Result<Vec<ServiceRegistration>> {
166		dev_log!("grove", "Listing all registered services");
167
168		// Placeholder - in real implementation, query Mountain for all services
169		Ok(Vec::new())
170	}
171
172	/// Start heartbeat loop
173	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		// Just test that it can be created
202		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}