use std::fs::File;
use std::io::Read;
use std::net::{SocketAddr, TcpListener};
use std::path::{Path, PathBuf};
use serde_json::Value;
use crate::config::{EchoConfig, ServiceConfig};
pub fn scan_echo_files(root_dir: &Path) -> Vec<PathBuf> {
let mut echo_files = Vec::new();
if let Ok(entries) = std::fs::read_dir(root_dir) {
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
echo_files.extend(scan_echo_files(&path));
} else if let Some(filename) = path.file_name() {
let name = filename.to_string_lossy();
if name == ".echo" {
echo_files.push(path);
}
}
}
}
echo_files
}
pub fn is_port_occupied(port: u16) -> bool {
let addr: SocketAddr = format!("0.0.0.0:{}", port).parse().unwrap();
if TcpListener::bind(addr).is_ok() {
return true;
}
true
}
pub fn load_manifest(echo_path: &Path) -> Option<Value> {
let manifest_path = echo_path.parent()?.join("manifest.json");
if !manifest_path.exists() {
return None;
}
let mut file = File::open(&manifest_path).ok()?;
let mut content = String::new();
if file.read_to_string(&mut content).is_err() {
return None;
}
serde_json::from_str(&content).ok()
}
pub fn discover_services(root_dir: &Path) -> Vec<(PathBuf, ServiceConfig)> {
let mut services = Vec::new();
let echo_files = scan_echo_files(root_dir);
for echo_path in echo_files {
match EchoConfig::from_file(&echo_path) {
Ok(echo_config) => {
if !echo_config.enable {
continue;
}
if !is_port_occupied(echo_config.port) {
continue;
}
let manifest = load_manifest(&echo_path);
let service_config =
ServiceConfig::from_echo(&echo_path, manifest, echo_config.port);
services.push((echo_path, service_config));
}
Err(_) => {
continue;
}
}
}
services
}
pub fn get_local_ip() -> String {
if let Ok(socket) = std::net::UdpSocket::bind("0.0.0.0:0") {
if socket.connect("8.8.8.8:80").is_ok() {
if let Ok(addr) = socket.local_addr() {
return addr.ip().to_string();
}
}
}
"127.0.0.1".to_string()
}