protox/
lib.rs

1//! A rust implementation of the protobuf compiler.
2//!
3//! For convenient compilation of protobuf source files in a single function, see
4//! [`compile()`]. For more options see [`Compiler`].
5//!
6//! # Examples
7//!
8//! Usage with [`prost-build`](https://crates.io/crates/prost-build):
9//!
10//! ```
11//! # use std::{env, fs, path::PathBuf};
12//! # use prost::Message;
13//! # let tempdir = tempfile::TempDir::new().unwrap();
14//! # env::set_current_dir(&tempdir).unwrap();
15//! # env::set_var("OUT_DIR", tempdir.path());
16//! # fs::write("root.proto", "").unwrap();
17//! let file_descriptors = protox::compile(["root.proto"], ["."]).unwrap();
18//! prost_build::compile_fds(file_descriptors).unwrap();
19//! ```
20//!
21//! Usage with [`tonic-build`](https://crates.io/crates/tonic-build):
22//!
23//! ```rust,ignore
24//! # use std::{env, fs, path::PathBuf};
25//! # let tempdir = tempfile::TempDir::new().unwrap();
26//! # env::set_current_dir(&tempdir).unwrap();
27//! # env::set_var("OUT_DIR", tempdir.path());
28//! # fs::write("root.proto", "").unwrap();
29//! use protox::prost::Message;
30//!
31//! let file_descriptors = protox::compile(["root.proto"], ["."]).unwrap();
32//!
33//! tonic_build::configure()
34//!     .build_server(true)
35//!     .compile_fds(file_descriptors)
36//!     .unwrap();
37//! ```
38//!
39//! ### Error messages
40//!
41//! This crate uses [`miette`](https://crates.io/crates/miette) to add additional details to errors. For nice error messages, add `miette` as a dependency with the `fancy` feature enabled and return a [`miette::Result`](https://docs.rs/miette/latest/miette/type.Result.html) from your build script.
42//!
43//! ```rust
44//! # use std::{env, fs, path::PathBuf};
45//! fn main() -> miette::Result<()> {
46//! # let tempdir = tempfile::TempDir::new().unwrap();
47//! # env::set_current_dir(&tempdir).unwrap();
48//! # env::set_var("OUT_DIR", tempdir.path());
49//! # fs::write("root.proto", "").unwrap();
50//!   let _ = protox::compile(["root.proto"], ["."])?;
51//!
52//!   Ok(())
53//! }
54//! ```
55//!
56//! Example error message:
57//!
58//! ```text
59//! Error:
60//!   × name 'Bar' is not defined
61//!    ╭─[root.proto:3:1]
62//!  3 │ message Foo {
63//!  4 │     Bar bar = 1;
64//!    ·     ─┬─
65//!    ·      ╰── found here
66//!  5 │ }
67//!    ╰────
68//! ```
69#![warn(missing_debug_implementations, missing_docs)]
70#![deny(unsafe_code)]
71#![doc(html_root_url = "https://docs.rs/protox/0.8.0/")]
72
73pub mod file;
74
75mod compile;
76mod error;
77
78use std::path::Path;
79
80pub use {prost, prost_reflect};
81
82pub use self::compile::Compiler;
83pub use self::error::Error;
84
85/// Compiles a set of protobuf files using the given include paths.
86///
87/// For more control over how files are compiled, see [`Compiler`]. This function is equivalent to:
88///
89/// ```rust
90/// # use protox::Compiler;
91/// # fn main() -> Result<(), protox::Error> {
92/// # let files: Vec<std::path::PathBuf> = vec![];
93/// # let includes: Vec<std::path::PathBuf> = vec![".".into()];
94/// let file_descriptor_set = Compiler::new(includes)?
95///     .include_source_info(true)
96///     .include_imports(true)
97///     .open_files(files)?
98///     .file_descriptor_set();
99/// # Ok(())
100/// # }
101/// ```
102///
103/// # Examples
104///
105/// ```
106/// # use std::fs;
107/// # use prost_types::{
108/// #    DescriptorProto, FieldDescriptorProto, field_descriptor_proto::{Label, Type}, FileDescriptorSet, FileDescriptorProto,
109/// #    SourceCodeInfo, source_code_info::Location
110/// # };
111/// # use protox::compile;
112/// # let tempdir = tempfile::TempDir::new().unwrap();
113/// # std::env::set_current_dir(&tempdir).unwrap();
114/// #
115/// fs::write("bar.proto", "
116///     message Bar { }
117/// ").unwrap();
118/// fs::write("root.proto", "
119///     import 'bar.proto';
120///
121///     message Foo {
122///         optional Bar bar = 1;
123///     }
124/// ").unwrap();
125///
126/// assert_eq!(compile(["root.proto"], ["."]).unwrap(), FileDescriptorSet {
127///     file: vec![
128///         FileDescriptorProto {
129///             name: Some("bar.proto".to_owned()),
130///             message_type: vec![DescriptorProto {
131///                 name: Some("Bar".to_owned()),
132///                 ..Default::default()
133///             }],
134///             source_code_info: Some(SourceCodeInfo {
135///                 location: vec![
136///                     Location { path: vec![], span: vec![1, 4, 19], ..Default::default() },
137///                     Location { path: vec![4, 0], span: vec![1, 4, 19], ..Default::default() },
138///                     Location { path: vec![4, 0, 1], span: vec![1, 12, 15], ..Default::default() },
139///                ],
140///             }),
141///             ..Default::default()
142///         },
143///         FileDescriptorProto {
144///             name: Some("root.proto".to_owned()),
145///             dependency: vec!["bar.proto".to_owned()],
146///             message_type: vec![DescriptorProto {
147///                 name: Some("Foo".to_owned()),
148///                 field: vec![FieldDescriptorProto {
149///                     name: Some("bar".to_owned()),
150///                     number: Some(1),
151///                     label: Some(Label::Optional as _),
152///                     r#type: Some(Type::Message as _),
153///                     type_name: Some(".Bar".to_owned()),
154///                     json_name: Some("bar".to_owned()),
155///                     ..Default::default()
156///                 }],
157///                 ..Default::default()
158///             }],
159///             source_code_info: Some(SourceCodeInfo {
160///                 location: vec![
161///                     Location { path: vec![], span: vec![1, 4, 5, 5], ..Default::default() },
162///                     Location { path: vec![3, 0], span: vec![1, 4, 23], ..Default::default() },
163///                     Location { path: vec![4, 0], span: vec![3, 4, 5, 5], ..Default::default() },
164///                     Location { path: vec![4, 0, 1], span: vec![3, 12, 15], ..Default::default() },
165///                     Location { path: vec![4, 0, 2, 0], span: vec![4, 8, 29], ..Default::default() },
166///                     Location { path: vec![4, 0, 2, 0, 1], span: vec![4, 21, 24], ..Default::default() },
167///                     Location { path: vec![4, 0, 2, 0, 3], span: vec![4, 27, 28], ..Default::default() },
168///                     Location { path: vec![4, 0, 2, 0, 4], span: vec![4, 8, 16], ..Default::default() },
169///                     Location { path: vec![4, 0, 2, 0, 6], span: vec![4, 17, 20], ..Default::default() },
170///                 ],
171///             }),
172///             ..Default::default()
173///         },
174///     ],
175///     ..Default::default()
176/// });
177/// ```
178pub fn compile(
179    files: impl IntoIterator<Item = impl AsRef<Path>>,
180    includes: impl IntoIterator<Item = impl AsRef<Path>>,
181) -> Result<prost_types::FileDescriptorSet, Error> {
182    Ok(Compiler::new(includes)?
183        .include_source_info(true)
184        .include_imports(true)
185        .open_files(files)?
186        .file_descriptor_set())
187}