1 use std::fmt::Write as _;
2 
3 use protobuf::reflect::FileDescriptor;
4 use protobuf::Message;
5 
6 use crate::gen::code_writer::CodeWriter;
7 use crate::gen::inside::protobuf_crate_path;
8 use crate::gen::paths::proto_path_to_fn_file_descriptor;
9 use crate::gen::rust::snippets::expr_vec_with_capacity_const;
10 use crate::gen::scope::FileScope;
11 use crate::gen::scope::Scope;
12 use crate::gen::scope::WithScope;
13 use crate::Customize;
14 
escape_byte(s: &mut String, b: u8)15 fn escape_byte(s: &mut String, b: u8) {
16     if b == b'\n' {
17         write!(s, "\\n").unwrap();
18     } else if b == b'\r' {
19         write!(s, "\\r").unwrap();
20     } else if b == b'\t' {
21         write!(s, "\\t").unwrap();
22     } else if b == b'\\' || b == b'"' {
23         write!(s, "\\{}", b as char).unwrap();
24     } else if b == b'\0' {
25         write!(s, "\\0").unwrap();
26     // ASCII printable except space
27     } else if b > 0x20 && b < 0x7f {
28         write!(s, "{}", b as char).unwrap();
29     } else {
30         write!(s, "\\x{:02x}", b).unwrap();
31     }
32 }
33 
write_generate_file_descriptor( file_descriptor: &FileDescriptor, customize: &Customize, w: &mut CodeWriter, )34 fn write_generate_file_descriptor(
35     file_descriptor: &FileDescriptor,
36     customize: &Customize,
37     w: &mut CodeWriter,
38 ) {
39     let deps = &file_descriptor.proto().dependency;
40     w.write_line(&format!(
41         "let mut deps = {vec_with_capacity};",
42         vec_with_capacity = expr_vec_with_capacity_const(deps.len())
43     ));
44     for f in deps {
45         w.write_line(&format!(
46             "deps.push({}().clone());",
47             proto_path_to_fn_file_descriptor(f, customize)
48         ));
49     }
50 
51     let scope = FileScope { file_descriptor };
52 
53     let messages = scope.find_messages_except_map();
54     w.write_line(&format!(
55         "let mut messages = {vec_with_capacity};",
56         vec_with_capacity = expr_vec_with_capacity_const(messages.len())
57     ));
58     for m in &messages {
59         w.write_line(&format!(
60             "messages.push({}::generated_message_descriptor_data());",
61             m.rust_name_to_file(),
62         ));
63     }
64 
65     let enums = scope.find_enums();
66     w.write_line(&format!(
67         "let mut enums = {};",
68         expr_vec_with_capacity_const(enums.len())
69     ));
70     for e in &enums {
71         w.write_line(&format!(
72             "enums.push({}::generated_enum_descriptor_data());",
73             e.rust_name_to_file(),
74         ));
75     }
76 
77     w.write_line(&format!(
78         "{}::reflect::GeneratedFileDescriptor::new_generated(",
79         protobuf_crate_path(&customize),
80     ));
81     w.indented(|w| {
82         w.write_line(&format!("file_descriptor_proto(),"));
83         w.write_line(&format!("deps,"));
84         w.write_line(&format!("messages,"));
85         w.write_line(&format!("enums,"));
86     });
87     w.write_line(")");
88 }
89 
write_file_descriptor( file_descriptor: &FileDescriptor, customize: &Customize, w: &mut CodeWriter, )90 fn write_file_descriptor(
91     file_descriptor: &FileDescriptor,
92     customize: &Customize,
93     w: &mut CodeWriter,
94 ) {
95     w.write_line("/// `FileDescriptor` object which allows dynamic access to files");
96     w.pub_fn(
97         &format!(
98             "file_descriptor() -> &'static {protobuf_crate}::reflect::FileDescriptor",
99             protobuf_crate = protobuf_crate_path(customize)
100         ),
101         |w| {
102             w.lazy_static(
103                 "generated_file_descriptor_lazy",
104                 &format!(
105                     "{protobuf_crate}::reflect::GeneratedFileDescriptor",
106                     protobuf_crate = protobuf_crate_path(customize)
107                 ),
108                 &format!("{}", protobuf_crate_path(customize)),
109             );
110             w.lazy_static_decl_get(
111                 "file_descriptor",
112                 &format!(
113                     "{protobuf_crate}::reflect::FileDescriptor",
114                     protobuf_crate = protobuf_crate_path(customize)
115                 ),
116                 &format!("{}", protobuf_crate_path(customize)),
117                 |w| {
118                     w.block(
119                         "let generated_file_descriptor = generated_file_descriptor_lazy.get(|| {",
120                         "});",
121                         |w| write_generate_file_descriptor(file_descriptor, customize, w),
122                     );
123                     w.write_line(&format!(
124                         "{protobuf_crate}::reflect::FileDescriptor::new_generated_2(generated_file_descriptor)",
125                         protobuf_crate=protobuf_crate_path(&customize),
126                     ));
127                 }
128             );
129         },
130     );
131 }
132 
write_file_descriptor_data( file: &FileDescriptor, customize: &Customize, w: &mut CodeWriter, )133 pub(crate) fn write_file_descriptor_data(
134     file: &FileDescriptor,
135     customize: &Customize,
136     w: &mut CodeWriter,
137 ) {
138     let fdp_bytes = file.proto().write_to_bytes().unwrap();
139     w.write_line("static file_descriptor_proto_data: &'static [u8] = b\"\\");
140     w.indented(|w| {
141         const MAX_LINE_LEN: usize = 72;
142 
143         let mut s = String::new();
144         for &b in &fdp_bytes {
145             let prev_len = s.len();
146             escape_byte(&mut s, b);
147             let truncate = s.len() > MAX_LINE_LEN;
148             if truncate {
149                 s.truncate(prev_len);
150             }
151             if truncate || s.len() == MAX_LINE_LEN {
152                 write!(s, "\\").unwrap();
153                 w.write_line(&s);
154                 s.clear();
155             }
156             if truncate {
157                 escape_byte(&mut s, b);
158             }
159         }
160         if !s.is_empty() {
161             write!(s, "\\").unwrap();
162             w.write_line(&s);
163             s.clear();
164         }
165     });
166     w.write_line("\";");
167     w.write_line("");
168     write_file_descriptor_proto(&customize, w);
169     w.write_line("");
170     write_file_descriptor(file, &customize, w);
171 }
172 
write_file_descriptor_proto(customize: &Customize, w: &mut CodeWriter)173 fn write_file_descriptor_proto(customize: &Customize, w: &mut CodeWriter) {
174     w.write_line("/// `FileDescriptorProto` object which was a source for this generated file");
175     w.def_fn(
176         &format!(
177             "file_descriptor_proto() -> &'static {protobuf_crate}::descriptor::FileDescriptorProto",
178             protobuf_crate=protobuf_crate_path(customize)
179         ),
180         |w| {
181             w.lazy_static_decl_get(
182                 "file_descriptor_proto_lazy",
183                 &format!(
184                     "{protobuf_crate}::descriptor::FileDescriptorProto",
185                     protobuf_crate=protobuf_crate_path(customize)
186                 ),
187                 &format!("{}", protobuf_crate_path(customize)),
188                 |w| {
189                     w.write_line(&format!(
190                         "{protobuf_crate}::Message::parse_from_bytes(file_descriptor_proto_data).unwrap()",
191                         protobuf_crate=protobuf_crate_path(customize)
192                     ));
193                 },
194             );
195         },
196     );
197 }
198 
199 /// Code to generate call `module::file_descriptor()`.
file_descriptor_call_expr(scope: &Scope) -> String200 pub(crate) fn file_descriptor_call_expr(scope: &Scope) -> String {
201     format!(
202         "{}()",
203         scope
204             .rust_path_to_file()
205             .to_reverse()
206             .append_ident("file_descriptor".into())
207     )
208 }
209