synapse-codegen 0.0.2

Code generation from protobuf service definitions for Synapse
Documentation
//! Build helper for generating Synapse service code

use crate::{generate_service_code, parser::parse_proto_file_with_includes};
use anyhow::Result;
use std::path::{Path, PathBuf};

/// Configuration for code generation
pub struct CodegenConfig {
    /// Proto file to process
    pub proto_file: PathBuf,
    /// Output directory for generated code
    pub out_dir: PathBuf,
    /// Module name for generated code
    pub module_name: String,
    /// Include paths for proto imports
    pub include_paths: Vec<PathBuf>,
}

impl CodegenConfig {
    /// Create config from proto file path
    pub fn from_proto(proto_file: impl AsRef<Path>) -> Self {
        let proto_file = proto_file.as_ref().to_path_buf();
        let module_name = proto_file
            .file_stem()
            .unwrap()
            .to_string_lossy()
            .replace('-', "_");

        Self {
            proto_file,
            out_dir: PathBuf::from(std::env::var("OUT_DIR").unwrap()),
            module_name,
            include_paths: Vec::new(),
        }
    }

    /// Set output directory
    pub fn out_dir(mut self, out_dir: impl AsRef<Path>) -> Self {
        self.out_dir = out_dir.as_ref().to_path_buf();
        self
    }

    /// Set module name
    pub fn module_name(mut self, name: impl Into<String>) -> Self {
        self.module_name = name.into();
        self
    }

    /// Set include paths for proto imports
    pub fn include_paths(mut self, paths: &[&str]) -> Self {
        self.include_paths = paths.iter().map(PathBuf::from).collect();
        self
    }

    /// Generate code
    pub fn generate(self) -> Result<()> {
        // Parse proto file
        let services = parse_proto_file_with_includes(&self.proto_file, &self.include_paths)?;

        if services.is_empty() {
            println!(
                "cargo:warning=No services found in {}",
                self.proto_file.display()
            );
            return Ok(());
        }

        // Generate code for all services into one file
        let mut all_code = String::new();
        for service in &services {
            let code = generate_service_code(service)?;
            all_code.push_str(&code);
            all_code.push_str("\n\n");

            println!(
                "cargo:warning=Generated Synapse code for service {}",
                service.service_name,
            );
        }

        // Write combined code to output file
        let output_file = self
            .out_dir
            .join(format!("{}_synapse.rs", self.module_name));
        std::fs::write(&output_file, all_code)?;

        println!(
            "cargo:warning=Wrote {} services to {}",
            services.len(),
            output_file.display()
        );

        // Emit rerun directives
        println!("cargo:rerun-if-changed={}", self.proto_file.display());

        Ok(())
    }
}

/// Helper function for use in build.rs
///
/// # Example
/// ```ignore
/// // In build.rs:
/// synapse_codegen::generate_synapse_code("proto/user_service.proto").unwrap();
/// ```
pub fn generate_synapse_code(proto_file: impl AsRef<Path>) -> Result<()> {
    CodegenConfig::from_proto(proto_file).generate()
}