fastboop-core 0.0.1-rc.21

Core profile matching and boot orchestration primitives for fastboop.
Documentation
use std::collections::HashSet;
use std::env;
use std::fs;
use std::path::{Path, PathBuf};

use fastboop_schema::DeviceProfile;
use fastboop_schema::bin::DeviceProfileBin;

fn main() {
    let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
    let devpro_dir = find_devpro_dir(&manifest_dir).unwrap_or_else(|| {
        manifest_dir
            .parent()
            .unwrap_or(manifest_dir.as_path())
            .join("devprofiles.d")
    });

    println!("cargo:rerun-if-changed={}", devpro_dir.display());

    let mut profiles = Vec::new();
    let mut seen = HashSet::new();

    if devpro_dir.is_dir() {
        let mut paths: Vec<PathBuf> = fs::read_dir(&devpro_dir)
            .unwrap()
            .filter_map(|entry| entry.ok().map(|entry| entry.path()))
            .filter(|path| path.is_file())
            .collect();
        paths.sort();

        for path in paths {
            if !is_devpro_path(&path) {
                continue;
            }
            println!("cargo:rerun-if-changed={}", path.display());
            let text = fs::read_to_string(&path)
                .unwrap_or_else(|err| panic!("reading {}: {err}", path.display()));
            let profile: DeviceProfile = serde_yaml::from_str(&text)
                .unwrap_or_else(|err| panic!("parsing {}: {err}", path.display()));
            if !seen.insert(profile.id.clone()) {
                panic!(
                    "duplicate device profile id '{}' in {}",
                    profile.id,
                    path.display()
                );
            }
            profiles.push(profile);
        }
    }

    let bin_profiles: Vec<DeviceProfileBin> =
        profiles.into_iter().map(DeviceProfileBin::from).collect();
    let bytes = postcard::to_allocvec(&bin_profiles).expect("serialize builtin devpros");
    let _: Vec<DeviceProfileBin> = postcard::from_bytes(&bytes).expect("roundtrip builtin devpros");
    let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
    let out_path = out_dir.join("builtin_devpros.bin");
    fs::write(&out_path, bytes).expect("write builtin devpros");
}

fn find_devpro_dir(start: &Path) -> Option<PathBuf> {
    let mut dir = start;
    loop {
        let candidate = dir.join("devprofiles.d");
        if candidate.is_dir() {
            return Some(candidate);
        }
        dir = dir.parent()?;
    }
}

fn is_devpro_path(path: &Path) -> bool {
    matches!(
        path.extension().and_then(|ext| ext.to_str()),
        Some("yml") | Some("yaml") | Some("json")
    )
}