use std::error::Error;
use std::io::Read;
use std::{env, fs, path::Path};
macro_rules! print_warn {
($($tokens: tt)*) => {
println!("cargo:warning={}", format!($($tokens)*))
}
}
const MAX_CRATES_IO_UPLOAD_SIZE: usize = 1024 * 1024 * 10;
const GH_CLI_PATH: &str = "gh_cli/gh";
const GH_CLI_COMPRESSED_PATH: &str = "gh_cli/compressed/gh_cli_bz2";
const INCLUDE_GH_CLI_FILE: &str = "include_gh_cli.rs";
pub fn bzip2_compress(input: &[u8]) -> Result<Vec<u8>, Box<dyn Error>> {
let mut e = bzip2::bufread::BzEncoder::new(input, bzip2::Compression::new(9));
let mut out = Vec::new();
e.read_to_end(&mut out)?;
Ok(out)
}
fn main() {
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=gh_cli/gh");
let env_out_dir = env::var_os("OUT_DIR").expect("OUT_DIR not set");
let out_dir_path = Path::new(&env_out_dir);
let gh_cli_path = Path::new(GH_CLI_PATH);
if !gh_cli_path.exists() {
print_warn!("{GH_CLI_PATH} does not exist - relying on the archived binary in {GH_CLI_COMPRESSED_PATH}");
} else {
let gh_cli_bytes = fs::read(gh_cli_path).expect("Failed to read gh_cli/gh");
let compressed_gh_cli_bytes = bzip2_compress(&gh_cli_bytes).unwrap();
assert!(compressed_gh_cli_bytes.len() < MAX_CRATES_IO_UPLOAD_SIZE);
fs::write(GH_CLI_COMPRESSED_PATH, compressed_gh_cli_bytes)
.expect("Failed to write gh_cli_bz2");
}
include_gh_cli(out_dir_path).unwrap();
}
fn include_gh_cli(out_dir: &Path) -> Result<(), Box<dyn Error>> {
let gh_cli_bytes = fs::read(GH_CLI_COMPRESSED_PATH).expect("Failed to read gh_cli/gh");
let gh_cli_size = gh_cli_bytes.len();
assert!(
gh_cli_size < MAX_CRATES_IO_UPLOAD_SIZE,
"gh_cli_bz2 size exceeds maximum size for crates.io"
);
let gh_cli_path = out_dir.join("gh_cli_bz2");
fs::write(&gh_cli_path, gh_cli_bytes).expect("Failed to write gh_cli_bz2");
let include_gh_cli_rs_contents = format_include_gh_cli_rs(gh_cli_size, &gh_cli_path);
let include_gh_cli_rs_path = out_dir.join(INCLUDE_GH_CLI_FILE);
fs::write(include_gh_cli_rs_path, include_gh_cli_rs_contents)
.expect("Failed to write include_gh_cli.rs");
Ok(())
}
fn format_include_gh_cli_rs(gh_cli_compressed_size: usize, gh_cli_path: &Path) -> String {
format!(
r#"
pub const GH_CLI_BYTES: &[u8; {gh_cli_compressed_size}] = include_bytes!({gh_cli_path:?});
"#
)
}