trapeze-macros 0.7.6

Macros for trapeze
Documentation
use std::fs::read_to_string;
use std::path::PathBuf;

use anyhow::{Context, Result};
use syn::visit_mut::{visit_item_mut, VisitMut};
use syn::{parse_str, File, Item, LitStr};

struct InlineIncludesVisitor {
    out_dir: PathBuf,
    error: Option<anyhow::Error>,
}

impl InlineIncludesVisitor {
    fn resolve_item(&mut self, item: &mut Item) -> Result<()> {
        let Item::Macro(mac) = item else {
            return Ok(());
        };

        let mac = &mac.mac;
        if !mac.path.is_ident("include") {
            return Ok(());
        }
        let path: LitStr =
            syn::parse2(mac.tokens.clone()).context("Expected single literal string")?;

        let buffer = read_to_string(self.out_dir.join(path.value()))?;
        let content: proc_macro2::TokenStream = parse_str(&buffer)?;

        *item = Item::Verbatim(content);

        Ok(())
    }
}

impl VisitMut for InlineIncludesVisitor {
    fn visit_item_mut(&mut self, item: &mut Item) {
        if self.error.is_some() {
            return;
        }

        if let Err(err) = self.resolve_item(item) {
            self.error = Some(err);
        }

        visit_item_mut(self, item);
    }
}

pub fn inline_includes(file: PathBuf) -> Result<File> {
    let out_dir = file.parent().context("File has no parent")?.to_owned();
    let mut resolver = InlineIncludesVisitor {
        out_dir,
        error: None,
    };
    let file = read_to_string(file)?;
    let mut file: File = parse_str(&file)?;

    resolver.visit_file_mut(&mut file);

    if let Some(err) = resolver.error {
        return Err(err);
    }

    Ok(file)
}