protobuf_codegen/gen/
file.rs

1use std::collections::HashMap;
2
3use protobuf::descriptor::file_options;
4use protobuf::descriptor::FileDescriptorProto;
5use protobuf::reflect::FileDescriptor;
6use protobuf_parse::ProtoPath;
7
8use crate::compiler_plugin;
9use crate::customize::ctx::CustomizeElemCtx;
10use crate::customize::rustproto_proto::customize_from_rustproto_for_file;
11use crate::gen::code_writer::CodeWriter;
12use crate::gen::enums::EnumGen;
13use crate::gen::extensions::write_extensions;
14use crate::gen::file_descriptor::write_file_descriptor_data;
15use crate::gen::inside::protobuf_crate_path;
16use crate::gen::message::MessageGen;
17use crate::gen::paths::proto_path_to_rust_mod;
18use crate::gen::scope::FileScope;
19use crate::gen::scope::RootScope;
20use crate::proto_name_to_rs;
21
22pub(crate) struct GenFileResult {
23    pub(crate) compiler_plugin_result: compiler_plugin::GenResult,
24    pub(crate) mod_name: String,
25}
26
27pub(crate) fn gen_file(
28    file_descriptor: &FileDescriptor,
29    _files_map: &HashMap<&ProtoPath, &FileDescriptor>,
30    root_scope: &RootScope,
31    parent_customize: &CustomizeElemCtx,
32    parser: &str,
33) -> anyhow::Result<GenFileResult> {
34    let lite_runtime_from_builtin_option = file_descriptor
35        .proto()
36        .options
37        .get_or_default()
38        .optimize_for()
39        == file_options::OptimizeMode::LITE_RUNTIME;
40
41    let mut customize_from_proto =
42        customize_from_rustproto_for_file(file_descriptor.proto().options.get_or_default());
43    if customize_from_proto.lite_runtime.is_none()
44        && parent_customize.for_elem.lite_runtime.is_none()
45    {
46        customize_from_proto.lite_runtime = Some(lite_runtime_from_builtin_option);
47    }
48
49    let customize = parent_customize.child(&customize_from_proto, file_descriptor);
50
51    let file_scope = FileScope { file_descriptor };
52    let scope = file_scope.to_scope();
53
54    let lite_runtime = customize.for_elem.lite_runtime.unwrap_or(false);
55
56    let v = CodeWriter::with(|w| {
57        w.write_generated_by("rust-protobuf", env!("CARGO_PKG_VERSION"), parser);
58
59        w.write_line("");
60        w.write_line(&format!(
61            "//! Generated file from `{}`",
62            file_descriptor.proto().name()
63        ));
64
65        if customize.for_elem.lite_runtime.unwrap_or(false) {
66            w.comment("Generated for lite runtime");
67        }
68
69        if customize.for_elem.inside_protobuf != Some(true) {
70            w.write_line("");
71            w.write_line("/// Generated files are compatible only with the same version");
72            w.write_line("/// of protobuf runtime.");
73            w.write_line(&format!(
74                "const _PROTOBUF_VERSION_CHECK: () = {}::{};",
75                protobuf_crate_path(&customize.for_elem),
76                protobuf::VERSION_IDENT
77            ));
78        }
79
80        static NESTED_TYPE_NUMBER: protobuf::rt::Lazy<i32> = protobuf::rt::Lazy::new();
81        let message_type_number = *NESTED_TYPE_NUMBER.get(|| {
82            protobuf::reflect::MessageDescriptor::for_type::<FileDescriptorProto>()
83                .field_by_name("message_type")
84                .expect("`message_type` must exist")
85                .proto()
86                .number()
87        });
88
89        let mut path = vec![message_type_number, 0];
90        for (id, message) in scope.messages().iter().enumerate() {
91            // ignore map entries, because they are not used in map fields
92            if !message.is_map() {
93                path[1] = id as i32;
94
95                w.write_line("");
96                MessageGen::new(
97                    file_descriptor,
98                    message,
99                    &root_scope,
100                    &customize,
101                    &path,
102                    file_descriptor.proto().source_code_info.as_ref(),
103                )?
104                .write(w)?;
105            }
106        }
107
108        static ENUM_TYPE_NUMBER: protobuf::rt::Lazy<i32> = protobuf::rt::Lazy::new();
109        let enum_type_number = *ENUM_TYPE_NUMBER.get(|| {
110            protobuf::reflect::MessageDescriptor::for_type::<FileDescriptorProto>()
111                .field_by_name("enum_type")
112                .expect("`enum_type` must exist")
113                .proto()
114                .number()
115        });
116
117        let mut path = vec![enum_type_number, 0];
118        for (id, enum_type) in scope.enums().iter().enumerate() {
119            path[1] = id as i32;
120
121            w.write_line("");
122            EnumGen::new(
123                enum_type,
124                &customize,
125                root_scope,
126                &path,
127                file_descriptor.proto().source_code_info.as_ref(),
128            )
129            .write(w);
130        }
131
132        write_extensions(file_descriptor, &root_scope, w, &customize);
133
134        if !lite_runtime {
135            w.write_line("");
136            write_file_descriptor_data(file_descriptor, &customize.for_elem, w);
137        }
138
139        Ok(())
140    })?;
141
142    Ok(GenFileResult {
143        compiler_plugin_result: compiler_plugin::GenResult {
144            name: proto_name_to_rs(file_descriptor.proto().name()),
145            content: v.into_bytes(),
146        },
147        mod_name: proto_path_to_rust_mod(file_descriptor.proto().name()).into_string(),
148    })
149}