harn-capability-contracts 0.10.113

Canonical typed Harness capability method contracts
Documentation
use std::env;
use std::fs;
use std::path::{Path, PathBuf};

use syn::parse::Parser as _;
use syn::{Ident, Item, Token};

fn main() {
    let manifest_dir =
        PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").expect("Cargo sets CARGO_MANIFEST_DIR"));
    let mut definitions = Vec::new();

    println!("cargo:rerun-if-changed=src");
    for source in rust_sources(&manifest_dir.join("src")) {
        println!(
            "cargo:rerun-if-changed={}",
            source
                .strip_prefix(&manifest_dir)
                .expect("capability source is under manifest directory")
                .display()
        );
        collect_definitions(&source, &mut definitions);
    }

    definitions.sort();
    definitions.dedup();

    let mut generated = String::from("&[\n");
    for definition in definitions {
        generated.push_str("    &");
        generated.push_str(&definition);
        generated.push_str("_DEF,\n");
    }
    generated.push_str("]\n");

    let out_dir = PathBuf::from(env::var_os("OUT_DIR").expect("Cargo sets OUT_DIR"));
    fs::write(out_dir.join("capability_method_defs.rs"), generated)
        .expect("write generated capability method manifest");
}

fn rust_sources(source_dir: &Path) -> Vec<PathBuf> {
    let mut sources = fs::read_dir(source_dir)
        .unwrap_or_else(|error| panic!("read {}: {error}", source_dir.display()))
        .map(|entry| entry.expect("read capability source entry").path())
        .filter(|path| path.extension().and_then(|extension| extension.to_str()) == Some("rs"))
        .filter(|path| path.file_name().and_then(|name| name.to_str()) != Some("lib.rs"))
        .collect::<Vec<_>>();
    sources.sort();
    sources
}

fn collect_definitions(path: &Path, definitions: &mut Vec<String>) {
    let source =
        fs::read_to_string(path).unwrap_or_else(|error| panic!("read {}: {error}", path.display()));
    let file = syn::parse_file(&source)
        .unwrap_or_else(|error| panic!("parse {} as Rust: {error}", path.display()));

    for item in file.items {
        let Item::Macro(item_macro) = item else {
            continue;
        };
        if !item_macro.mac.path.is_ident("capability_method") {
            continue;
        }
        let first_argument = |input: syn::parse::ParseStream<'_>| {
            let name: Ident = input.parse()?;
            input.parse::<Token![,]>()?;
            let _: proc_macro2::TokenStream = input.parse()?;
            Ok(name)
        };
        let name = first_argument
            .parse2(item_macro.mac.tokens)
            .unwrap_or_else(|error| {
                panic!(
                    "parse capability declaration in {}: {error}",
                    path.display()
                )
            });
        definitions.push(name.to_string().to_uppercase());
    }
}