#![forbid(unsafe_code)]
use crate::BuildError;
use prost_types::FileDescriptorSet;
#[cfg(not(feature = "native-parser"))]
use std::io::Write as _;
#[cfg(not(feature = "native-parser"))]
use std::sync::atomic::{AtomicU64, Ordering};
#[cfg(not(feature = "native-parser"))]
static COUNTER: AtomicU64 = AtomicU64::new(0);
#[cfg(feature = "native-parser")]
#[inline]
fn compile_str_impl(proto_source: &str) -> Result<FileDescriptorSet, BuildError> {
crate::compile_str_native(proto_source)
}
#[cfg(not(feature = "native-parser"))]
fn compile_str_impl(proto_source: &str) -> Result<FileDescriptorSet, BuildError> {
let n = COUNTER.fetch_add(1, Ordering::Relaxed);
let temp_dir = std::env::temp_dir();
let proto_path = temp_dir.join(format!(
"oxiproto-compile-str-{}-{}.proto",
std::process::id(),
n
));
{
let mut f = std::fs::File::create(&proto_path)?;
f.write_all(proto_source.as_bytes())?;
}
let proto_filename = format!("oxiproto-compile-str-{}-{}.proto", std::process::id(), n);
let result = protox::compile(
std::iter::once(proto_filename.as_str()),
std::iter::once(temp_dir.as_path()),
)
.map_err(|e| BuildError::from_parse_string(&format!("{e:?}")));
let _ = std::fs::remove_file(&proto_path);
result
}
pub fn compile_str(proto_source: &str) -> Result<FileDescriptorSet, BuildError> {
compile_str_impl(proto_source)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn compile_valid_proto() {
let src = r#"syntax = "proto3";
package inline;
message Ping { string payload = 1; }
"#;
let fds = compile_str(src).expect("valid proto should compile");
assert_eq!(
fds.file.len(),
1,
"expected exactly one FileDescriptorProto"
);
}
#[test]
fn compile_invalid_proto_returns_parse_error() {
let src = r#"syntax = "proto3";
message Bad { string x = 1 }
"#;
let err = compile_str(src).expect_err("broken proto should fail");
match err {
BuildError::Parse { .. } => {}
other => panic!("expected BuildError::Parse, got {other:?}"),
}
}
#[cfg(not(feature = "native-parser"))]
#[test]
fn temp_file_cleaned_up_after_compile() {
let n_before = COUNTER.fetch_add(0, Ordering::SeqCst); let src = r#"syntax = "proto3"; message CleanupCheck { int32 id = 1; }"#;
let _ = compile_str(src);
let n_after = COUNTER.load(Ordering::SeqCst);
let temp_dir = std::env::temp_dir();
for n in n_before..n_after {
let path = temp_dir.join(format!(
"oxiproto-compile-str-{}-{}.proto",
std::process::id(),
n
));
assert!(
!path.exists(),
"temp file {path:?} was not cleaned up after compile_str"
);
}
}
}