oxiproto_build/lib.rs
1#![forbid(unsafe_code)]
2
3//! `oxiproto-build` — Pure Rust `.proto` → Rust codegen, no `protoc` required.
4//!
5//! Downstream crates add this as a `[build-dependencies]` entry and call
6//! [`compile_protos`] (or [`Builder`]) from their `build.rs`.
7//!
8//! # Quick start
9//!
10//! ```no_run
11//! fn main() -> Result<(), Box<dyn std::error::Error>> {
12//! oxiproto_build::compile_protos(&["proto/service.proto"], &["proto/"])?;
13//! Ok(())
14//! }
15//! ```
16
17pub mod builder;
18pub mod compile_str;
19pub mod error;
20/// Native `.proto` parser (Slice P fills this in).
21pub mod parser;
22
23pub use builder::Builder;
24pub use compile_str::compile_str as compile_str_fn;
25pub use error::BuildError;
26
27/// Re-export `compile_str` under its canonical name for test compatibility.
28pub use compile_str::compile_str;
29
30use std::path::Path;
31
32/// Compile `.proto` files to Rust without requiring `protoc` on `PATH`.
33///
34/// Generated `.rs` files are written to `OUT_DIR` (set by Cargo in
35/// `build.rs` context).
36///
37/// # Arguments
38///
39/// * `protos` — Paths to the `.proto` source files to compile.
40/// * `includes` — Include directories used to resolve imports inside the
41/// `.proto` files.
42///
43/// # Errors
44///
45/// Returns [`oxiproto_core::OxiProtoError::ParseError`] if `protox` cannot
46/// parse or resolve the proto sources, or
47/// [`oxiproto_core::OxiProtoError::CodegenError`] if `prost-build` cannot
48/// emit Rust from the resolved descriptors.
49///
50/// # Example (in `build.rs`)
51///
52/// ```no_run
53/// fn main() -> Result<(), Box<dyn std::error::Error>> {
54/// oxiproto_build::compile_protos(&["proto/service.proto"], &["proto/"])?;
55/// Ok(())
56/// }
57/// ```
58pub fn compile_protos(
59 protos: &[impl AsRef<Path>],
60 includes: &[impl AsRef<Path>],
61) -> Result<(), oxiproto_core::OxiProtoError> {
62 Builder::new()
63 .compile(protos, includes)
64 .map_err(oxiproto_core::OxiProtoError::from)
65}
66
67/// Parse `.proto` files to a [`prost_types::FileDescriptorSet`] without
68/// invoking `protoc` or writing any files.
69///
70/// This is the low-level building block used by [`compile_protos`] and by the
71/// `oxiproto-cli` binary. It delegates to [`protox::compile`] (pure Rust).
72///
73/// # Errors
74///
75/// Returns [`oxiproto_core::OxiProtoError::ParseError`] if `protox` cannot
76/// parse or resolve the proto sources.
77///
78/// # Example
79///
80/// ```no_run
81/// let fds = oxiproto_build::compile_to_fds(
82/// &["proto/hello.proto"],
83/// &["proto/"],
84/// )?;
85/// println!("{} file(s) in FDS", fds.file.len());
86/// # Ok::<(), oxiproto_core::OxiProtoError>(())
87/// ```
88pub fn compile_to_fds(
89 protos: &[impl AsRef<Path>],
90 includes: &[impl AsRef<Path>],
91) -> Result<prost_types::FileDescriptorSet, oxiproto_core::OxiProtoError> {
92 Builder::new()
93 .compile_to_fds(protos, includes)
94 .map_err(oxiproto_core::OxiProtoError::from)
95}
96
97/// Compile a proto3 source string to a [`prost_types::FileDescriptorSet`]
98/// using the native pure-Rust parser (no `protox` dependency).
99///
100/// Requires the `native-parser` feature.
101///
102/// This function handles only single-file protos with no imports. For
103/// multi-file protos use [`compile_files_native`].
104///
105/// # Errors
106///
107/// Returns [`BuildError`] on parse, resolution, or I/O failure.
108#[cfg(feature = "native-parser")]
109pub fn compile_str_native(
110 proto_source: &str,
111) -> Result<prost_types::FileDescriptorSet, BuildError> {
112 use crate::parser::{build_file_descriptor_set, parse_file, resolve};
113
114 let proto_file = parse_file(proto_source).map_err(|e| BuildError::Parse {
115 file: "<inline>.proto".to_owned(),
116 line: 0,
117 col: 0,
118 message: e.to_string(),
119 })?;
120 // Single-file mode: reject any imports
121 if !proto_file.imports.is_empty() {
122 return Err(BuildError::Parse {
123 file: "<inline>.proto".to_owned(),
124 line: 0,
125 col: 0,
126 message:
127 "imports are not supported in compile_str_native; use compile_files_native instead"
128 .to_owned(),
129 });
130 }
131 let resolved = resolve(&proto_file).map_err(|e| BuildError::Parse {
132 file: "<inline>.proto".to_owned(),
133 line: 0,
134 col: 0,
135 message: e.to_string(),
136 })?;
137 Ok(build_file_descriptor_set(
138 &resolved,
139 "<inline>.proto",
140 proto_source,
141 ))
142}
143
144/// Compile a set of `.proto` files to a [`prost_types::FileDescriptorSet`]
145/// using the native parser.
146///
147/// Include directories are searched in order to resolve imports.
148/// Well-known types (google/protobuf/*.proto) are loaded from the bundled pool.
149///
150/// # Errors
151///
152/// Returns [`BuildError`] if any file cannot be found, parsed, or type-resolved.
153#[cfg(feature = "native-parser")]
154pub fn compile_files_native(
155 protos: &[impl AsRef<std::path::Path>],
156 includes: &[impl AsRef<std::path::Path>],
157) -> Result<prost_types::FileDescriptorSet, BuildError> {
158 let inc: Vec<std::path::PathBuf> = includes.iter().map(|p| p.as_ref().to_path_buf()).collect();
159 parser::loader::compile_files(protos, &inc)
160}