1 use std::collections::HashSet; 2 use std::ffi::OsStr; 3 use std::ffi::OsString; 4 use std::path::Path; 5 use std::path::PathBuf; 6 7 use anyhow::Context; 8 use protobuf::descriptor::FileDescriptorSet; 9 10 use crate::protoc; 11 use crate::pure; 12 use crate::which_parser::WhichParser; 13 use crate::ParsedAndTypechecked; 14 15 /// Configure and invoke `.proto` parser. 16 #[derive(Default, Debug)] 17 pub 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 26 impl Parser { 27 /// Create new default configured parser. new() -> Parser28 pub fn new() -> Parser { 29 Parser::default() 30 } 31 32 /// Use pure rust parser. pure(&mut self) -> &mut Self33 pub fn pure(&mut self) -> &mut Self { 34 self.which_parser = WhichParser::Pure; 35 self 36 } 37 38 /// Use `protoc` for parsing. protoc(&mut self) -> &mut Self39 pub fn protoc(&mut self) -> &mut Self { 40 self.which_parser = WhichParser::Protoc; 41 self 42 } 43 44 /// Add an include directory. include(&mut self, include: impl AsRef<Path>) -> &mut Self45 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. includes(&mut self, includes: impl IntoIterator<Item = impl AsRef<Path>>) -> &mut Self51 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 input(&mut self, input: impl AsRef<Path>) -> &mut Self59 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 inputs(&mut self, inputs: impl IntoIterator<Item = impl AsRef<Path>>) -> &mut Self65 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. protoc_path(&mut self, protoc: &Path) -> &mut Self75 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. protoc_extra_args( &mut self, args: impl IntoIterator<Item = impl AsRef<OsStr>>, ) -> &mut Self83 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. capture_stderr(&mut self) -> &mut Self95 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. parse_and_typecheck(&self) -> anyhow::Result<ParsedAndTypechecked>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`. file_descriptor_set(&self) -> anyhow::Result<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 } 127