protoc_prebuilt/
init.rs

1use std::{
2  env::{ consts::{ ARCH, OS }, var },
3  fs::metadata,
4  io::{ Error as IoError, ErrorKind },
5  path::PathBuf,
6  process::Command,
7  str::from_utf8
8};
9use crate::{
10  error::Error,
11  helpers::var_bool,
12  force::{ get_force_bin, get_force_include },
13  install::install,
14  path::{ get_bin_path, get_include_path },
15  version::{ compare_versions, get_protoc_asset_name }
16};
17
18/// Install pre-built protobuf compiler binary if it hasn't been done before
19/// and return paths to it content
20///
21/// Version parameter should be a tag name from protobuf repository without `v` prefix,
22/// for example, "21.12" or "22.0-rc3"
23/// (see [protobuf repository tags](https://github.com/protocolbuffers/protobuf/tags)).
24///
25/// Return a tuple contains paths to `protoc` binary and `include` directory.
26pub fn init(version: &str) -> Result<(PathBuf, PathBuf), Error> {
27  let protoc_bin: PathBuf = get_force_bin()?.map_or_else(|| -> Result<PathBuf, Error> {
28    let out_dir = PathBuf::from(var("OUT_DIR").map_err(Error::VarError)?);
29
30    let protoc_asset_name = get_protoc_asset_name(version, OS, ARCH)?;
31    let protoc_out_dir = out_dir.join(&protoc_asset_name);
32
33    // Install if installation directory doesn't exist
34    if !protoc_out_dir.exists() {
35      install(version, &out_dir, &protoc_asset_name, &protoc_out_dir)?;
36    }
37
38    Ok(get_bin_path(version, &protoc_out_dir))
39  }, Ok)?;
40
41  // Check binary file exists
42  metadata(&protoc_bin).map_err(Error::Io)?;
43
44  // Test run binary file
45  let output = Command::new(&protoc_bin).args(["--version"]).output().map_err(Error::Io)?;
46  if !output.status.success() {
47    return Err(Error::Io(IoError::new(ErrorKind::Other, "test run protoc fail")))
48  }
49
50  if !var_bool("PROTOC_PREBUILT_NOT_CHECK_VERSION") {
51    let stdout = match from_utf8(&output.stdout) {
52      Ok(stdout) => stdout,
53      Err(_) => return Err(
54        Error::Io(IoError::new(ErrorKind::Other, "parse test run protoc output fail"))
55      )
56    };
57
58    let returned = stdout.trim().replace("libprotoc ", "");
59
60    if !compare_versions(version, &returned) {
61      return Err(Error::VersionCheck((version, returned)))
62    }
63  }
64
65  let protoc_include: PathBuf = get_force_include()?
66    .map_or_else(|| Ok(get_include_path(version, &protoc_bin)), Ok)?;
67
68  Ok((protoc_bin, protoc_include))
69}