use std::io::Write;
use std::path::Path;
use super::manifest::{HealthProbe, PortDiscovery, ServiceDecl, ServicesManifest};
const SEARCH_SERVICE: &str = "trusty-search";
const LEGACY_SEARCH_PORT: u16 = 7878;
pub fn load_user_manifest(path: &Path, warn: &mut dyn Write) -> anyhow::Result<ServicesManifest> {
if !path.exists() {
return Ok(ServicesManifest::default_manifest());
}
let text = std::fs::read_to_string(path)?;
let mut manifest: ServicesManifest = serde_yaml::from_str(&text)
.map_err(|e| anyhow::anyhow!("failed to parse services.yaml: {e}"))?;
if map_legacy_search_entry(&mut manifest) {
let _ = writeln!(
warn,
"WARN: {}: trusty-search probes port {LEGACY_SEARCH_PORT} over HTTP, which it no \
longer binds; probing its socket instead (health_probe: uds_search). The file is \
unchanged; `tm services init --force` rewrites it.",
path.display()
);
}
manifest
.validate()
.map_err(|e| anyhow::anyhow!("services.yaml validation failed: {e}"))?;
manifest.expand_paths()?;
Ok(manifest)
}
fn is_legacy_search_decl(decl: &ServiceDecl) -> bool {
decl.health_probe == HealthProbe::Http
&& decl.port_discovery == PortDiscovery::Static
&& decl.default_port == Some(LEGACY_SEARCH_PORT)
&& decl
.health_url
.as_deref()
.is_some_and(|u| u.starts_with("http://") || u.starts_with("https://"))
}
fn map_legacy_search_entry(manifest: &mut ServicesManifest) -> bool {
match manifest.services.get_mut(SEARCH_SERVICE) {
Some(decl) if is_legacy_search_decl(decl) => {
decl.health_probe = HealthProbe::UdsSearch;
decl.default_port = None;
decl.health_url = None;
true
}
_ => false,
}
}