embed_bytes/
lib.rs

1use bytes::Bytes;
2use std::env;
3use std::fs::{self, File};
4use std::io::{self, BufWriter, Write};
5use std::path::Path;
6
7/// Writes binary files to `OUT_DIR/embed/` and generates a Rust struct in `src/`.
8///
9/// * Binary files are written to `OUT_DIR/embed/`.
10/// * The generated `.rs` file is placed at `src/<struct_name>.rs`.
11///
12/// If the `embed-bytes` feature is enabled, the struct contains constants
13/// referencing the binary files using `include_bytes!`. Otherwise, they are
14/// empty arrays with a deprecation warning.
15///
16/// # Arguments
17///
18/// * `struct_output_path` - The path to `src/<struct_name>.rs`.
19/// * `struct_name` - The name of the generated struct.
20/// * `byte_arrays` - A vector of tuples `(field_name, content)`,
21///   where `field_name` is the associated constant and `content` is the binary.
22///
23/// # Errors
24/// Returns an `io::Error` if any file operation fails.
25pub fn write_byte_arrays(
26    struct_output_path: &Path,
27    struct_name: &str,
28    // TODO: Enforce UPPER_SNAKE_CASE: Constant `local_file` should have UPPER_SNAKE_CASE name, e.g. `LOCAL_FILE`rust-analyzernon_upper_case_globals
29    byte_arrays: Vec<(&str, Bytes)>,
30) -> io::Result<()> {
31    // Get OUT_DIR and resolve the binary output directory.
32    let out_dir = env::var("OUT_DIR")
33        .map_err(|_| io::Error::new(io::ErrorKind::NotFound, "OUT_DIR not set"))?;
34    let bin_output_path = Path::new(&out_dir).join("embed");
35
36    // Ensure binary output directory exists.
37    fs::create_dir_all(&bin_output_path)?;
38
39    // Ensure `src/` exists.
40    let src_dir = Path::new("src");
41    fs::create_dir_all(src_dir)?;
42
43    // Ensure the struct output file path is within `src/`
44    if !struct_output_path.starts_with("src/") {
45        return Err(io::Error::new(
46            io::ErrorKind::InvalidInput,
47            "Struct output path must be inside `src/`.",
48        ));
49    }
50
51    // Open the Rust file for writing.
52    let rs_file = File::create(struct_output_path)?;
53    let mut rs_writer = BufWriter::new(rs_file);
54
55    // Write header comments.
56    writeln!(
57        rs_writer,
58        "// Automatically generated file. Do not edit.\n\
59         // Generated by embed-bytes crate.\n"
60    )?;
61
62    // Write the struct definition.
63    writeln!(rs_writer, "pub struct {};", struct_name)?;
64    writeln!(rs_writer)?;
65    writeln!(rs_writer, "impl {} {{", struct_name)?;
66
67    // Process each byte array.
68    for (field, content) in &byte_arrays {
69        let bin_filename = format!("{}.bin", field);
70        let bin_path = bin_output_path.join(&bin_filename);
71
72        // Write the binary file.
73        let mut bin_file = File::create(&bin_path)?;
74        bin_file.write_all(content)?;
75
76        // Write the associated constant with conditional compilation.
77        writeln!(rs_writer, "    #[cfg(feature = \"embed-bytes\")]")?;
78        writeln!(rs_writer, "    pub const {}: &'static [u8] =", field)?;
79        writeln!(
80            rs_writer,
81            "        include_bytes!(concat!(env!(\"OUT_DIR\"), \"/embed/{}\"));",
82            bin_filename
83        )?;
84        writeln!(rs_writer)?;
85
86        writeln!(rs_writer, "    #[cfg(not(feature = \"embed-bytes\"))]")?;
87        writeln!(
88            rs_writer,
89            "    #[deprecated(note = \"Feature flag not set, using empty array\")]"
90        )?;
91        writeln!(rs_writer, "    pub const {}: &'static [u8] = &[];", field)?;
92        writeln!(rs_writer)?;
93    }
94
95    writeln!(rs_writer, "}}")?;
96
97    Ok(())
98}