solverforge_service/
util.rs1use crate::error::{ServiceError, ServiceResult};
2use log::debug;
3use std::env;
4use std::net::TcpListener;
5use std::path::{Path, PathBuf};
6
7pub fn find_available_port() -> ServiceResult<u16> {
8 let listener = TcpListener::bind("127.0.0.1:0")?;
9 let port = listener.local_addr()?.port();
10 Ok(port)
11}
12
13pub fn find_java(java_home: Option<&Path>) -> ServiceResult<PathBuf> {
14 if let Some(home) = java_home {
16 let java_path = home.join("bin").join("java");
17 debug!("Checking explicit java_home: {}", java_path.display());
18 if java_path.exists() {
19 return Ok(java_path);
20 }
21 return Err(ServiceError::JavaNotFound(format!(
22 "java not found in JAVA_HOME: {}",
23 home.display()
24 )));
25 }
26
27 if let Ok(output) = std::process::Command::new("which").arg("java").output() {
29 if output.status.success() {
30 let path = String::from_utf8_lossy(&output.stdout).trim().to_string();
31 debug!("Found java via PATH: {}", path);
32 if !path.is_empty() {
33 return Ok(PathBuf::from(path));
34 }
35 }
36 }
37
38 if let Ok(home) = env::var("JAVA_HOME") {
40 let java_path = PathBuf::from(&home).join("bin").join("java");
41 debug!(
42 "Checking JAVA_HOME env: {} -> {}",
43 home,
44 java_path.display()
45 );
46 if java_path.exists() {
47 return Ok(java_path);
48 }
49 }
50
51 Err(ServiceError::JavaNotFound(
52 "java not found in PATH or JAVA_HOME".to_string(),
53 ))
54}
55
56pub fn find_maven() -> ServiceResult<PathBuf> {
57 if let Ok(output) = std::process::Command::new("which").arg("mvn").output() {
58 if output.status.success() {
59 let path = String::from_utf8_lossy(&output.stdout).trim().to_string();
60 if !path.is_empty() {
61 let path_buf = PathBuf::from(&path);
66 return Ok(path_buf.canonicalize().unwrap_or(path_buf));
67 }
68 }
69 }
70
71 Err(ServiceError::MavenNotFound(
72 "mvn not found in PATH".to_string(),
73 ))
74}
75
76pub fn get_cache_dir() -> PathBuf {
77 dirs::cache_dir()
78 .unwrap_or_else(|| PathBuf::from("/tmp"))
79 .join("solverforge")
80}
81
82pub fn find_submodule_dir() -> ServiceResult<PathBuf> {
83 if let Ok(dir) = env::var("SOLVERFORGE_SERVICE_DIR") {
85 let path = PathBuf::from(&dir);
86 if path.is_dir() && path.join("pom.xml").exists() {
87 debug!("Using SOLVERFORGE_SERVICE_DIR: {}", dir);
88 return Ok(path);
89 }
90 }
91
92 let mut current = env::current_dir()?;
94 loop {
95 let candidate = current.join("solverforge-wasm-service");
96 if candidate.is_dir() && candidate.join("pom.xml").exists() {
97 return Ok(candidate);
98 }
99
100 if !current.pop() {
101 break;
102 }
103 }
104
105 if let Ok(manifest_dir) = env::var("CARGO_MANIFEST_DIR") {
107 let workspace_root = PathBuf::from(manifest_dir)
108 .parent()
109 .map(|p| p.to_path_buf())
110 .unwrap_or_default();
111 let candidate = workspace_root.join("solverforge-wasm-service");
112 if candidate.is_dir() && candidate.join("pom.xml").exists() {
113 return Ok(candidate);
114 }
115 }
116
117 Err(ServiceError::SubmoduleNotFound(
118 "solverforge-wasm-service submodule not found. Set SOLVERFORGE_SERVICE_DIR or ensure JAR is available on Maven Central.".to_string(),
119 ))
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125
126 #[test]
127 fn test_find_available_port() {
128 let port = find_available_port().unwrap();
129 assert!(port > 0);
130
131 let port2 = find_available_port().unwrap();
132 assert!(port2 > 0);
133 }
134
135 #[test]
136 fn test_get_cache_dir() {
137 let cache = get_cache_dir();
138 assert!(cache.to_string_lossy().contains("solverforge"));
139 }
140
141 #[test]
142 fn test_find_java_with_invalid_home() {
143 let result = find_java(Some(Path::new("/nonexistent/path")));
144 assert!(result.is_err());
145 }
146}