use std::path::Path;
use serde::de::DeserializeOwned;
use crate::error::LaunchError;
use crate::fs::{DirEntry, FileSystem};
use crate::types::{DirContext, Monorepo, Service};
#[derive(Default)]
pub struct SignalOutput {
pub services: Vec<Service>,
pub context: Vec<DirContext>,
pub monorepo: Option<Monorepo>,
}
pub fn read_config<T: DeserializeOwned>(
fs: &dyn FileSystem,
path: &Path,
signal_name: &str,
) -> Option<T> {
let bytes = match fs.read_file(path) {
Ok(b) => b,
Err(e) => {
eprintln!("{signal_name}: failed to read {}: {e}", path.display());
return None;
}
};
let text = match std::str::from_utf8(&bytes) {
Ok(s) => s,
Err(e) => {
eprintln!("{signal_name}: invalid utf8 in {}: {e}", path.display());
return None;
}
};
let ext = path.extension().and_then(|e| e.to_str()).unwrap_or("");
let result: Result<T, String> = match ext {
"json" => serde_json::from_str(text).map_err(|e| e.to_string()),
"yaml" | "yml" => serde_yml::from_str(text).map_err(|e| e.to_string()),
_ => toml::from_str(text).map_err(|e| e.to_string()),
};
match result {
Ok(config) => Some(config),
Err(e) => {
eprintln!("{signal_name}: failed to parse {}: {e}", path.display());
None
}
}
}
pub trait Signal: Send {
fn name(&self) -> &'static str;
fn observe(&mut self, dir: &Path, entry: &DirEntry);
fn generate(&mut self, fs: &dyn FileSystem) -> Result<SignalOutput, LaunchError>;
}