1#![deny(rustdoc::broken_intra_doc_links)]
79#![deny(missing_docs)]
80
81extern crate protobuf;
82
83use std::collections::hash_map::HashMap;
84use std::fmt::Write as FmtWrite;
85use std::fs::File;
86use std::io;
87use std::io::Write;
88use std::path::Path;
89
90use protobuf::compiler_plugin;
91use protobuf::descriptor::*;
92use protobuf::Message;
93
94mod customize;
95mod enums;
96mod extensions;
97mod field;
98mod file;
99mod file_and_mod;
100mod file_descriptor;
101#[doc(hidden)]
102pub mod float;
103mod inside;
104mod message;
105mod oneof;
106mod protobuf_name;
107mod rust_name;
108mod rust_types_values;
109mod serde;
110mod well_known_types;
111
112pub(crate) mod rust;
113pub(crate) mod scope;
114pub(crate) mod strx;
115pub(crate) mod syntax;
116
117use customize::customize_from_rustproto_for_file;
118#[doc(hidden)]
119pub use customize::Customize;
120
121pub mod code_writer;
122
123use inside::protobuf_crate_path;
124#[doc(hidden)]
125pub use protobuf_name::ProtobufAbsolutePath;
126#[doc(hidden)]
127pub use protobuf_name::ProtobufIdent;
128#[doc(hidden)]
129pub use protobuf_name::ProtobufRelativePath;
130use scope::FileScope;
131use scope::RootScope;
132
133use self::code_writer::CodeWriter;
134use self::enums::*;
135use self::extensions::*;
136use self::message::*;
137use crate::file::proto_path_to_rust_mod;
138
139fn escape_byte(s: &mut String, b: u8) {
140 if b == b'\n' {
141 write!(s, "\\n").unwrap();
142 } else if b == b'\r' {
143 write!(s, "\\r").unwrap();
144 } else if b == b'\t' {
145 write!(s, "\\t").unwrap();
146 } else if b == b'\\' || b == b'"' {
147 write!(s, "\\{}", b as char).unwrap();
148 } else if b == b'\0' {
149 write!(s, "\\0").unwrap();
150 } else if b > 0x20 && b < 0x7f {
152 write!(s, "{}", b as char).unwrap();
153 } else {
154 write!(s, "\\x{:02x}", b).unwrap();
155 }
156}
157
158fn write_file_descriptor_data(
159 file: &FileDescriptorProto,
160 customize: &Customize,
161 w: &mut CodeWriter,
162) {
163 let fdp_bytes = file.write_to_bytes().unwrap();
164 w.write_line("static file_descriptor_proto_data: &'static [u8] = b\"\\");
165 w.indented(|w| {
166 const MAX_LINE_LEN: usize = 72;
167
168 let mut s = String::new();
169 for &b in &fdp_bytes {
170 let prev_len = s.len();
171 escape_byte(&mut s, b);
172 let truncate = s.len() > MAX_LINE_LEN;
173 if truncate {
174 s.truncate(prev_len);
175 }
176 if truncate || s.len() == MAX_LINE_LEN {
177 write!(s, "\\").unwrap();
178 w.write_line(&s);
179 s.clear();
180 }
181 if truncate {
182 escape_byte(&mut s, b);
183 }
184 }
185 if !s.is_empty() {
186 write!(s, "\\").unwrap();
187 w.write_line(&s);
188 s.clear();
189 }
190 });
191 w.write_line("\";");
192 w.write_line("");
193 w.lazy_static(
194 "file_descriptor_proto_lazy",
195 &format!(
196 "{}::descriptor::FileDescriptorProto",
197 protobuf_crate_path(customize)
198 ),
199 customize,
200 );
201 w.write_line("");
202 w.def_fn(
203 &format!(
204 "parse_descriptor_proto() -> {}::descriptor::FileDescriptorProto",
205 protobuf_crate_path(customize)
206 ),
207 |w| {
208 w.write_line(&format!(
209 "{}::Message::parse_from_bytes(file_descriptor_proto_data).unwrap()",
210 protobuf_crate_path(customize)
211 ));
212 },
213 );
214 w.write_line("");
215 w.pub_fn(
216 &format!(
217 "file_descriptor_proto() -> &'static {}::descriptor::FileDescriptorProto",
218 protobuf_crate_path(customize)
219 ),
220 |w| {
221 w.block("file_descriptor_proto_lazy.get(|| {", "})", |w| {
222 w.write_line("parse_descriptor_proto()");
223 });
224 },
225 );
226}
227
228struct GenFileResult {
229 compiler_plugin_result: compiler_plugin::GenResult,
230 mod_name: String,
231}
232
233fn gen_file(
234 file: &FileDescriptorProto,
235 _files_map: &HashMap<&str, &FileDescriptorProto>,
236 root_scope: &RootScope,
237 customize: &Customize,
238) -> GenFileResult {
239 let mut customize = customize.clone();
241 customize.update_with(&customize_from_rustproto_for_file(file.get_options()));
243
244 let scope = FileScope {
245 file_descriptor: file,
246 }
247 .to_scope();
248 let lite_runtime = customize.lite_runtime.unwrap_or_else(|| {
249 file.get_options().get_optimize_for() == FileOptions_OptimizeMode::LITE_RUNTIME
250 });
251
252 let mut v = Vec::new();
253
254 {
255 let mut w = CodeWriter::new(&mut v);
256
257 w.write_generated_by("rust-protobuf", env!("CARGO_PKG_VERSION"));
258 w.write_line(&format!("//! Generated file from `{}`", file.get_name()));
259 if customize.inside_protobuf != Some(true) {
260 w.write_line("");
261 w.write_line("/// Generated files are compatible only with the same version");
262 w.write_line("/// of protobuf runtime.");
263 w.commented(|w| {
264 w.write_line(&format!(
265 "const _PROTOBUF_VERSION_CHECK: () = {}::{};",
266 protobuf_crate_path(&customize),
267 protobuf::VERSION_IDENT
268 ));
269 })
270 }
271
272 for message in &scope.get_messages() {
273 if message.map_entry().is_none() {
275 w.write_line("");
276 MessageGen::new(message, &root_scope, &customize).write(&mut w);
277 }
278 }
279 for enum_type in &scope.get_enums() {
280 w.write_line("");
281 EnumGen::new(enum_type, file, &customize, root_scope).write(&mut w);
282 }
283
284 write_extensions(file, &root_scope, &mut w, &customize);
285
286 if !lite_runtime {
287 w.write_line("");
288 write_file_descriptor_data(file, &customize, &mut w);
289 }
290 }
291
292 GenFileResult {
293 compiler_plugin_result: compiler_plugin::GenResult {
294 name: format!("{}.rs", proto_path_to_rust_mod(file.get_name())),
295 content: v,
296 },
297 mod_name: proto_path_to_rust_mod(file.get_name()).into_string(),
298 }
299}
300
301fn gen_mod_rs(mods: &[String]) -> compiler_plugin::GenResult {
302 let mut v = Vec::new();
303 let mut w = CodeWriter::new(&mut v);
304 w.comment("@generated");
305 w.write_line("");
306 for m in mods {
307 w.write_line(&format!("pub mod {};", m));
308 }
309 drop(w);
310 compiler_plugin::GenResult {
311 name: "mod.rs".to_owned(),
312 content: v,
313 }
314}
315
316#[doc(hidden)]
320pub fn gen(
321 file_descriptors: &[FileDescriptorProto],
322 files_to_generate: &[String],
323 customize: &Customize,
324) -> Vec<compiler_plugin::GenResult> {
325 let root_scope = RootScope {
326 file_descriptors: file_descriptors,
327 };
328
329 let mut results: Vec<compiler_plugin::GenResult> = Vec::new();
330 let files_map: HashMap<&str, &FileDescriptorProto> =
331 file_descriptors.iter().map(|f| (f.get_name(), f)).collect();
332
333 let all_file_names: Vec<&str> = file_descriptors.iter().map(|f| f.get_name()).collect();
334
335 let mut mods = Vec::new();
336
337 for file_name in files_to_generate {
338 let file = files_map.get(&file_name[..]).expect(&format!(
339 "file not found in file descriptors: {:?}, files: {:?}",
340 file_name, all_file_names
341 ));
342
343 let gen_file_result = gen_file(file, &files_map, &root_scope, customize);
344 results.push(gen_file_result.compiler_plugin_result);
345 mods.push(gen_file_result.mod_name);
346 }
347
348 if customize.gen_mod_rs.unwrap_or(false) {
349 results.push(gen_mod_rs(&mods));
350 }
351
352 results
353}
354
355#[doc(hidden)]
356pub fn gen_and_write(
357 file_descriptors: &[FileDescriptorProto],
358 files_to_generate: &[String],
359 out_dir: &Path,
360 customize: &Customize,
361) -> io::Result<()> {
362 let results = gen(file_descriptors, files_to_generate, customize);
363
364 for r in &results {
365 let mut file_path = out_dir.to_owned();
366 file_path.push(&r.name);
367 let mut file_writer = File::create(&file_path)?;
368 file_writer.write_all(&r.content)?;
369 file_writer.flush()?;
370 }
371
372 Ok(())
373}
374
375#[doc(hidden)]
376pub fn protoc_gen_rust_main() {
377 compiler_plugin::plugin_main_2(|r| {
378 let customize = Customize::parse_from_parameter(r.parameter).expect("parse options");
379 gen(r.file_descriptors, r.files_to_generate, &customize)
380 });
381}
382
383#[doc(hidden)]
385pub fn proto_name_to_rs(name: &str) -> String {
386 format!("{}.rs", proto_path_to_rust_mod(name))
387}