use std::collections::hash_map::HashMap;
use std::fmt::Write;
use descriptor::*;
use core::Message;
use compiler_plugin;
use code_writer::CodeWriter;
use descriptorx::*;
mod message;
mod enums;
mod rust_types_values;
mod well_known_types;
mod field;
mod extensions;
use self::message::*;
use self::enums::*;
use self::extensions::*;
fn escape_byte(s: &mut String, b: u8) {
if b == b'\n' {
write!(s, "\\n").unwrap();
} else if b == b'\r' {
write!(s, "\\r").unwrap();
} else if b == b'\t' {
write!(s, "\\t").unwrap();
} else if b == b'\\' || b == b'"' {
write!(s, "\\{}", b as char).unwrap();
} else if b == b'\0' {
write!(s, "\\0").unwrap();
} else if b > 0x20 && b < 0x7f {
write!(s, "{}", b as char).unwrap();
} else {
write!(s, "\\x{:02x}", b).unwrap();
}
}
fn write_file_descriptor_data(file: &FileDescriptorProto, w: &mut CodeWriter) {
let fdp_bytes = file.write_to_bytes().unwrap();
w.write_line("static file_descriptor_proto_data: &'static [u8] = b\"\\");
w.indented(|w| {
const MAX_LINE_LEN: usize = 72;
let mut s = String::new();
for &b in &fdp_bytes {
let prev_len = s.len();
escape_byte(&mut s, b);
let truncate = s.len() > MAX_LINE_LEN;
if truncate {
s.truncate(prev_len);
}
if truncate || s.len() == MAX_LINE_LEN {
write!(s, "\\").unwrap();
w.write_line(&s);
s.clear();
}
if truncate {
escape_byte(&mut s, b);
}
}
if !s.is_empty() {
write!(s, "\\").unwrap();
w.write_line(&s);
s.clear();
}
});
w.write_line("\";");
w.write_line("");
w.lazy_static("file_descriptor_proto_lazy", "::protobuf::descriptor::FileDescriptorProto");
w.write_line("");
w.def_fn("parse_descriptor_proto() -> ::protobuf::descriptor::FileDescriptorProto", |w| {
w.write_line("::protobuf::parse_from_bytes(file_descriptor_proto_data).unwrap()");
});
w.write_line("");
w.pub_fn("file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto", |w| {
w.unsafe_expr(|w| {
w.block("file_descriptor_proto_lazy.get(|| {", "})", |w| {
w.write_line("parse_descriptor_proto()");
});
});
});
}
fn gen_file(
file: &FileDescriptorProto,
_files_map: &HashMap<&str, &FileDescriptorProto>,
root_scope: &RootScope,
)
-> Option<compiler_plugin::GenResult>
{
let scope = FileScope { file_descriptor: file } .to_scope();
if scope.get_messages().is_empty()
&& scope.get_enums().is_empty()
&& file.get_extension().is_empty()
{
return None;
}
let mut v = Vec::new();
{
let mut w = CodeWriter::new(&mut v);
w.write_generated();
w.write_line("");
w.write_line("use protobuf::Message as Message_imported_for_functions;");
w.write_line("use protobuf::ProtobufEnum as ProtobufEnum_imported_for_functions;");
for message in &scope.get_messages() {
if message.map_entry().is_none() {
w.write_line("");
MessageGen::new(message, &root_scope).write(&mut w);
}
}
for enum_type in &scope.get_enums() {
w.write_line("");
EnumGen::new(enum_type, file).write(&mut w);
}
write_extensions(file, &root_scope, &mut w);
if file.get_options().get_optimize_for() != FileOptions_OptimizeMode::LITE_RUNTIME {
w.write_line("");
write_file_descriptor_data(file, &mut w);
}
}
Some(compiler_plugin::GenResult {
name: format!("{}.rs", proto_path_to_rust_mod(file.get_name())),
content: v,
})
}
pub fn gen(file_descriptors: &[FileDescriptorProto], files_to_generate: &[String])
-> Vec<compiler_plugin::GenResult>
{
let root_scope = RootScope { file_descriptors: file_descriptors };
let mut results: Vec<compiler_plugin::GenResult> = Vec::new();
let files_map: HashMap<&str, &FileDescriptorProto> =
file_descriptors.iter().map(|f| (f.get_name(), f)).collect();
let all_file_names: Vec<&str> = file_descriptors.iter().map(|f| f.get_name()).collect();
for file_name in files_to_generate {
let file = files_map.get(&file_name[..])
.expect(&format!(
"file not found in file descriptors: {:?}, files: {:?}", file_name, all_file_names));
results.extend(gen_file(file, &files_map, &root_scope));
}
results
}
pub fn protoc_gen_rust_main() {
compiler_plugin::plugin_main(gen);
}