1use std::{
2 io::Write,
3 ops::Deref,
4 path::{Path, PathBuf},
5};
6
7use ahash::{AHashMap, AHashSet};
8use dashmap::{DashMap, mapref::one::RefMut};
9use faststr::FastStr;
10use itertools::Itertools;
11use normpath::PathExt;
12use pkg_tree::PkgNode;
13use quote::quote;
14use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
15use traits::CodegenBackend;
16
17use self::workspace::Workspace;
18use crate::{
19 Context, Symbol,
20 db::RirDatabase,
21 dedup::def_id_equal,
22 fmt::fmt_file,
23 middle::{
24 self,
25 context::{Mode, tls::CUR_ITEM},
26 rir,
27 },
28 rir::{Item, NodeKind},
29 symbol::{DefId, EnumRepr, FileId, ModPath},
30 tags::protobuf::Deprecated,
31};
32
33pub(crate) mod pkg_tree;
34pub mod toml;
35pub(crate) mod traits;
36
37mod workspace;
38
39pub mod pb;
40pub mod thrift;
41
42#[derive(Clone)]
43pub struct Codegen<B> {
44 backend: B,
45}
46
47impl<B> Deref for Codegen<B>
48where
49 B: CodegenBackend,
50{
51 type Target = Context;
52
53 fn deref(&self) -> &Self::Target {
54 self.backend.cx()
55 }
56}
57
58impl<B> Codegen<B> {
59 pub fn new(backend: B) -> Self {
60 Codegen { backend }
61 }
62}
63
64#[derive(Clone, Copy)]
65pub enum CodegenKind {
66 Direct,
67 RePub,
68}
69
70#[derive(Clone, Copy)]
71pub struct CodegenItem {
72 def_id: DefId,
73 kind: CodegenKind,
74}
75
76impl From<DefId> for CodegenItem {
77 fn from(value: DefId) -> Self {
78 CodegenItem {
79 def_id: value,
80 kind: CodegenKind::Direct,
81 }
82 }
83}
84
85impl<B> Codegen<B>
86where
87 B: CodegenBackend + Send,
88{
89 fn is_deprecated(&self, def_id: DefId) -> bool {
90 self.node_tags(def_id)
91 .and_then(|tags| tags.get::<Deprecated>().map(|d| d.0))
92 .unwrap_or_default()
93 }
94
95 pub fn write_struct(&self, def_id: DefId, stream: &mut String, s: &rir::Message) {
96 let name = self.rust_name(def_id);
97
98 let mut fields = s
99 .fields
100 .iter()
101 .map(|f| {
102 let name = self.rust_name(f.did);
103 self.with_adjust(f.did, |adjust| {
104 let ty = self.codegen_item_ty(f.ty.kind.clone());
105 let mut ty = format!("{ty}");
106
107 if let Some(adjust) = adjust {
108 if adjust.boxed() {
109 ty = format!("::std::boxed::Box<{ty}>")
110 }
111 }
112
113 if f.is_optional() {
114 ty = format!("::std::option::Option<{ty}>")
115 }
116
117 let attrs = adjust.iter().flat_map(|a| a.attrs()).join("");
118
119 let deprecated_attr = if self.is_deprecated(f.did) {
120 "#[deprecated]\n"
121 } else {
122 ""
123 };
124
125 if self.config.with_comments {
126 let leading_comment = f.leading_comments.to_string();
127 let trailing_comment = f.trailing_comments.to_string();
128
129 format! {
130 r#"
131 {leading_comment}
132 {attrs}
133 {deprecated_attr}pub {name}: {ty},{trailing_comment}"#
134 }
135 } else {
136 format! {
137 r#"
138 {attrs}
139 {deprecated_attr}pub {name}: {ty},"#
140 }
141 }
142 })
143 })
144 .join("\n");
145
146 if self.cache.keep_unknown_fields.contains(&def_id) {
147 fields.push_str("\npub _unknown_fields: ::pilota::BytesVec,\n");
148 }
149
150 if !s.is_wrapper && self.config.with_field_mask {
151 fields.push_str(
152 "\npub _field_mask: ::std::option::Option<::pilota_thrift_fieldmask::FieldMask>,\n",
153 );
154 }
155
156 let deprecated_attr = if self.is_deprecated(def_id) {
157 "#[deprecated]\n"
158 } else {
159 ""
160 };
161
162 let trailing_comment = if self.config.with_comments {
163 s.trailing_comments.as_str()
164 } else {
165 ""
166 };
167
168 stream.push_str(&format! {
169 r#"#[derive(Clone, PartialEq)]
170 {deprecated_attr}pub struct {name} {{
171 {fields}
172 }}{trailing_comment}"#
173 });
174
175 self.backend.codegen_struct_impl(def_id, stream, s);
176 }
177
178 pub fn write_item(
179 &self,
180 stream: &mut String,
181 item: CodegenItem,
182 dup: &mut AHashMap<FastStr, Vec<DefId>>,
183 ) {
184 CUR_ITEM.set(&item.def_id, || match item.kind {
185 CodegenKind::Direct => {
186 if !self.duplicate(dup, item.def_id) {
187 let def_id = item.def_id;
188 let item = self.item(def_id).unwrap();
189 tracing::trace!("write item {}", item.symbol_name());
190
191 let comments = if self.config.with_comments {
193 match &*item {
194 middle::rir::Item::Message(s) => s.leading_comments.as_str(),
195 middle::rir::Item::Enum(e) => e.leading_comments.as_str(),
196 middle::rir::Item::Service(s) => s.leading_comments.as_str(),
197 middle::rir::Item::NewType(t) => t.leading_comments.as_str(),
198 middle::rir::Item::Const(c) => c.leading_comments.as_str(),
199 _ => "",
200 }
201 } else {
202 ""
203 };
204
205 if !comments.is_empty() {
206 stream.push_str(&format!("\n{comments}\n"));
207 }
208
209 self.with_adjust(def_id, |adjust| {
210 let attrs = adjust.iter().flat_map(|a| a.attrs()).join("\n");
211
212 let impls = adjust
213 .iter()
214 .flat_map(|a| &a.nested_items)
215 .sorted()
216 .join("\n");
217 stream.push_str(&impls);
218 stream.push_str(&attrs);
219 });
220
221 match &*item {
222 middle::rir::Item::Message(s) => {
223 self.write_struct(def_id, stream, s);
224 }
225 middle::rir::Item::Enum(e) => self.write_enum(def_id, stream, e),
226 middle::rir::Item::Service(s) => self.write_service(def_id, stream, s),
227 middle::rir::Item::NewType(t) => self.write_new_type(def_id, stream, t),
228 middle::rir::Item::Const(c) => self.write_const(def_id, stream, c),
229 middle::rir::Item::Mod(m) => {
230 let name = self.rust_name(def_id);
231 let mut inner = Default::default();
232 self.backend.codegen_pilota_trait(&mut inner);
233 m.items.iter().for_each(|def_id| {
234 self.write_item(&mut inner, (*def_id).into(), dup)
235 });
236
237 if self.config.with_descriptor && m.extensions.has_extendees() {
238 let cur_pkg = self.item_path(def_id);
239 self.backend.codegen_mod_exts(
240 &mut inner,
241 &name,
242 &cur_pkg,
243 &m.extensions,
244 );
245 }
246
247 let name = self.rust_name(def_id);
248 stream.push_str(&format! {
249 r#"pub mod {name} {{
250 {inner}
251 }}"#
252 });
253 }
254 }
255 };
256 }
257 CodegenKind::RePub => {
258 let path = self
259 .item_path(item.def_id)
260 .iter()
261 .map(|item| item.to_string())
262 .join("::");
263 stream.push_str(format!("pub use ::{path};").as_str());
264 }
265 })
266 }
267
268 fn duplicate(&self, dup: &mut AHashMap<FastStr, Vec<DefId>>, def_id: DefId) -> bool {
269 let name = self.rust_name(def_id);
270 if !self.cache.dedups.contains(&name.0) {
271 return false;
272 }
273 let dup = dup.entry(name.0).or_default();
274 for id in dup.iter() {
275 if def_id_equal(self.nodes(), *id, def_id) {
276 return true;
277 }
278 }
279 dup.push(def_id);
280 false
281 }
282
283 pub fn write_enum_as_new_type(
284 &self,
285 def_id: DefId,
286 stream: &mut String,
287 e: &middle::rir::Enum,
288 ) {
289 let name = self.rust_name(def_id);
290
291 let repr = match e.repr {
292 Some(EnumRepr::I32) => quote!(i32),
293 _ => panic!(),
294 };
295
296 let variants = e
297 .variants
298 .iter()
299 .map(|v| {
300 let name = self.rust_name(v.did);
301
302 let discr = v.discr.unwrap();
303 let discr = match e.repr {
304 Some(EnumRepr::I32) => discr as i32,
305 None => panic!(),
306 };
307
308 let deprecated_attr = if self.is_deprecated(v.did) {
309 "#[deprecated]\n"
310 } else {
311 ""
312 };
313
314 (
315 format!("{deprecated_attr}pub const {name}: Self = Self({discr});"),
316 format!("Self({discr}) => ::std::string::String::from(\"{name}\"),"),
317 )
318 })
319 .collect::<Vec<_>>();
320 let variants_const = variants.iter().map(|(v, _)| v).join("");
321 let variants_as_str_fields = variants.iter().map(|(_, v)| v).join("");
322 let try_from_arms = e
323 .variants
324 .iter()
325 .map(|v| {
326 let name = self.rust_name(v.did);
327 let discr = v.discr.unwrap();
328 let discr = match e.repr {
329 Some(EnumRepr::I32) => discr as i32,
330 None => panic!(),
331 };
332 format!("{discr} => Some(Self::{name}),")
333 })
334 .join("\n");
335
336 let deprecated_attr = if self.is_deprecated(def_id) {
337 "#[deprecated]\n"
338 } else {
339 ""
340 };
341
342 let impl_enum_message = if self.config.with_descriptor {
343 self.backend.codegen_impl_enum_message(&name)
344 } else {
345 Default::default()
346 };
347
348 stream.push_str(&format! {
349 r#"#[derive(Clone, PartialEq, Copy)]
350 #[repr(transparent)]
351 {deprecated_attr}pub struct {name}({repr});
352
353 impl {name} {{
354 {variants_const}
355
356 pub fn inner(&self) -> {repr} {{
357 self.0
358 }}
359
360 pub fn to_string(&self) -> ::std::string::String {{
361 match self {{
362 {variants_as_str_fields}
363 Self(val) => val.to_string(),
364 }}
365 }}
366
367 pub fn try_from_{repr}(value: {repr}) -> ::std::option::Option<Self> {{
368 match value {{
369 {try_from_arms}
370 _ => None,
371 }}
372 }}
373 }}
374
375 {impl_enum_message}
376
377 impl ::std::convert::From<{repr}> for {name} {{
378 fn from(value: {repr}) -> Self {{
379 Self(value)
380 }}
381 }}
382
383 impl ::std::convert::From<{name}> for {repr} {{
384 fn from(value: {name}) -> {repr} {{
385 value.0
386 }}
387 }}
388
389 "#
390 });
391
392 self.backend.codegen_enum_impl(def_id, stream, e);
393 }
394
395 pub fn write_enum(&self, def_id: DefId, stream: &mut String, e: &middle::rir::Enum) {
396 if e.repr.is_some() {
397 return self.write_enum_as_new_type(def_id, stream, e);
398 }
399 let name = self.rust_name(def_id);
400
401 let mut keep = true;
402 let mut variants = e
403 .variants
404 .iter()
405 .map(|v| {
406 let name = self.rust_name(v.did);
407
408 self.with_adjust(v.did, |adjust| {
409 let attrs = adjust.iter().flat_map(|a| a.attrs()).join("\n");
410
411 let fields = v
412 .fields
413 .iter()
414 .map(|ty| self.codegen_item_ty(ty.kind.clone()).to_string())
415 .join(",");
416
417 let fields_stream = if fields.is_empty() {
418 keep = false;
419 Default::default()
420 } else {
421 format!("({fields})")
422 };
423
424 let leading_comment = if self.config.with_comments {
425 v.leading_comments.as_str()
426 } else {
427 ""
428 };
429
430 format!(
431 r#"{leading_comment}
432 {attrs}
433 {name} {fields_stream},"#
434 )
435 })
436 })
437 .join("\n");
438
439 if self.cache.keep_unknown_fields.contains(&def_id) && keep {
440 variants.push_str("_UnknownFields(::pilota::BytesVec),");
441 }
442 let trailing_comment = if self.config.with_comments {
443 e.trailing_comments.as_str()
444 } else {
445 ""
446 };
447 stream.push_str(&format! {
448 r#"
449 #[derive(Clone, PartialEq)]
450 pub enum {name} {{
451 {variants}
452 }}{trailing_comment}
453 "#
454 });
455
456 self.backend.codegen_enum_impl(def_id, stream, e);
457 }
458
459 pub fn write_service(&self, def_id: DefId, stream: &mut String, s: &middle::rir::Service) {
460 let name = self.rust_name(def_id);
461 let methods = self.service_methods(def_id);
462
463 let methods = methods
464 .iter()
465 .map(|m| self.backend.codegen_service_method(def_id, m))
466 .join("\n");
467
468 let deprecated_attr = if self.is_deprecated(def_id) {
469 "#[deprecated]\n"
470 } else {
471 ""
472 };
473
474 stream.push_str(&format! {
475 r#"
476 {deprecated_attr}pub trait {name} {{
477 {methods}
478 }}
479 "#
480 });
481 self.backend.codegen_service_impl(def_id, stream, s);
482 }
483
484 pub fn get_init_service(&self, def_id: DefId) -> (String, String) {
487 CUR_ITEM.set(&def_id, || {
488 let service_name = self.rust_name(def_id).to_string();
489 let mod_prefix = self.mod_path(def_id);
490 let service_path = if mod_prefix.is_empty() {
491 service_name
492 } else {
493 let mod_path = mod_prefix.iter().map(|item| item.to_string()).join("::");
494 format!("{mod_path}::{service_name}")
495 };
496 tracing::debug!("service_path: {}", service_path);
497 let methods = self.service_methods(def_id);
498
499 let methods = methods
500 .iter()
501 .map(|m| {
502 self.backend
503 .codegen_service_method_with_global_path(def_id, m)
504 })
505 .join("\n");
506
507 (service_path, methods)
508 })
509 }
510
511 pub fn pick_init_service(&self, path: PathBuf) -> anyhow::Result<(String, String)> {
513 let path = path
515 .normalize()
516 .map_err(|e| {
517 anyhow::Error::msg(format!(
518 "Normalize path {} failed: {}, please check service path",
519 path.display(),
520 e
521 ))
522 })?
523 .into_path_buf();
524 tracing::debug!("path {:?}", path);
525 let file_id: FileId = self.file_id(path).unwrap();
526 let item = self
527 .cache
528 .codegen_items
529 .iter()
530 .copied()
531 .filter(|def_id| {
532 let item = self.item(*def_id).unwrap();
534 matches!(&*item, middle::rir::Item::Service(_))
535 })
536 .find(
537 |def_id| self.node(*def_id).unwrap().file_id == file_id,
539 );
540 match item {
541 Some(def_id) => Ok(self.get_init_service(def_id)),
542 None => Err(anyhow::anyhow!("No service found.")),
543 }
544 }
545
546 pub fn write_new_type(&self, def_id: DefId, stream: &mut String, t: &middle::rir::NewType) {
547 let name = self.rust_name(def_id);
548 let ty = self.codegen_item_ty(t.ty.kind.clone());
549 stream.push_str(&format! {
550 r#"
551 #[derive(Clone, PartialEq)]
552 pub struct {name}(pub {ty});
553
554 impl ::std::ops::Deref for {name} {{
555 type Target = {ty};
556
557 fn deref(&self) -> &Self::Target {{
558 &self.0
559 }}
560 }}
561
562 impl From<{ty}> for {name} {{
563 fn from(v: {ty}) -> Self {{
564 Self(v)
565 }}
566 }}
567
568 "#
569 });
570 self.backend.codegen_newtype_impl(def_id, stream, t);
571 }
572
573 pub fn write_const(&self, did: DefId, stream: &mut String, c: &middle::rir::Const) {
574 let mut ty = self.codegen_ty(did);
575
576 let name = self.rust_name(did);
577
578 if name.to_string().starts_with("__PILOTA_PB_EXT_") {
579 return;
580 }
581
582 stream.push_str(&self.def_lit(&name, &c.lit, &mut ty).unwrap())
583 }
584
585 pub fn write_workspace(self, base_dir: PathBuf) -> anyhow::Result<()> {
586 let ws = Workspace::new(base_dir, self);
587 ws.write_crates()
588 }
589
590 pub fn write_items(
591 &self,
592 stream: &mut String,
593 mod_items: AHashMap<ModPath, Vec<CodegenItem>>,
594 base_dir: &Path,
595 ) {
596 let mut mod_files = AHashMap::<ModPath, AHashSet<FileId>>::default();
598 let mut file_has_direct = AHashMap::default();
599
600 for (mod_path, items) in mod_items.iter() {
601 for item in items.iter() {
602 let file_id = self.node(item.def_id).unwrap().file_id;
603 let set = mod_files.entry(mod_path.clone()).or_default();
604 if !set.contains(&file_id) {
605 set.insert(file_id);
606 file_has_direct.insert(file_id, false);
607 }
608 if matches!(item.kind, CodegenKind::Direct) {
609 *file_has_direct.get_mut(&file_id).unwrap() = true;
610 }
611 }
612 }
613
614 if self.config.with_descriptor {
617 let mods_files_with_direct_items = mod_items
618 .keys()
619 .flat_map(|mod_path| {
620 mod_files
621 .get(mod_path)
622 .unwrap()
623 .iter()
624 .filter_map(|file_id| {
625 if *file_has_direct.get(file_id).unwrap()
626 && let Some(file_path) = self.file_paths().get(file_id)
627 {
628 Some((mod_path.clone(), file_path.clone()))
629 } else {
630 None
631 }
632 })
633 .collect::<Vec<_>>()
634 })
635 .collect::<Vec<_>>();
636 if !mods_files_with_direct_items.is_empty() {
637 self.backend
638 .codegen_register_mod_file_descriptor(stream, &mods_files_with_direct_items);
639 }
640 }
641
642 let mut pkgs: DashMap<ModPath, String> = Default::default();
644 let this = self.clone();
645 mod_items
646 .par_iter()
647 .for_each_with(this, |this, (mod_path, items)| {
648 let mut stream = pkgs.entry(mod_path.clone()).or_default();
649 for file_id in mod_files.get(mod_path).unwrap().iter() {
651 let file = this.file(*file_id).unwrap();
652 if !file.comments.is_empty() && this.config.with_comments {
654 stream.push_str(&format!("\n{}\n", file.comments));
655 }
656
657 if this.config.with_descriptor && *file_has_direct.get(file_id).unwrap() {
658 this.backend.codegen_file_descriptor_at_mod(
660 &mut stream,
661 &file,
662 mod_path,
663 *file_has_direct.get(file_id).unwrap(),
664 );
665
666 let file_pkg = file
668 .package
669 .iter()
670 .map(|s| s.0.clone())
671 .collect::<Vec<_>>()
672 .join("::");
673 if file_pkg == "google::protobuf" {
674 continue;
675 }
676 let mod_pkg = mod_path.iter().cloned().collect::<Vec<_>>().join("::");
677 if file_pkg == mod_pkg {
678 let filename_lower = this
679 .file_name(*file_id)
680 .unwrap()
681 .replace(".", "_")
682 .to_lowercase();
683 if file.extensions.has_extendees() {
684 this.backend.codegen_file_exts(
685 &mut stream,
686 &filename_lower,
687 &file.package,
688 &file.extensions,
689 );
690 }
691 }
692 }
693 }
694
695 let mut dup = AHashMap::default();
697
698 let span = tracing::span!(tracing::Level::TRACE, "write_mod", path = ?mod_path);
699 let _enter = span.enter();
700
701 if let Some(mod_idx) = this.cache.mod_idxes.get(mod_path) {
702 let mod_item = this.item(*mod_idx).unwrap();
703
704 if let middle::rir::Item::Mod(m) = &*mod_item {
705 if this.config.with_descriptor && m.extensions.has_extendees() {
706 let name = this.rust_name(*mod_idx);
707 let cur_pkg = this.item_path(*mod_idx).clone();
708 this.backend.codegen_mod_exts(
709 &mut stream,
710 &name,
711 &cur_pkg,
712 &m.extensions,
713 );
714 }
715 }
716 }
717
718 if this.config.split {
720 Self::write_split_mod(this, base_dir, mod_path, items, &mut stream, &mut dup);
721 } else {
722 for def_id in items.iter() {
723 this.write_item(&mut stream, *def_id, &mut dup)
724 }
725 }
726 });
727
728 let keys = pkgs.iter().map(|kv| kv.key().clone()).collect_vec();
729 let pkg_node = PkgNode::from_pkgs(&keys.iter().map(|s| &**s).collect_vec());
730 tracing::debug!(?pkg_node);
731
732 self.write_stream(&mut pkgs, stream, &pkg_node);
733 }
734
735 fn write_stream(
736 &self,
737 pkgs: &mut DashMap<ModPath, String>,
738 stream: &mut String,
739 nodes: &[PkgNode],
740 ) {
741 for node in nodes.iter().sorted_by_key(|x| &x.path) {
742 let mut inner_stream = String::default();
743 if let Some((_, node_stream)) = pkgs.remove(&node.path) {
744 inner_stream.push_str(&node_stream);
745 }
746
747 self.write_stream(pkgs, &mut inner_stream, &node.children);
748 let name = node.ident();
749 if name.clone().unwrap_or_default() == "" {
750 stream.push_str(&inner_stream);
751 return;
752 }
753
754 let name = Symbol::from(name.unwrap());
755 let mut pilota_buf_trait = Default::default();
756 self.backend.codegen_pilota_trait(&mut pilota_buf_trait);
757 stream.push_str(&format! {
758 r#"
759 pub mod {name} {{
760 {pilota_buf_trait}
761 {inner_stream}
762 }}
763 "#
764 });
765 }
766 }
767
768 fn write_split_mod(
769 this: &mut Codegen<B>,
770 base_dir: &Path,
771 mod_path: &ModPath,
772 def_ids: &[CodegenItem],
773 stream: &mut RefMut<ModPath, String>,
774 dup: &mut AHashMap<FastStr, Vec<DefId>>,
775 ) {
776 let base_mod_name = mod_path.iter().map(|s| s.to_string()).join("/");
777 let mod_file_name = format!("{base_mod_name}/mod.rs");
778 let mut mod_stream = String::new();
779
780 let mut existing_file_names: AHashSet<String> = AHashSet::new();
781
782 for def_id in def_ids.iter() {
783 let mut item_stream = String::new();
784 let node = this.db.node(def_id.def_id).unwrap();
785 let name_prefix = match node.kind {
786 NodeKind::Item(ref item) => match item.as_ref() {
787 Item::Message(_) => "message",
788 Item::Enum(_) => "enum",
789 Item::Service(_) => "service",
790 Item::NewType(_) => "new_type",
791 Item::Const(_) => "const",
792 Item::Mod(_) => "mod",
793 },
794 NodeKind::Variant(_) => "variant",
795 NodeKind::Field(_) => "field",
796 NodeKind::Method(_) => "method",
797 NodeKind::Arg(_) => "arg",
798 };
799
800 let mod_dir = base_dir.join(base_mod_name.clone());
801
802 let simple_name = format!("{}_{}", name_prefix, node.name());
803 let unique_name = Self::generate_unique_name(&existing_file_names, &simple_name);
804 existing_file_names.insert(unique_name.to_ascii_lowercase().clone());
805 let file_name = format!("{unique_name}.rs");
806 this.write_item(&mut item_stream, *def_id, dup);
807
808 let full_path = mod_dir.join(file_name.clone());
809 std::fs::create_dir_all(mod_dir).unwrap();
810
811 let item_stream = item_stream.lines().map(|s| s.trim_end()).join("\n");
812 let mut file =
813 std::io::BufWriter::new(std::fs::File::create(full_path.clone()).unwrap());
814 file.write_all(item_stream.as_bytes()).unwrap();
815 file.flush().unwrap();
816 fmt_file(full_path);
817
818 mod_stream.push_str(format!("include!(\"{file_name}\");\n").as_str());
819 }
820
821 let mod_path = base_dir.join(&mod_file_name);
822 let mod_stream = mod_stream.lines().map(|s| s.trim_end()).join("\n");
823 let mut mod_file = std::io::BufWriter::new(std::fs::File::create(&mod_path).unwrap());
824 mod_file.write_all(mod_stream.as_bytes()).unwrap();
825 mod_file.flush().unwrap();
826 fmt_file(&mod_path);
827
828 stream.push_str(format!("include!(\"{mod_file_name}\");\n").as_str());
829 }
830
831 fn generate_unique_name(existing_names: &AHashSet<String>, simple_name: &str) -> String {
837 let mut counter = 1;
838 let mut name = simple_name.to_string();
839 while existing_names.contains(name.to_ascii_lowercase().as_str()) {
840 counter += 1;
841 name = format!("{simple_name}_{counter}")
842 }
843 name
844 }
845
846 fn collect_direct_codegen_items(
847 &self,
848 mod_items: &AHashMap<ModPath, Vec<DefId>>,
849 ) -> AHashMap<ModPath, Vec<CodegenItem>> {
850 mod_items
851 .iter()
852 .map(|(mod_path, items)| {
853 (
854 mod_path.clone(),
855 items.iter().map(|def_id| (*def_id).into()).collect_vec(),
856 )
857 })
858 .collect::<AHashMap<_, _>>()
859 }
860
861 pub fn write_file(self, ns_name: Symbol, file_name: impl AsRef<Path>) {
862 let base_dir = file_name.as_ref().parent().unwrap();
863 let mut stream = String::default();
864 self.backend.codegen_pilota_trait(&mut stream);
865
866 let mod_items = self.collect_direct_codegen_items(&self.cache.mod_items);
867
868 self.write_items(&mut stream, mod_items, base_dir);
869
870 stream = format! {r#"pub mod {ns_name} {{
871 #![allow(warnings, clippy::all)]
872 {stream}
873 }}"#};
874 let stream = stream.lines().map(|s| s.trim_end()).join("\n");
875 let mut file = std::io::BufWriter::new(std::fs::File::create(&file_name).unwrap());
876 file.write_all(stream.as_bytes()).unwrap();
877 file.flush().unwrap();
878 fmt_file(file_name)
879 }
880
881 pub fn r#gen(self) -> anyhow::Result<()> {
882 match &*self.source.mode.clone() {
883 Mode::Workspace(info) => self.write_workspace(info.dir.clone()),
884 Mode::SingleFile { file_path: p } => {
885 self.write_file(
886 FastStr::new(
887 p.file_name()
888 .and_then(|s| s.to_str())
889 .and_then(|s| s.split('.').next())
890 .unwrap(),
891 )
892 .into(),
893 p,
894 );
895 Ok(())
896 }
897 }
898 }
899}