blob: cff1da22e7ea8216bad17ae12803fd8ce547860e [file] [log] [blame]
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -07001//! This crate implement protobuf codegen.
2//!
3//! This crate:
4//! * provides `protoc-gen-rust` plugin for `protoc` command
5//! * implement protobuf codegen
6//!
7//! This crate is not meant to be used directly, in fact, it does not provide any public API
8//! (except for `protoc-gen-rust` binary).
9//!
10//! Code can be generated with either:
11//! * `protoc-gen-rust` binary or
12//! * `protoc-rust` crate (codegen which depends on `protoc` binary for parsing)
13//! * `protobuf-codegen-pure` crate
14
Haibo Huangba676d32020-08-12 13:52:13 -070015#![deny(broken_intra_doc_links)]
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -070016#![deny(missing_docs)]
17
18extern crate protobuf;
19
20use std::collections::hash_map::HashMap;
21use std::fmt::Write as FmtWrite;
22use std::fs::File;
23use std::io;
24use std::io::Write;
25use std::path::Path;
26
27use protobuf::compiler_plugin;
28use protobuf::descriptor::*;
29use protobuf::Message;
30
31mod customize;
32mod enums;
33mod extensions;
34mod field;
35mod file;
36mod file_and_mod;
37mod file_descriptor;
38#[doc(hidden)]
39pub mod float;
40mod inside;
41mod message;
42mod oneof;
43mod protobuf_name;
44mod rust_name;
45mod rust_types_values;
46mod serde;
47mod well_known_types;
48
49pub(crate) mod rust;
50pub(crate) mod scope;
51pub(crate) mod strx;
52pub(crate) mod syntax;
53
54use customize::customize_from_rustproto_for_file;
55#[doc(hidden)]
56pub use customize::Customize;
57
58pub mod code_writer;
59
60use self::code_writer::CodeWriter;
61use self::enums::*;
62use self::extensions::*;
63use self::message::*;
64use file::proto_path_to_rust_mod;
65use inside::protobuf_crate_path;
66use scope::FileScope;
67use scope::RootScope;
68
Haibo Huangba676d32020-08-12 13:52:13 -070069#[doc(hidden)]
70pub use protobuf_name::ProtobufAbsolutePath;
71#[doc(hidden)]
72pub use protobuf_name::ProtobufIdent;
73#[doc(hidden)]
74pub use protobuf_name::ProtobufRelativePath;
75
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -070076fn escape_byte(s: &mut String, b: u8) {
77 if b == b'\n' {
78 write!(s, "\\n").unwrap();
79 } else if b == b'\r' {
80 write!(s, "\\r").unwrap();
81 } else if b == b'\t' {
82 write!(s, "\\t").unwrap();
83 } else if b == b'\\' || b == b'"' {
84 write!(s, "\\{}", b as char).unwrap();
85 } else if b == b'\0' {
86 write!(s, "\\0").unwrap();
87 // ASCII printable except space
88 } else if b > 0x20 && b < 0x7f {
89 write!(s, "{}", b as char).unwrap();
90 } else {
91 write!(s, "\\x{:02x}", b).unwrap();
92 }
93}
94
95fn write_file_descriptor_data(
96 file: &FileDescriptorProto,
97 customize: &Customize,
98 w: &mut CodeWriter,
99) {
100 let fdp_bytes = file.write_to_bytes().unwrap();
101 w.write_line("static file_descriptor_proto_data: &'static [u8] = b\"\\");
102 w.indented(|w| {
103 const MAX_LINE_LEN: usize = 72;
104
105 let mut s = String::new();
106 for &b in &fdp_bytes {
107 let prev_len = s.len();
108 escape_byte(&mut s, b);
109 let truncate = s.len() > MAX_LINE_LEN;
110 if truncate {
111 s.truncate(prev_len);
112 }
113 if truncate || s.len() == MAX_LINE_LEN {
114 write!(s, "\\").unwrap();
115 w.write_line(&s);
116 s.clear();
117 }
118 if truncate {
119 escape_byte(&mut s, b);
120 }
121 }
122 if !s.is_empty() {
123 write!(s, "\\").unwrap();
124 w.write_line(&s);
125 s.clear();
126 }
127 });
128 w.write_line("\";");
129 w.write_line("");
Haibo Huangba676d32020-08-12 13:52:13 -0700130 w.lazy_static(
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -0700131 "file_descriptor_proto_lazy",
132 &format!(
133 "{}::descriptor::FileDescriptorProto",
134 protobuf_crate_path(customize)
135 ),
Haibo Huangba676d32020-08-12 13:52:13 -0700136 customize,
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -0700137 );
138 w.write_line("");
139 w.def_fn(
Haibo Huangba676d32020-08-12 13:52:13 -0700140 &format!(
141 "parse_descriptor_proto() -> {}::descriptor::FileDescriptorProto",
142 protobuf_crate_path(customize)
143 ),
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -0700144 |w| {
Haibo Huangba676d32020-08-12 13:52:13 -0700145 w.write_line(&format!(
146 "{}::parse_from_bytes(file_descriptor_proto_data).unwrap()",
147 protobuf_crate_path(customize)
148 ));
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -0700149 },
150 );
151 w.write_line("");
152 w.pub_fn(
Haibo Huangba676d32020-08-12 13:52:13 -0700153 &format!(
154 "file_descriptor_proto() -> &'static {}::descriptor::FileDescriptorProto",
155 protobuf_crate_path(customize)
156 ),
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -0700157 |w| {
Haibo Huang72fec012020-07-10 20:24:04 -0700158 w.block("file_descriptor_proto_lazy.get(|| {", "})", |w| {
159 w.write_line("parse_descriptor_proto()");
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -0700160 });
161 },
162 );
163}
164
165fn gen_file(
166 file: &FileDescriptorProto,
167 _files_map: &HashMap<&str, &FileDescriptorProto>,
168 root_scope: &RootScope,
169 customize: &Customize,
170) -> Option<compiler_plugin::GenResult> {
171 // TODO: use it
172 let mut customize = customize.clone();
173 // options specified in invocation have precedence over options specified in file
174 customize.update_with(&customize_from_rustproto_for_file(file.get_options()));
175
176 let scope = FileScope {
177 file_descriptor: file,
178 }
179 .to_scope();
180 let lite_runtime = customize.lite_runtime.unwrap_or_else(|| {
181 file.get_options().get_optimize_for() == FileOptions_OptimizeMode::LITE_RUNTIME
182 });
183
184 let mut v = Vec::new();
185
186 {
187 let mut w = CodeWriter::new(&mut v);
188
Chih-Hung Hsieh0e4ce382020-09-30 20:42:07 -0700189 w.write_generated_by("rust-protobuf", "2.17.0"); // ANDROID ported version
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -0700190 w.write_line(&format!("//! Generated file from `{}`", file.get_name()));
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -0700191 if customize.inside_protobuf != Some(true) {
192 w.write_line("");
193 w.write_line("/// Generated files are compatible only with the same version");
194 w.write_line("/// of protobuf runtime.");
195 w.commented(|w| {
196 w.write_line(&format!(
197 "const _PROTOBUF_VERSION_CHECK: () = {}::{};",
198 protobuf_crate_path(&customize),
199 protobuf::VERSION_IDENT
200 ));
201 })
202 }
203
204 for message in &scope.get_messages() {
205 // ignore map entries, because they are not used in map fields
206 if message.map_entry().is_none() {
207 w.write_line("");
208 MessageGen::new(message, &root_scope, &customize).write(&mut w);
209 }
210 }
211 for enum_type in &scope.get_enums() {
212 w.write_line("");
213 EnumGen::new(enum_type, file, &customize, root_scope).write(&mut w);
214 }
215
216 write_extensions(file, &root_scope, &mut w, &customize);
217
218 if !lite_runtime {
219 w.write_line("");
220 write_file_descriptor_data(file, &customize, &mut w);
221 }
222 }
223
224 Some(compiler_plugin::GenResult {
225 name: format!("{}.rs", proto_path_to_rust_mod(file.get_name())),
226 content: v,
227 })
228}
229
230// This function is also used externally by cargo plugin
231// https://github.com/plietar/rust-protobuf-build
232// So be careful changing its signature.
233#[doc(hidden)]
234pub fn gen(
235 file_descriptors: &[FileDescriptorProto],
236 files_to_generate: &[String],
237 customize: &Customize,
238) -> Vec<compiler_plugin::GenResult> {
239 let root_scope = RootScope {
240 file_descriptors: file_descriptors,
241 };
242
243 let mut results: Vec<compiler_plugin::GenResult> = Vec::new();
244 let files_map: HashMap<&str, &FileDescriptorProto> =
245 file_descriptors.iter().map(|f| (f.get_name(), f)).collect();
246
247 let all_file_names: Vec<&str> = file_descriptors.iter().map(|f| f.get_name()).collect();
248
249 for file_name in files_to_generate {
250 let file = files_map.get(&file_name[..]).expect(&format!(
251 "file not found in file descriptors: {:?}, files: {:?}",
252 file_name, all_file_names
253 ));
254 results.extend(gen_file(file, &files_map, &root_scope, customize));
255 }
256 results
257}
258
259#[doc(hidden)]
260pub fn gen_and_write(
261 file_descriptors: &[FileDescriptorProto],
262 files_to_generate: &[String],
263 out_dir: &Path,
264 customize: &Customize,
265) -> io::Result<()> {
266 let results = gen(file_descriptors, files_to_generate, customize);
267
268 for r in &results {
269 let mut file_path = out_dir.to_owned();
270 file_path.push(&r.name);
271 let mut file_writer = File::create(&file_path)?;
272 file_writer.write_all(&r.content)?;
273 file_writer.flush()?;
274 }
275
276 Ok(())
277}
278
279#[doc(hidden)]
280pub fn protoc_gen_rust_main() {
281 compiler_plugin::plugin_main_2(|r| {
282 let customize = Customize::parse_from_parameter(r.parameter).expect("parse options");
283 gen(r.file_descriptors, r.files_to_generate, &customize)
284 });
285}
Haibo Huangba676d32020-08-12 13:52:13 -0700286
287/// Used in protobuf-codegen-identical-test
288#[doc(hidden)]
289pub fn proto_name_to_rs(name: &str) -> String {
290 format!("{}.rs", proto_path_to_rust_mod(name))
291}