protobuf_parse/
parser.rs

1use std::collections::HashSet;
2use std::ffi::OsStr;
3use std::ffi::OsString;
4use std::path::Path;
5use std::path::PathBuf;
6
7use anyhow::Context;
8use protobuf::descriptor::FileDescriptorSet;
9
10use crate::protoc;
11use crate::pure;
12use crate::which_parser::WhichParser;
13use crate::ParsedAndTypechecked;
14
15/// Configure and invoke `.proto` parser.
16#[derive(Default, Debug)]
17pub struct Parser {
18    which_parser: WhichParser,
19    pub(crate) includes: Vec<PathBuf>,
20    pub(crate) inputs: Vec<PathBuf>,
21    pub(crate) protoc: Option<PathBuf>,
22    pub(crate) protoc_extra_args: Vec<OsString>,
23    pub(crate) capture_stderr: bool,
24}
25
26impl Parser {
27    /// Create new default configured parser.
28    pub fn new() -> Parser {
29        Parser::default()
30    }
31
32    /// Use pure rust parser.
33    pub fn pure(&mut self) -> &mut Self {
34        self.which_parser = WhichParser::Pure;
35        self
36    }
37
38    /// Use `protoc` for parsing.
39    pub fn protoc(&mut self) -> &mut Self {
40        self.which_parser = WhichParser::Protoc;
41        self
42    }
43
44    /// Add an include directory.
45    pub fn include(&mut self, include: impl AsRef<Path>) -> &mut Self {
46        self.includes.push(include.as_ref().to_owned());
47        self
48    }
49
50    /// Add include directories.
51    pub fn includes(&mut self, includes: impl IntoIterator<Item = impl AsRef<Path>>) -> &mut Self {
52        for include in includes {
53            self.include(include);
54        }
55        self
56    }
57
58    /// Append a `.proto` file path to compile
59    pub fn input(&mut self, input: impl AsRef<Path>) -> &mut Self {
60        self.inputs.push(input.as_ref().to_owned());
61        self
62    }
63
64    /// Append multiple `.proto` file paths to compile
65    pub fn inputs(&mut self, inputs: impl IntoIterator<Item = impl AsRef<Path>>) -> &mut Self {
66        for input in inputs {
67            self.input(input);
68        }
69        self
70    }
71
72    /// Specify `protoc` path used for parsing.
73    ///
74    /// This is ignored if pure rust parser is used.
75    pub fn protoc_path(&mut self, protoc: &Path) -> &mut Self {
76        self.protoc = Some(protoc.to_owned());
77        self
78    }
79
80    /// Extra arguments to pass to `protoc` command (like experimental options).
81    ///
82    /// This is ignored if pure rust parser is used.
83    pub fn protoc_extra_args(
84        &mut self,
85        args: impl IntoIterator<Item = impl AsRef<OsStr>>,
86    ) -> &mut Self {
87        self.protoc_extra_args = args.into_iter().map(|s| s.as_ref().to_owned()).collect();
88        self
89    }
90
91    /// Capture stderr and return it in error.
92    ///
93    /// This option applies only to `protoc` parser.
94    /// By default `protoc` stderr is inherited from this process stderr.
95    pub fn capture_stderr(&mut self) -> &mut Self {
96        self.capture_stderr = true;
97        self
98    }
99
100    /// Parse `.proto` files and typecheck them using pure Rust parser of `protoc` command.
101    pub fn parse_and_typecheck(&self) -> anyhow::Result<ParsedAndTypechecked> {
102        match &self.which_parser {
103            WhichParser::Pure => {
104                pure::parse_and_typecheck::parse_and_typecheck(&self).context("using pure parser")
105            }
106            WhichParser::Protoc => protoc::parse_and_typecheck::parse_and_typecheck(&self)
107                .context("using protoc parser"),
108        }
109    }
110
111    /// Parse and convert result to `FileDescriptorSet`.
112    pub fn file_descriptor_set(&self) -> anyhow::Result<FileDescriptorSet> {
113        let mut generated = self.parse_and_typecheck()?;
114        let relative_paths: HashSet<_> = generated
115            .relative_paths
116            .iter()
117            .map(|path| path.to_string())
118            .collect();
119        generated
120            .file_descriptors
121            .retain(|fd| relative_paths.contains(fd.name()));
122        let mut fds = FileDescriptorSet::new();
123        fds.file = generated.file_descriptors;
124        Ok(fds)
125    }
126}