Skip to main content

Mountain/Vine/Server/
Initialize.rs

1//! # Initialize (Vine Server)
2//!
3//! Contains the logic to initialize and start the Mountain gRPC server.
4//!
5//! This module provides the entry point for starting Vine's gRPC servers:
6//! - **MountainServiceServer**: Listens for connections from Cocoon sidecar
7//! - **CocoonServiceServer**: Listens for connections from Mountain
8//!   (bidirectional)
9//!
10//! ## Initialization Process
11//!
12//! 1. Validates socket addresses
13//! 2. Retrieves ApplicationRunTime from Tauri state
14//! 3. Creates service implementations with runtime dependencies
15//! 4. Spawns server tasks as background tokio tasks
16//! 5. Servers begin listening on specified ports
17//!
18//! ## Server Configuration
19//!
20//! - **Mountaln Service**: Typically on port 50051 (configurable)
21//! - **Cocoon Service**: Typically on port 50052 (configurable)
22//! - Both servers support compression and message size limits
23//!
24//! ## Error Handling
25//!
26//! Initialization failures are logged and returned to the caller.
27//! Once started, servers run independently and log their own errors.
28//!
29//! ## Lifecycle
30//!
31//! Servers run as detached tokio tasks. They will:
32//! - Start immediately when spawned
33//! - Continue until process termination or tokio runtime shutdown
34//! - Log errors to the logging system
35//! - Not automatically restart on failure (caller should implement retry logic
36//!   if needed)
37
38use std::{net::SocketAddr, sync::Arc};
39
40use tauri::{AppHandle, Manager};
41use tonic::transport::Server;
42use ::Vine::{
43	Error::VineError,
44	Generated::{cocoon_service_server::CocoonServiceServer, mountain_service_server::MountainServiceServer},
45};
46
47use super::MountainVinegRPCService::MountainVinegRPCService;
48use crate::{RPC::CocoonService::CocoonServiceImpl, RunTime::ApplicationRunTime::ApplicationRunTime, dev_log};
49
50/// Server configuration constants
51mod ServerConfig {
52
53	use std::time::Duration;
54
55	/// Default port for MountainService server
56	pub const DEFAULT_MOUNTAIN_PORT:u16 = 50051;
57
58	/// Default port for CocoonService server
59	pub const DEFAULT_COCOON_PORT:u16 = 50052;
60
61	/// Maximum concurrent connections per server
62	pub const MAX_CONNECTIONS:usize = 100;
63
64	/// Connection timeout duration
65	pub const CONNECTION_TIMEOUT:Duration = Duration::from_secs(30);
66
67	/// Default message size limit (4MB)
68	pub const MAX_MESSAGE_SIZE:usize = 4 * 1024 * 1024;
69}
70
71/// Validates a socket address string before parsing.
72///
73/// # Parameters
74/// - `AddressString`: The address string to validate
75/// - `ServerName`: Name of the server for error messages
76///
77/// # Returns
78/// - `Ok(SocketAddr)`: Validated and parsed socket address
79/// - `Err(VineError)`: Invalid address format
80fn ValidateSocketAddress(AddressString:&str, ServerName:&str) -> Result<SocketAddr, VineError> {
81	if AddressString.is_empty() {
82		return Err(VineError::InvalidMessageFormat(format!(
83			"{} address cannot be empty",
84			ServerName
85		)));
86	}
87
88	if AddressString.len() > 256 {
89		return Err(VineError::InvalidMessageFormat(format!(
90			"{} address exceeds maximum length (256 characters)",
91			ServerName
92		)));
93	}
94
95	match AddressString.parse::<SocketAddr>() {
96		Ok(addr) => {
97			// Validate port is within valid range
98			if addr.port() < 1024 {
99				dev_log!(
100					"grpc",
101					"warn: [VineServer] {} using privileged port {}, this may require elevated privileges",
102					ServerName,
103					addr.port()
104				);
105			}
106
107			Ok(addr)
108		},
109
110		Err(e) => Err(VineError::AddressParseError(e)),
111	}
112}
113
114/// Initializes and starts the gRPC servers on background tasks.
115///
116/// This function retrieves the core `ApplicationRunTime` from Tauri's managed
117/// state, instantiates the gRPC service implementations
118/// (`MountainVinegRPCService` and `CocoonServiceServer`), and uses `tonic` to
119/// serve them at the specified addresses.
120///
121/// # Parameters
122/// - `ApplicationHandle`: The Tauri application handle
123/// - `MountainAddressString`: The address and port to bind the Mountain server
124///   to (e.g., `"[::1]:50051"`)
125/// - `CocoonAddressString`: The address and port to bind the Cocoon server to
126///   (e.g., `"[::1]:50052"`)
127///
128/// # Returns
129/// - `Ok(())`: Successfully initialized and started both servers
130/// - `Err(VineError)`: Initialization failed (invalid address, missing runtime,
131///   etc.)
132///
133/// # Errors
134///
135/// This function will return an error if:
136/// - Either socket address string is invalid or unparseable
137/// - ApplicationRunTime is not available in Tauri state
138/// - Server task spawning fails (rare)
139///
140/// # Example
141///
142/// ```rust,no_run
143/// # use Vine::Server::Initialize::Initialize;
144/// # use tauri::AppHandle;
145/// # async fn example(handle: AppHandle) -> Result<(), Box<dyn std::error::Error>> {
146/// Initialize(handle, "[::1]:50051".to_string(), "[::1]:50052".to_string())?;
147/// # Ok(())
148/// # }
149/// ```
150///
151/// # Notes
152///
153/// - Servers run as detached tokio tasks
154/// - Initialization is async-safe but function is synchronous
155/// - Servers log errors independently after startup
156/// - Use `Default` addresses for development (localhost with default ports)
157pub fn Initialize(
158	ApplicationHandle:AppHandle,
159
160	MountainAddressString:String,
161
162	CocoonAddressString:String,
163) -> Result<(), VineError> {
164	dev_log!("grpc", "[VineServer] Initializing Vine gRPC servers...");
165
166	crate::dev_log!("grpc", "initializing Vine gRPC servers");
167
168	// Validate and parse socket addresses
169	let MountainAddress = ValidateSocketAddress(&MountainAddressString, "MountainService")?;
170
171	let CocoonAddress = ValidateSocketAddress(&CocoonAddressString, "CocoonService")?;
172
173	dev_log!("grpc", "[VineServer] MountainService will bind to: {}", MountainAddress);
174
175	dev_log!(
176		"grpc",
177		"[VineServer] Cocoon expected on: {} (started by Cocoon process)",
178		CocoonAddress
179	);
180
181	crate::dev_log!("grpc", "Mountain={} Cocoon(remote)={}", MountainAddress, CocoonAddress);
182
183	// Retrieve ApplicationRunTime from Tauri managed state
184	let RunTime = ApplicationHandle
185		.try_state::<Arc<ApplicationRunTime>>()
186		.ok_or_else(|| {
187			let msg = "[VineServer] CRITICAL: ApplicationRunTime not found in Tauri state. Server cannot start.";
188
189			dev_log!("grpc", "error: {}", msg);
190
191			VineError::InternalLockError(msg.to_string())
192		})?
193		.inner()
194		.clone();
195
196	dev_log!("grpc", "[VineServer] ApplicationRunTime retrieved successfully");
197
198	// Create MountainService implementation (handles calls from Cocoon to Mountain)
199	let MountainService = MountainVinegRPCService::Create(ApplicationHandle.clone(), RunTime.clone());
200
201	// Create CocoonService implementation (handles calls from Mountain to Cocoon)
202	let cocoon_service_impl = CocoonServiceImpl::new(RunTime.Environment.clone());
203
204	dev_log!("grpc", "[VineServer] Service implementations created");
205
206	// Spawn Mountain server to run in the background
207	let MountainServerName = MountainAddress.to_string();
208
209	tokio::spawn(async move {
210		dev_log!(
211			"grpc",
212			"[VineServer] Starting MountainService gRPC server on {}",
213			MountainServerName
214		);
215
216		let ServerResult = Server::builder()
217			.add_service(
218				MountainServiceServer::new(MountainService)
219					.max_decoding_message_size(ServerConfig::MAX_MESSAGE_SIZE)
220					.max_encoding_message_size(ServerConfig::MAX_MESSAGE_SIZE),
221			)
222			.serve(MountainAddress)
223			.await;
224
225		match ServerResult {
226			Ok(_) => {
227				dev_log!("grpc", "[VineServer] MountainService server shut down gracefully");
228			},
229			Err(e) => {
230				dev_log!("grpc", "error: [VineServer] MountainService gRPC server error: {}", e);
231			},
232		}
233	});
234
235	// NOTE: CocoonService gRPC server is NOT started by Mountain.
236	// Port 50052 is reserved for Cocoon's own gRPC server (started by
237	// Cocoon's Effect-TS bootstrap, Stage 5). Mountain connects to Cocoon
238	// as a CLIENT on 50052 via Vine::Client::ConnectToSideCar.
239	// Starting CocoonServiceServer here would cause EADDRINUSE when Cocoon
240	// tries to bind the same port.
241	let _ = cocoon_service_impl; // suppress unused variable warning
242
243	dev_log!(
244		"grpc",
245		"[VineServer] MountainService gRPC server initialized on {}",
246		MountainAddress
247	);
248
249	Ok(())
250}