use std::{
io::Write,
path::{Path, PathBuf},
};
use crate::{EvaluatorOptions, api::Evaluator};
use super::{CODEGEN_HEADER, CodegenOptions};
pub fn configure() -> Builder {
Builder::default()
}
impl Builder {
pub fn codegen(self, modules: &[impl AsRef<Path>]) -> Result<(), Box<dyn std::error::Error>> {
let output_path = self.output.unwrap_or(
PathBuf::from(std::env::var("OUT_DIR").expect(
"OUT_DIR not set, expected to be run in a build script or have an output path set",
))
.join("mod.rs"),
);
if let Some(parent) = output_path.parent() {
if !parent.exists() {
std::fs::create_dir_all(parent).unwrap();
}
};
let mut evaluator = Evaluator::new_from_options(self.evaluator_options)?;
let mut output_file = std::fs::File::create(output_path)?;
output_file.write_all(CODEGEN_HEADER.as_bytes())?;
for module in modules {
let module_path = module.as_ref();
if self.rerun_if_changed {
println!("cargo:rerun-if-changed={}", module_path.display());
}
let pkl_mod = evaluator.evaluate_module(module_path)?;
let code = pkl_mod.codegen_with_options(&self.codegen_options)?;
output_file.write_all(&code.as_bytes()[CODEGEN_HEADER.len()..])?;
}
Ok(())
}
}
pub struct Builder {
codegen_options: CodegenOptions,
evaluator_options: EvaluatorOptions,
output: Option<PathBuf>,
rerun_if_changed: bool,
}
impl Default for Builder {
fn default() -> Self {
Self {
codegen_options: CodegenOptions::default(),
evaluator_options: EvaluatorOptions::default(),
output: None,
rerun_if_changed: true,
}
}
}
impl Builder {
pub fn type_attribute(mut self, ident: &str, value: &str) -> Self {
self.codegen_options = self.codegen_options.type_attribute(ident, value);
self
}
pub fn field_attribute(mut self, ident: &str, value: &str) -> Self {
self.codegen_options = self.codegen_options.field_attribute(ident, value);
self
}
pub fn as_enum(mut self, ident: &str, variants: &[&str]) -> Self {
self.codegen_options = self.codegen_options.as_enum(ident, variants);
self
}
pub fn opaque(mut self, ident: &str) -> Self {
self.codegen_options = self.codegen_options.opaque(ident);
self
}
pub fn evaluator_options(mut self, options: EvaluatorOptions) -> Self {
self.evaluator_options = options;
self
}
pub fn output(mut self, output: impl AsRef<Path>) -> Self {
self.output = Some(output.as_ref().to_path_buf());
self
}
pub fn rerun_if_changed(mut self, rerun_if_changed: bool) -> Self {
self.rerun_if_changed = rerun_if_changed;
self
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::utils::tests::pkl_tests_file;
#[test]
#[should_panic]
fn test_codegen() {
let path = pkl_tests_file("example.pkl");
let output_path = pkl_tests_file("example.rs");
configure()
.type_attribute("Example", "#[derive(Default)]")
.field_attribute("Example.ip", "#[serde(rename = \"ip\")]")
.as_enum("Example.mode", &["Dev", "Production"])
.type_attribute("Mode", "#[derive(Default)]")
.field_attribute("Mode.Dev", "#[default]")
.opaque("Example.mapping")
.output(output_path)
.codegen(&[path])
.unwrap();
}
}