oxidef/
compile_builder.rs

1use std::path::{Path, PathBuf};
2
3use anyhow::Context;
4
5use crate::generate::{generate, GenerateCommand};
6
7pub struct CompileBuilder {
8    file_paths: Vec<PathBuf>,
9    codecs: Vec<String>,
10    index: Option<String>,
11}
12
13/// Intended for use in Rust build scripts (`build.rs` files).
14///
15/// Start a compiler
16pub fn compile_files<P, PS>(files: PS) -> CompileBuilder
17where
18    P: AsRef<Path>,
19    PS: AsRef<[P]>,
20{
21    let file_paths: Vec<PathBuf> = files
22        .as_ref()
23        .iter()
24        .map(|path| path.as_ref().to_owned())
25        .collect();
26
27    for file in &file_paths {
28        let Some(file) = file.to_str() else {
29            continue;
30        };
31        println!("cargo:rerun-if-changed={file}");
32    }
33
34    CompileBuilder {
35        file_paths,
36        codecs: Vec::new(),
37        index: None,
38    }
39}
40
41impl CompileBuilder {
42    /// Compile the `compact1` codec
43    pub fn compact1(mut self) -> Self {
44        self.codecs.push("compact1".to_owned());
45        self
46    }
47
48    /// Compile the `validation` 'codec'
49    pub fn validation(mut self) -> Self {
50        self.codecs.push("validation".to_owned());
51        self
52    }
53
54    /// Compile the `axum` 'codec'
55    pub fn axum(mut self) -> Self {
56        self.codecs.push("axum".to_owned());
57        self
58    }
59
60    /// Finish Rust codegen to the specified directory.
61    ///
62    /// At a minimum, one file for each
63    /// Oxidef module will be generated, along with an index file if
64    /// requested (TODO currently only available through `finish_rust`...).
65    ///
66    /// # In a Rust build script (`build.rs`)?
67    ///
68    /// See [`finish_rust`] for a shorthand.
69    pub fn finish_rust_to_specified_dir(mut self, target: &Path) -> anyhow::Result<()> {
70        std::fs::create_dir_all(target).with_context(|| format!("could not create {target:?}"))?;
71
72        if self.codecs.is_empty() {
73            self = self.compact1().validation();
74        }
75
76        // TODO add cargo rerun output
77
78        generate(&GenerateCommand {
79            grammars: self.file_paths,
80            opinion_files: Vec::new(),
81            codecs: self.codecs,
82            target_language: "rust".to_owned(),
83            target_path: target.to_owned(),
84            index: self.index,
85        })?;
86
87        Ok(())
88    }
89
90    /// Finish Rust codegen to a named subdirectory of `OUT_DIR`.
91    ///
92    /// This is the most comfortable option to use within Rust build scripts.
93    pub fn finish_rust(mut self, subdir_name: &str) -> anyhow::Result<()> {
94        let subdir =
95            Path::new(&std::env::var("OUT_DIR").context("no OUT_DIR env var")?).join(subdir_name);
96
97        self.index = Some("mod.rs".to_owned());
98        self.finish_rust_to_specified_dir(&subdir)
99    }
100}