1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
use anyhow::bail;
use reqwest::StatusCode;
use std::io::Cursor;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::{env, fs};
pub fn protoc(version: &str, out_dir: &Path) -> anyhow::Result<PathBuf> {
let protoc_path = ensure_protoc_installed(version, out_dir)?;
Ok(protoc_path)
}
fn ensure_protoc_installed(version: &str, install_dir: &Path) -> anyhow::Result<PathBuf> {
let release_name = get_protoc_release_name(version);
let protoc_dir = install_dir.join(format!("protoc-fetcher/{release_name}"));
let protoc_path = protoc_dir.join("bin/protoc");
if protoc_path.exists() {
println!("protoc with correct version is already installed.");
} else {
println!("protoc v{version} not found, downloading...");
download_protoc(&protoc_dir, &release_name, version)?;
}
println!(
"`protoc --version`: {}",
get_protoc_version(&protoc_path).unwrap()
);
Ok(protoc_path)
}
fn download_protoc(protoc_dir: &Path, release_name: &str, version: &str) -> anyhow::Result<()> {
let archive_url = protoc_release_archive_url(release_name, version);
let response = reqwest::blocking::get(archive_url)?;
if response.status() != StatusCode::OK {
bail!(
"Error downloading release archive: {} {}",
response.status(),
response.text().unwrap_or_default()
);
}
println!("Download successful.");
fs::create_dir_all(protoc_dir)?;
let cursor = Cursor::new(response.bytes()?);
zip_extract::extract(cursor, protoc_dir, false)?;
println!("Extracted archive.");
let protoc_path = protoc_dir.join("bin/protoc");
if !protoc_path.exists() {
bail!("Extracted protoc archive, but could not find bin/protoc!");
}
println!("protoc installed successfully: {:?}", &protoc_path);
Ok(())
}
fn protoc_release_archive_url(release_name: &str, version: &str) -> String {
let archive_url =
format!("https://github.com/protocolbuffers/protobuf/releases/download/v{version}/{release_name}.zip");
println!("Release URL: {archive_url}");
archive_url
}
fn get_protoc_release_name(version: &str) -> String {
let mut platform = env::consts::OS;
let mut arch = env::consts::ARCH;
println!("Detected: {}, {}", platform, arch);
if platform == "macos" {
platform = "osx"; }
if arch == "aarch64" {
arch = "aarch_64";
}
format!("protoc-{version}-{platform}-{arch}")
}
fn get_protoc_version(protoc_path: &Path) -> anyhow::Result<String> {
let version = String::from_utf8(Command::new(&protoc_path).arg("--version").output()?.stdout)?;
Ok(version)
}