protoc_bin_vendored/
lib.rs

1//! `protoc` binary downloaded and stored inside the crate.
2//!
3//! Can be used to avoid downloading and installing `protoc` binary.
4//!
5//! # Example
6//!
7//! ```no_run
8//! # let _ =
9//! protoc_bin_vendored::protoc_bin_path().unwrap()
10//! # ;
11//! ```
12//!
13//! returns a path to a `protoc` binary packaged into the crate.
14//!
15//! Crate also packs `.proto` files distributed with protobuf:
16//!
17//! ```no_run
18//! # let _ =
19//! protoc_bin_vendored::include_path().unwrap()
20//! # ;
21//! ```
22
23#![deny(missing_docs)]
24#![deny(rustdoc::broken_intra_doc_links)]
25
26use std::env;
27use std::fmt;
28use std::path::PathBuf;
29
30/// Error returned when a binary is not available.
31#[derive(Debug)]
32pub struct Error {
33    os: &'static str,
34    arch: &'static str,
35}
36
37impl fmt::Display for Error {
38    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39        write!(
40            f,
41            "protoc binary cannot be found for platform {}-{}",
42            self.os, self.arch
43        )
44    }
45}
46
47impl std::error::Error for Error {}
48
49#[allow(non_camel_case_types)]
50enum ArchCrate {
51    Linux_X86_32,
52    Linux_X86_64,
53    Linux_Aarch_64,
54    Linux_Ppcle_64,
55    Linux_S390_64,
56    Macos_Aarch_64,
57    Macos_x86_64,
58    Win32,
59}
60
61impl ArchCrate {
62    fn detect() -> Result<ArchCrate, Error> {
63        Ok(match (env::consts::OS, env::consts::ARCH) {
64            ("linux", "x86") => ArchCrate::Linux_X86_32,
65            ("linux", "x86_64") => ArchCrate::Linux_X86_64,
66            ("linux", "aarch64") => ArchCrate::Linux_Aarch_64,
67            ("linux", "powerpc64") => ArchCrate::Linux_Ppcle_64,
68            ("linux", "s390x") => ArchCrate::Linux_S390_64,
69            ("macos", "x86_64") => ArchCrate::Macos_x86_64,
70            ("macos", "aarch64") => ArchCrate::Macos_Aarch_64,
71            ("windows", _) => ArchCrate::Win32,
72            (os, arch) => return Err(Error { os, arch }),
73        })
74    }
75}
76
77/// Return a path to `protoc` binary.
78///
79/// This function returns an error when binary is not available for
80/// the current operating system and architecture.
81pub fn protoc_bin_path() -> Result<PathBuf, Error> {
82    Ok(match ArchCrate::detect()? {
83        ArchCrate::Linux_X86_32 => protoc_bin_vendored_linux_x86_32::protoc_bin_path(),
84        ArchCrate::Linux_X86_64 => protoc_bin_vendored_linux_x86_64::protoc_bin_path(),
85        ArchCrate::Linux_Aarch_64 => protoc_bin_vendored_linux_aarch_64::protoc_bin_path(),
86        ArchCrate::Linux_Ppcle_64 => protoc_bin_vendored_linux_ppcle_64::protoc_bin_path(),
87        ArchCrate::Linux_S390_64 => protoc_bin_vendored_linux_s390_64::protoc_bin_path(),
88        ArchCrate::Macos_Aarch_64 => protoc_bin_vendored_macos_aarch_64::protoc_bin_path(),
89        ArchCrate::Macos_x86_64 => protoc_bin_vendored_macos_x86_64::protoc_bin_path(),
90        ArchCrate::Win32 => protoc_bin_vendored_win32::protoc_bin_path(),
91    })
92}
93
94pub(crate) fn include_path_for_arch(arch_crate: &ArchCrate) -> PathBuf {
95    match arch_crate {
96        ArchCrate::Linux_X86_32 => protoc_bin_vendored_linux_x86_32::include_path(),
97        ArchCrate::Linux_X86_64 => protoc_bin_vendored_linux_x86_64::include_path(),
98        ArchCrate::Linux_Aarch_64 => protoc_bin_vendored_linux_aarch_64::include_path(),
99        ArchCrate::Linux_Ppcle_64 => protoc_bin_vendored_linux_ppcle_64::include_path(),
100        ArchCrate::Linux_S390_64 => protoc_bin_vendored_linux_s390_64::include_path(),
101        ArchCrate::Macos_Aarch_64 => protoc_bin_vendored_macos_aarch_64::include_path(),
102        ArchCrate::Macos_x86_64 => protoc_bin_vendored_macos_x86_64::include_path(),
103        ArchCrate::Win32 => protoc_bin_vendored_win32::include_path(),
104    }
105}
106
107/// Include path which contains protobuf bundled `.proto` (like `descriptor.proto`).
108///
109/// Include directory content is guaranteed to be identical regardless of the platform.
110pub fn include_path() -> Result<PathBuf, Error> {
111    Ok(include_path_for_arch(&ArchCrate::detect()?))
112}
113
114#[cfg(test)]
115mod test {
116    use std::fs;
117    use std::io::Read;
118    use std::path::Path;
119    use std::path::PathBuf;
120    use std::process;
121
122    use crate::include_path_for_arch;
123    use crate::ArchCrate;
124
125    #[test]
126    fn include_path() {
127        assert!(crate::include_path()
128            .unwrap()
129            .join("google/protobuf/descriptor.proto")
130            .exists());
131    }
132
133    #[test]
134    fn arch_crates_includes_identical() {
135        fn compare_recursively(a: &Path, b: &Path) {
136            assert_eq!(a.is_file(), b.is_file());
137            if a.is_file() {
138                let a_content = fs::read(a).unwrap();
139                let b_content = fs::read(b).unwrap();
140                assert_eq!(a_content, b_content);
141            } else {
142                let mut a_files: Vec<PathBuf> = fs::read_dir(a)
143                    .unwrap()
144                    .map(|e| e.unwrap().path())
145                    .collect();
146                let mut b_files: Vec<PathBuf> = fs::read_dir(b)
147                    .unwrap()
148                    .map(|e| e.unwrap().path())
149                    .collect();
150                a_files.sort();
151                b_files.sort();
152                let mut a_files = a_files.as_slice();
153                let mut b_files = b_files.as_slice();
154                while !a_files.is_empty() || !b_files.is_empty() {
155                    let (a_next, a_rem) = a_files.split_first().unwrap();
156                    let (b_next, b_rem) = b_files.split_first().unwrap();
157
158                    compare_recursively(a_next, b_next);
159
160                    a_files = a_rem;
161                    b_files = b_rem;
162                }
163                assert!(a_files.is_empty());
164                assert!(b_files.is_empty());
165            }
166        }
167
168        compare_recursively(
169            &include_path_for_arch(&ArchCrate::Linux_X86_64),
170            &include_path_for_arch(&ArchCrate::Linux_X86_64),
171        );
172        compare_recursively(
173            &include_path_for_arch(&ArchCrate::Linux_X86_64),
174            &include_path_for_arch(&ArchCrate::Linux_X86_32),
175        );
176        compare_recursively(
177            &include_path_for_arch(&ArchCrate::Linux_X86_64),
178            &include_path_for_arch(&ArchCrate::Linux_Aarch_64),
179        );
180        compare_recursively(
181            &include_path_for_arch(&ArchCrate::Linux_X86_64),
182            &include_path_for_arch(&ArchCrate::Linux_Ppcle_64),
183        );
184        compare_recursively(
185            &include_path_for_arch(&ArchCrate::Linux_X86_64),
186            &include_path_for_arch(&ArchCrate::Linux_S390_64),
187        );
188        compare_recursively(
189            &include_path_for_arch(&ArchCrate::Linux_X86_64),
190            &include_path_for_arch(&ArchCrate::Macos_Aarch_64),
191        );
192        compare_recursively(
193            &include_path_for_arch(&ArchCrate::Linux_X86_64),
194            &include_path_for_arch(&ArchCrate::Macos_x86_64),
195        );
196        compare_recursively(
197            &include_path_for_arch(&ArchCrate::Linux_X86_64),
198            &include_path_for_arch(&ArchCrate::Win32),
199        );
200    }
201
202    #[test]
203    fn smoke() {
204        let process = process::Command::new(crate::protoc_bin_path().unwrap())
205            .arg("--version")
206            .stdin(process::Stdio::null())
207            .stdout(process::Stdio::piped())
208            .spawn()
209            .unwrap();
210        let mut stdout = String::new();
211        process.stdout.unwrap().read_to_string(&mut stdout).unwrap();
212        assert!(stdout.contains("libprotoc"), "stdout is: {:?}", stdout)
213    }
214}