Skip to main content

synapse_codegen/
build_helper.rs

1//! Build helper for generating Synapse service code
2
3use crate::{generate_service_code, parser::parse_proto_file_with_includes};
4use anyhow::Result;
5use std::path::{Path, PathBuf};
6
7/// Configuration for code generation
8pub struct CodegenConfig {
9    /// Proto file to process
10    pub proto_file: PathBuf,
11    /// Output directory for generated code
12    pub out_dir: PathBuf,
13    /// Module name for generated code
14    pub module_name: String,
15    /// Include paths for proto imports
16    pub include_paths: Vec<PathBuf>,
17}
18
19impl CodegenConfig {
20    /// Create config from proto file path
21    pub fn from_proto(proto_file: impl AsRef<Path>) -> Self {
22        let proto_file = proto_file.as_ref().to_path_buf();
23        let module_name = proto_file
24            .file_stem()
25            .unwrap()
26            .to_string_lossy()
27            .replace('-', "_");
28
29        Self {
30            proto_file,
31            out_dir: PathBuf::from(std::env::var("OUT_DIR").unwrap()),
32            module_name,
33            include_paths: Vec::new(),
34        }
35    }
36
37    /// Set output directory
38    pub fn out_dir(mut self, out_dir: impl AsRef<Path>) -> Self {
39        self.out_dir = out_dir.as_ref().to_path_buf();
40        self
41    }
42
43    /// Set module name
44    pub fn module_name(mut self, name: impl Into<String>) -> Self {
45        self.module_name = name.into();
46        self
47    }
48
49    /// Set include paths for proto imports
50    pub fn include_paths(mut self, paths: &[&str]) -> Self {
51        self.include_paths = paths.iter().map(PathBuf::from).collect();
52        self
53    }
54
55    /// Generate code
56    pub fn generate(self) -> Result<()> {
57        // Parse proto file
58        let services = parse_proto_file_with_includes(&self.proto_file, &self.include_paths)?;
59
60        if services.is_empty() {
61            println!(
62                "cargo:warning=No services found in {}",
63                self.proto_file.display()
64            );
65            return Ok(());
66        }
67
68        // Generate code for all services into one file
69        let mut all_code = String::new();
70        for service in &services {
71            let code = generate_service_code(service)?;
72            all_code.push_str(&code);
73            all_code.push_str("\n\n");
74
75            println!(
76                "cargo:warning=Generated Synapse code for service {}",
77                service.service_name,
78            );
79        }
80
81        // Write combined code to output file
82        let output_file = self
83            .out_dir
84            .join(format!("{}_synapse.rs", self.module_name));
85        std::fs::write(&output_file, all_code)?;
86
87        println!(
88            "cargo:warning=Wrote {} services to {}",
89            services.len(),
90            output_file.display()
91        );
92
93        // Emit rerun directives
94        println!("cargo:rerun-if-changed={}", self.proto_file.display());
95
96        Ok(())
97    }
98}
99
100/// Helper function for use in build.rs
101///
102/// # Example
103/// ```ignore
104/// // In build.rs:
105/// synapse_codegen::generate_synapse_code("proto/user_service.proto").unwrap();
106/// ```
107pub fn generate_synapse_code(proto_file: impl AsRef<Path>) -> Result<()> {
108    CodegenConfig::from_proto(proto_file).generate()
109}