paraglide-launch 0.1.2

Analyze a project and detect deployable services, languages, frameworks, commands, and env vars
Documentation
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>,
}

/// Read a config file, detect format by extension, and deserialize.
/// Returns `None` on any error (read, utf8, parse), logging to stderr.
/// Supports `.toml`, `.json`, `.yaml`, and `.yml`.
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>;
}