bui_backend_codegen/
lib.rs

1//! build-time code generation for the `bui-backend` crate
2#![deny(missing_docs)]
3
4use std::error::Error;
5use std::fs::OpenOptions;
6use std::io::Write;
7use std::path::Path;
8
9/// Do codegen to write a file (`codegen_fname`) which includes
10/// the contents of all entries in `files_dir`.
11#[cfg(feature = "bundle_files")]
12fn create_codegen_file<P, Q>(files_dir: P, codegen_fname: Q) -> Result<(), std::io::Error>
13where
14    P: AsRef<Path>,
15    Q: AsRef<Path>,
16{
17    // Collect list of files to include
18    let entries = walkdir::WalkDir::new(files_dir.as_ref())
19        .into_iter()
20        .map(|entry| entry.map(|entry| entry.path().into()))
21        .collect::<Result<Vec<std::path::PathBuf>, walkdir::Error>>()?;
22
23    // Make sure we recompile if these files change
24    println!("cargo:rerun-if-changed={}", files_dir.as_ref().display());
25    for entry in entries.iter() {
26        println!("cargo:rerun-if-changed={}", entry.display());
27    }
28
29    // Check that at least one of the needed files is there.
30    let required: std::path::PathBuf = files_dir.as_ref().join("index.html");
31    if !entries.contains(&required) {
32        return Err(std::io::Error::new(
33            std::io::ErrorKind::Other,
34            format!("no {:?} file", required),
35        ));
36    }
37
38    let codegen_fname_str = format!("{}", codegen_fname.as_ref().display());
39    // Write the contents of the files.
40    includedir_codegen::start("PUBLIC")
41        .dir(files_dir, includedir_codegen::Compression::None)
42        .build(&codegen_fname_str)?;
43    Ok(())
44}
45
46/// Create an empty file (`codegen_fname`).
47#[cfg(feature = "serve_files")]
48fn create_codegen_file<P, Q>(_: P, codegen_fname: Q) -> Result<(), Box<dyn Error>>
49where
50    P: AsRef<Path>,
51    Q: AsRef<Path>,
52{
53    let out_dir = std::env::var("OUT_DIR")?;
54    let dest_path = std::path::Path::new(&out_dir).join(codegen_fname);
55    std::fs::File::create(dest_path)?;
56    Ok(())
57}
58
59#[cfg(not(any(feature = "bundle_files", feature = "serve_files")))]
60fn create_codegen_file<P, Q>(_: P, codegen_fname: Q) -> Result<(), Box<Error>>
61where
62    P: AsRef<Path>,
63    Q: AsRef<Path>,
64{
65    // Intentionally trigger a compile time error to force a feature
66    // flag to be used.
67    compile_error!(
68        "You are attempting to compile without a required feature flag \
69    being used. You must use one of either `bundle_files` or `serve_files`"
70    );
71}
72
73/// Update the codegen file (`codegen_fname`) to include
74/// the `Config`.
75fn include_config<P, Q>(files_dir: P, codegen_fname: Q) -> Result<(), Box<dyn Error>>
76where
77    P: AsRef<Path>,
78    Q: AsRef<Path>,
79{
80    let out_dir = std::env::var("OUT_DIR")?;
81    let dest_path = std::path::Path::new(&out_dir).join(codegen_fname);
82
83    let mut f = OpenOptions::new().append(true).open(dest_path)?;
84
85    writeln!(f, "use bui_backend::lowlevel::Config;")?;
86    writeln!(f, "fn get_default_config() -> Config {{")?;
87    writeln!(f, "    Config {{")?;
88    writeln!(
89        f,
90        "        serve_filepath: std::path::Path::new(r#\"{}\"#),",
91        files_dir.as_ref().display()
92    )?;
93    #[cfg(feature = "bundle_files")]
94    {
95        writeln!(f, "        bundled_files: &PUBLIC,")?;
96    }
97    writeln!(f, "        channel_size: 10,")?;
98    writeln!(f, "        cookie_name: \"client\".into(),")?;
99    writeln!(f, "    }}")?;
100    writeln!(f, "}}")?;
101
102    Ok(())
103}
104
105/// Write a file at the location specified by `generated_path`.
106///
107/// This file should be included with the `include!` directive into your rust
108/// source code. This should be called from the `build.rs` script in your crate.
109/// This will define the variable `BUI_BACKEND_CONFIG` which should be passed to
110/// `bui_backend::BuiBackend::new()` to configure it correctly. See the
111/// `bui-demo` for example usage.
112pub fn codegen<P, Q>(files_dir: P, generated_path: Q) -> Result<(), Box<dyn Error>>
113where
114    P: AsRef<Path>,
115    Q: AsRef<Path>,
116{
117    create_codegen_file(&files_dir, &generated_path)?;
118    include_config(&files_dir, &generated_path)
119}