1use std::{collections::HashMap, ops::Deref, path::PathBuf, sync::Arc};
2
3use ahash::{AHashMap, HashSet};
4use anyhow::Context as _;
5use dashmap::DashMap;
6use faststr::FastStr;
7use itertools::Itertools;
8use normpath::PathExt;
9use rustc_hash::{FxHashMap, FxHashSet};
10
11use self::tls::with_cur_item;
12use super::{
13 adjust::Adjust,
14 resolver::{DefaultPathResolver, PathResolver, WorkspacePathResolver},
15 rir::NodeKind,
16};
17use crate::{
18 Plugin,
19 db::{RirDatabase, RootDatabase},
20 rir::{self, Field, Item, ItemPath, Literal},
21 symbol::{DefId, FileId, IdentName, ModPath, SPECIAL_NAMINGS, Symbol},
22 tags::{TagId, Tags},
23 ty::{AdtDef, AdtKind, CodegenTy, Visitor},
24};
25
26#[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Clone)]
27pub struct CrateId {
28 pub(crate) main_file: FileId,
29}
30
31#[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Clone)]
32pub enum DefLocation {
33 Fixed(CrateId, ItemPath),
34 Dynamic,
35}
36
37pub enum CollectMode {
38 All,
39 OnlyUsed {
40 touches: Vec<(std::path::PathBuf, Vec<String>)>,
41 },
42}
43
44#[derive(Debug)]
45pub struct WorkspaceInfo {
46 pub dir: PathBuf,
47 pub(crate) location_map: FxHashMap<DefId, DefLocation>,
48}
49
50#[derive(Debug)]
51pub enum Mode {
52 Workspace(WorkspaceInfo),
53 SingleFile { file_path: std::path::PathBuf },
54}
55
56pub struct Context {
57 pub db: RootDatabase,
58 pub source: Source,
59 pub config: Config,
60 pub cache: Cache,
61}
62
63#[derive(Clone)]
64pub struct Source {
65 pub source_type: SourceType,
66 pub services: Arc<[crate::IdlService]>,
67 pub mode: Arc<Mode>,
68 pub path_resolver: Arc<dyn PathResolver>,
69}
70
71#[derive(Clone)]
72pub struct Config {
73 pub change_case: bool,
74 pub split: bool,
75 pub with_descriptor: bool,
76 pub with_field_mask: bool,
77 pub touch_all: bool,
78 pub common_crate_name: FastStr,
79 pub with_comments: bool,
80}
81
82#[derive(Clone)]
83pub struct Cache {
84 pub adjusts: Arc<DashMap<DefId, Adjust>>,
85 pub mod_idxes: AHashMap<ModPath, DefId>, pub codegen_items: Vec<DefId>,
87 pub mod_items: AHashMap<ModPath, Vec<DefId>>,
88 pub def_mod: HashMap<DefId, ModPath>,
89 pub mod_files: HashMap<ModPath, Vec<FileId>>,
90 pub keep_unknown_fields: Arc<FxHashSet<DefId>>,
91 pub location_map: Arc<FxHashMap<DefId, DefLocation>>,
92 pub entry_map: Arc<HashMap<DefLocation, Vec<(DefId, DefLocation)>>>,
93 pub plugin_gen: Arc<DashMap<DefLocation, String>>,
94 pub dedups: Vec<FastStr>,
95 pub names: FxHashMap<DefId, usize>,
96}
97
98impl Clone for Context {
99 fn clone(&self) -> Self {
100 Self {
101 db: self.db.clone(),
102 source: self.source.clone(),
103 config: self.config.clone(),
104 cache: self.cache.clone(),
105 }
106 }
107}
108
109pub(crate) struct ContextBuilder {
110 db: RootDatabase,
111 pub(crate) codegen_items: Vec<DefId>,
112 input_items: Vec<DefId>,
113 mode: Mode,
114 keep_unknown_fields: FxHashSet<DefId>,
115 pub location_map: FxHashMap<DefId, DefLocation>,
116 entry_map: HashMap<DefLocation, Vec<(DefId, DefLocation)>>,
117}
118
119impl ContextBuilder {
120 pub fn new(db: RootDatabase, mode: Mode, input_items: Vec<DefId>) -> Self {
121 ContextBuilder {
122 db,
123 mode,
124 input_items,
125 codegen_items: Default::default(),
126 keep_unknown_fields: Default::default(),
127 location_map: Default::default(),
128 entry_map: Default::default(),
129 }
130 }
131 pub(crate) fn collect(&mut self, mode: CollectMode) {
132 match mode {
133 CollectMode::All => {
134 let nodes = self.db.nodes();
135 self.codegen_items
136 .extend(nodes.iter().filter_map(|(k, v)| match &v.kind {
137 NodeKind::Item(i) => {
138 if !matches!(&**i, Item::Mod(_)) {
139 Some(k)
140 } else {
141 None
142 }
143 }
144 _ => None,
145 }));
146 }
147 CollectMode::OnlyUsed { touches } => {
148 let extra_def_ids = touches
149 .into_iter()
150 .flat_map(|s| {
151 let path = s.0.normalize().unwrap().into_path_buf();
152 let file_id = *self.db.file_ids_map().get(&path).unwrap();
153 s.1.into_iter()
154 .filter_map(|item_name| {
155 let def_id = self
156 .db
157 .files()
158 .get(&file_id)
159 .unwrap()
160 .items
161 .iter()
162 .find(|def_id| {
163 *self.db.item(**def_id).unwrap().symbol_name() == item_name
164 })
165 .cloned();
166 if let Some(def_id) = def_id {
167 Some(def_id)
168 } else {
169 println!(
170 "cargo:warning=item `{item_name}` of `{}` not exists",
171 path.display(),
172 );
173 None
174 }
175 })
176 .collect::<Vec<_>>()
177 })
178 .collect::<Vec<_>>();
179
180 self.input_items.extend(extra_def_ids);
181
182 let def_ids = self.collect_items(&self.input_items);
183 self.codegen_items.extend(def_ids.iter());
184 }
185 }
186 if matches!(self.mode, Mode::Workspace(_)) {
187 let location_map = self.workspace_collect_def_ids(&self.codegen_items);
188 self.location_map = location_map.clone();
189 self.entry_map = location_map
190 .clone()
191 .into_iter()
192 .into_group_map_by(|item| item.1.clone());
193 if let Mode::Workspace(info) = &mut self.mode {
194 info.location_map = location_map
195 }
196 }
197 }
198
199 pub(crate) fn collect_items(&self, input: &[DefId]) -> FxHashSet<DefId> {
200 struct PathCollector<'a> {
201 set: &'a mut FxHashSet<DefId>,
202 cx: &'a ContextBuilder,
203 file_ids: &'a mut FxHashSet<FileId>,
204 }
205
206 impl super::ty::Visitor for PathCollector<'_> {
207 fn visit_path(&mut self, path: &crate::rir::Path) {
208 collect(self.cx, path.did, self.set, self.file_ids)
209 }
210 }
211
212 fn collect(
213 cx: &ContextBuilder,
214 def_id: DefId,
215 set: &mut FxHashSet<DefId>,
216 file_ids: &mut FxHashSet<FileId>,
217 ) {
218 if set.contains(&def_id) {
219 return;
220 }
221
222 let node = cx.db.node(def_id).unwrap();
223
224 if !file_ids.contains(&node.file_id) {
225 file_ids.insert(node.file_id);
226
227 let file = cx.db.file(node.file_id).unwrap();
228 if file.extensions.has_used_options() {
229 file.extensions
230 .unwrap_as_pb()
231 .used_options
232 .0
233 .iter()
234 .for_each(|option| {
235 let extendee = cx.db.pb_ext(option).unwrap();
236 PathCollector { cx, set, file_ids }
237 .visit(&extendee.extendee_ty.item_ty);
238 });
239 }
240 }
241
242 match node.kind {
243 NodeKind::Item(_) => {}
244 _ => return collect(cx, node.parent.unwrap(), set, file_ids),
245 }
246
247 if !matches!(&*cx.db.item(def_id).unwrap(), rir::Item::Mod(_)) {
248 set.insert(def_id);
249 }
250
251 let node = cx.db.node(def_id).unwrap();
252 tracing::trace!("collecting {:?}", node.expect_item().symbol_name());
253
254 node.related_nodes
255 .iter()
256 .for_each(|def_id| collect(cx, *def_id, set, file_ids));
257
258 let item = node.expect_item();
259
260 match item {
261 rir::Item::Message(m) => {
262 m.fields.iter().for_each(|f| {
264 PathCollector { cx, set, file_ids }.visit(&f.ty);
265 if let Some(Literal::Path(p)) = &f.default {
266 PathCollector { cx, set, file_ids }.visit_path(p);
267 }
268 if f.item_exts.has_used_options() {
270 f.item_exts
271 .unwrap_as_pb()
272 .used_options
273 .0
274 .iter()
275 .for_each(|index| {
276 let extendee = cx.db.pb_ext(index).unwrap_or_else(|| {
277 panic!("extension:{:?} not found", index)
278 });
279 PathCollector { cx, set, file_ids }
280 .visit(&extendee.extendee_ty.item_ty);
281 });
282 }
283 });
284 if m.item_exts.has_used_options() {
286 m.item_exts
287 .unwrap_as_pb()
288 .used_options
289 .0
290 .iter()
291 .for_each(|index| {
292 let extendee = cx.db.pb_ext(index).unwrap();
293 PathCollector { cx, set, file_ids }
294 .visit(&extendee.extendee_ty.item_ty);
295 });
296 }
297 }
298 rir::Item::Enum(e) => {
299 e.variants.iter().for_each(|v| {
300 for ty in &v.fields {
301 PathCollector { cx, set, file_ids }.visit(ty);
302 }
303 if v.item_exts.has_used_options() {
304 v.item_exts
305 .unwrap_as_pb()
306 .used_options
307 .0
308 .iter()
309 .for_each(|index| {
310 let extendee = cx.db.pb_ext(index).unwrap();
311 PathCollector { cx, set, file_ids }
312 .visit(&extendee.extendee_ty.item_ty);
313 });
314 }
315 });
316 if e.item_exts.has_used_options() {
317 e.item_exts
318 .unwrap_as_pb()
319 .used_options
320 .0
321 .iter()
322 .for_each(|index| {
323 let extendee = cx.db.pb_ext(index).unwrap();
324 PathCollector { cx, set, file_ids }
325 .visit(&extendee.extendee_ty.item_ty);
326 });
327 }
328 }
329 rir::Item::Service(s) => {
330 s.extend
331 .iter()
332 .for_each(|p| collect(cx, p.did, set, file_ids));
333 s.methods.iter().for_each(|m| {
334 m.args
336 .iter()
337 .for_each(|f| PathCollector { cx, set, file_ids }.visit(&f.ty));
338 PathCollector { cx, set, file_ids }.visit(&m.ret);
340 if let Some(exceptions) = &m.exceptions {
342 PathCollector { cx, set, file_ids }.visit_path(exceptions);
343 }
344 if m.item_exts.has_used_options() {
346 m.item_exts
347 .unwrap_as_pb()
348 .used_options
349 .0
350 .iter()
351 .for_each(|index| {
352 let extendee = cx.db.pb_ext(index).unwrap();
353 PathCollector { cx, set, file_ids }
354 .visit(&extendee.extendee_ty.item_ty);
355 });
356 }
357 });
358
359 if s.item_exts.has_used_options() {
361 s.item_exts
362 .unwrap_as_pb()
363 .used_options
364 .0
365 .iter()
366 .for_each(|index| {
367 let extendee = cx.db.pb_ext(index).unwrap();
368 PathCollector { cx, set, file_ids }
369 .visit(&extendee.extendee_ty.item_ty);
370 });
371 }
372 }
373 rir::Item::NewType(n) => PathCollector { cx, set, file_ids }.visit(&n.ty),
374 rir::Item::Const(c) => {
375 PathCollector { cx, set, file_ids }.visit(&c.ty);
376 }
377 rir::Item::Mod(m) => {
378 m.items.iter().for_each(|i| collect(cx, *i, set, file_ids));
379 }
380 }
381 }
382 let mut set = FxHashSet::default();
383
384 let mut file_ids = FxHashSet::default();
385
386 input.iter().for_each(|def_id| {
387 collect(self, *def_id, &mut set, &mut file_ids);
388 });
389
390 self.db.nodes().iter().for_each(|(def_id, node)| {
391 if let NodeKind::Item(item) = &node.kind {
392 if let rir::Item::Const(_) = &**item {
393 collect(self, *def_id, &mut set, &mut file_ids);
394 }
395 }
396 });
397
398 set
399 }
400
401 pub(crate) fn workspace_collect_def_ids(
402 &self,
403 input: &[DefId],
404 ) -> FxHashMap<DefId, DefLocation> {
405 self.db.collect_def_ids(input, None)
406 }
407
408 pub(crate) fn keep(&mut self, keep_unknown_fields: Vec<PathBuf>) {
409 let mut file_ids = FxHashSet::default();
410 keep_unknown_fields.into_iter().for_each(|p| {
411 let path = p.normalize().unwrap().into_path_buf();
412 let file_id = {
413 let file_ids_map = self.db.file_ids_map();
414 *file_ids_map.get(&path).unwrap()
415 };
416 keep_files(self, &file_id, &mut file_ids);
417
418 fn keep_files(
419 cx: &mut ContextBuilder,
420 file_id: &FileId,
421 file_ids: &mut FxHashSet<FileId>,
422 ) {
423 if !file_ids.insert(*file_id) {
424 return;
425 }
426 let (uses, items_to_keep) = {
427 let files = cx.db.files();
428 let file = files.get(file_id).unwrap();
429 let uses = file.uses.clone();
430 let items_to_keep = file
431 .items
432 .iter()
433 .filter(|&&def_id| match cx.db.node(def_id) {
434 Some(rir::Node {
435 kind: rir::NodeKind::Item(_),
436 tags,
437 ..
438 }) => !matches!(
439 cx.db.tags_map().get(&tags).and_then(|tags| {
440 tags.get::<crate::tags::KeepUnknownFields>()
441 }),
442 Some(crate::tags::KeepUnknownFields(false))
443 ),
444 _ => true,
445 })
446 .cloned()
447 .collect::<Vec<_>>();
448 (uses, items_to_keep)
449 };
450
451 for f in &uses {
452 keep_files(cx, f, file_ids);
453 }
454
455 cx.keep_unknown_fields.extend(items_to_keep);
456 }
457 });
458 }
459
460 #[allow(clippy::too_many_arguments)]
461 pub(crate) fn build(
462 self,
463 services: Arc<[crate::IdlService]>,
464 source_type: SourceType,
465 change_case: bool,
466 dedups: Vec<FastStr>,
467 special_namings: Vec<FastStr>,
468 common_crate_name: FastStr,
469 split: bool,
470 with_descriptor: bool,
471 with_field_mask: bool,
472 touch_all: bool,
473 with_comments: bool,
474 ) -> Context {
475 let mode = Arc::new(self.mode);
476 SPECIAL_NAMINGS.get_or_init(|| special_namings);
477 let mut cx = Context {
478 db: self.db.clone(),
479 source: Source {
480 source_type,
481 services,
482 mode: mode.clone(),
483 path_resolver: match &*mode {
484 Mode::Workspace(_) => Arc::new(WorkspacePathResolver),
485 Mode::SingleFile { .. } => Arc::new(DefaultPathResolver),
486 },
487 },
488 config: Config {
489 change_case,
490 split,
491 with_descriptor,
492 with_field_mask,
493 touch_all,
494 common_crate_name,
495 with_comments,
496 },
497 cache: Cache {
498 adjusts: Default::default(),
499 codegen_items: self.codegen_items,
500 keep_unknown_fields: Arc::new(self.keep_unknown_fields),
501 location_map: Arc::new(self.location_map),
502 entry_map: Arc::new(self.entry_map),
503 plugin_gen: Default::default(),
504 dedups,
505 names: Default::default(),
506 mod_idxes: Default::default(),
507 mod_items: Default::default(),
508 mod_files: Default::default(),
509 def_mod: Default::default(),
510 },
511 };
512 let mut map: FxHashMap<(Vec<DefId>, String), Vec<DefId>> = FxHashMap::default();
513 let mut mod_idxes = AHashMap::default();
514 cx.nodes()
515 .iter()
516 .for_each(|(def_id, node)| match &node.kind {
517 NodeKind::Item(item) => {
518 if let crate::rir::Item::Mod(_) = &**item {
519 mod_idxes.insert(
520 ModPath::from(Arc::from_iter(
521 cx.mod_path(*def_id).iter().map(|s| s.0.clone()),
522 )),
523 *def_id,
524 );
525 return;
526 }
527 if let Mode::Workspace(_) = &*cx.source.mode {
528 if !cx.cache.location_map.contains_key(def_id) {
529 return;
530 }
531 }
532 let rust_name = cx.item_path(*def_id).join("::");
533 map.entry((vec![], rust_name)).or_default().push(*def_id);
534 }
535 _ => {
536 let mut item_def_ids = vec![];
537 let mut item_def_id = *def_id;
538 while !matches!(cx.node(item_def_id).unwrap().kind, NodeKind::Item(_)) {
539 item_def_id = cx.node(item_def_id).unwrap().parent.unwrap();
540 item_def_ids.push(item_def_id);
541 }
542 let rust_name = cx.rust_name(*def_id).to_string();
543 map.entry((item_def_ids, rust_name))
544 .or_default()
545 .push(*def_id);
546 }
547 });
548 cx.cache.names.extend(
549 map.into_iter()
550 .filter(|(_, v)| v.len() > 1)
551 .map(|(_, v)| v)
552 .flat_map(|v| v.into_iter().enumerate().map(|(i, def_id)| (def_id, i)))
553 .collect::<HashMap<DefId, usize>>(),
554 );
555 cx.cache.mod_idxes.extend(mod_idxes);
556
557 if matches!(&*cx.source.mode, Mode::SingleFile { .. }) {
558 let mut mod_files = HashMap::<ModPath, HashSet<FileId>>::default();
559 let mod_items = cx
560 .cache
561 .codegen_items
562 .clone()
563 .into_iter()
564 .into_group_map_by(|def_id| {
565 let file_id = cx.node(*def_id).unwrap().file_id;
566
567 let mod_path = cx.mod_index(*def_id);
568 let set = mod_files.entry(mod_path.clone()).or_default();
569 if !set.contains(&file_id) {
570 set.insert(file_id);
571 }
572 cx.cache.def_mod.insert(*def_id, mod_path.clone());
573 mod_path
574 });
575 cx.cache.mod_items.extend(AHashMap::from_iter(mod_items));
576 cx.cache.mod_files.extend(
577 mod_files
578 .into_iter()
579 .map(|(k, v)| (k, v.into_iter().collect())),
580 );
581 }
582 cx
583 }
584}
585
586impl Deref for Context {
587 type Target = RootDatabase;
588
589 fn deref(&self) -> &Self::Target {
590 &self.db
591 }
592}
593
594#[derive(Clone, Copy)]
595pub enum SourceType {
596 Thrift,
597 Protobuf,
598}
599
600impl Context {
601 pub fn config_data(&self) -> &Config {
602 &self.config
603 }
604
605 pub fn cache_data(&self) -> &Cache {
606 &self.cache
607 }
608
609 pub fn source_data(&self) -> &Source {
610 &self.source
611 }
612
613 pub fn with_adjust<T, F>(&self, def_id: DefId, f: F) -> T
614 where
615 F: FnOnce(Option<&Adjust>) -> T,
616 {
617 match self.cache.adjusts.get(&def_id) {
618 Some(adj) => f(Some(&*adj)),
619 None => f(None),
620 }
621 }
622
623 pub fn with_adjust_mut<T, F>(&self, def_id: DefId, f: F) -> T
624 where
625 F: FnOnce(&mut Adjust) -> T,
626 {
627 let adjust = &mut *self.cache.adjusts.entry(def_id).or_default();
628 f(adjust)
629 }
630
631 pub fn tags(&self, tags_id: TagId) -> Option<Arc<Tags>> {
632 self.db.tags_map().get(&tags_id).cloned()
633 }
634
635 pub fn node_tags(&self, def_id: DefId) -> Option<Arc<Tags>> {
636 let tags_id = self.node(def_id).unwrap().tags;
637 self.tags(tags_id)
638 }
639
640 pub fn contains_tag<T: 'static>(&self, tags_id: TagId) -> bool {
641 self.tags(tags_id)
642 .and_then(|tags| tags.contains::<T>().then_some(true))
643 .is_some()
644 }
645
646 pub fn node_contains_tag<T: 'static>(&self, def_id: DefId) -> bool {
647 self.node_tags(def_id)
648 .and_then(|tags| tags.contains::<T>().then_some(true))
649 .is_some()
650 }
651
652 pub fn symbol_name(&self, def_id: DefId) -> Symbol {
653 let item = self.item(def_id).unwrap();
654 item.symbol_name()
655 }
656
657 fn get_codegen_ty_for_path(&self, def_id: DefId) -> CodegenTy {
658 let node = self.node(def_id).unwrap();
659 match &node.kind {
660 NodeKind::Item(item) => match &**item {
661 Item::Const(c) => {
662 let ty = self.codegen_const_ty(c.ty.kind.clone());
663 if ty.should_lazy_static() {
664 CodegenTy::LazyStaticRef(Arc::new(ty))
665 } else {
666 ty
667 }
668 }
669 Item::Enum(_) => CodegenTy::Adt(AdtDef {
670 did: def_id,
671 kind: AdtKind::Enum,
672 }),
673 Item::NewType(t) => CodegenTy::Adt(AdtDef {
674 did: def_id,
675 kind: AdtKind::NewType(Arc::new(self.codegen_item_ty(t.ty.kind.clone()))),
676 }),
677 Item::Message(_) => CodegenTy::Adt(AdtDef {
678 did: def_id,
679 kind: AdtKind::Struct,
680 }),
681 _ => panic!("Unexpected item type for path: {:?}", item),
682 },
683 NodeKind::Variant(_v) => {
684 let parent_def_id = node.parent.unwrap();
686 CodegenTy::Adt(AdtDef {
687 did: parent_def_id,
688 kind: AdtKind::Enum,
689 })
690 }
691 NodeKind::Field(_) | NodeKind::Method(_) | NodeKind::Arg(_) => {
692 panic!("Unexpected node kind for path: {:?}", node.kind)
693 }
694 }
695 }
696
697 fn convert_element_expr(&self, expr: &str, from: &CodegenTy, to: &CodegenTy) -> String {
698 if from == to {
699 if self.is_copy_ty(from) {
700 expr.to_string()
701 } else {
702 format!("({expr}).clone()", expr = expr)
703 }
704 } else if let Some((converted, _)) =
705 self.convert_codegen_ty_expr(expr.to_string().into(), from, to, false)
706 {
707 converted.to_string()
708 } else {
709 expr.to_string()
710 }
711 }
712
713 fn convert_owned_element_expr(&self, expr: &str, from: &CodegenTy, to: &CodegenTy) -> String {
714 if from == to {
715 return expr.to_string();
716 }
717
718 match (from, to) {
719 (
720 CodegenTy::Adt(AdtDef {
721 kind: AdtKind::NewType(inner),
722 ..
723 }),
724 _,
725 ) => {
726 let inner_expr = format!("({expr}).0", expr = expr);
727 self.convert_owned_element_expr(&inner_expr, inner, to)
728 }
729 (
730 _,
731 CodegenTy::Adt(AdtDef {
732 kind: AdtKind::NewType(inner),
733 did,
734 }),
735 ) => {
736 let inner_expr = self.convert_owned_element_expr(expr, from, inner);
737 let ctor = self.cur_related_item_path(*did);
738 format!("{ctor}({inner_expr})")
739 }
740 _ => self
741 .convert_codegen_ty_expr(expr.to_string().into(), from, to, true)
742 .map(|(converted, _)| converted.to_string())
743 .unwrap_or_else(|| expr.to_string()),
744 }
745 }
746
747 fn is_copy_ty(&self, ty: &CodegenTy) -> bool {
748 matches!(
749 ty,
750 CodegenTy::Bool
751 | CodegenTy::I8
752 | CodegenTy::I16
753 | CodegenTy::I32
754 | CodegenTy::I64
755 | CodegenTy::UInt32
756 | CodegenTy::UInt64
757 | CodegenTy::U8
758 | CodegenTy::F32
759 | CodegenTy::F64
760 | CodegenTy::OrderedF64
761 )
762 }
763
764 pub fn default_val(&self, f: &Field) -> Option<(FastStr, bool )> {
765 f.default.as_ref().map(|d| {
766 let ty = self.codegen_item_ty(f.ty.kind.clone());
767 match self
768 .lit_as_rvalue(d, &ty)
769 .with_context(|| format!("calc the default value for field {}", f.name))
770 {
771 Ok(v) => v,
772 Err(err) => {
773 panic!("{err}")
774 }
775 }
776 })
777 }
778
779 fn map_literal_expr(
780 &self,
781 entries: &[(Literal, Literal)],
782 key_ty: &Arc<CodegenTy>,
783 value_ty: &Arc<CodegenTy>,
784 btree: bool,
785 ) -> anyhow::Result<FastStr> {
786 let key_ty = &**key_ty;
787 let value_ty = &**value_ty;
788 let len = entries.len();
789 let kvs = entries
790 .iter()
791 .map(|(k, v)| {
792 let (k_expr, _) = self.lit_into_ty(k, key_ty)?;
793 let (v_expr, _) = self.lit_into_ty(v, value_ty)?;
794 anyhow::Ok(format!("map.insert({k_expr}, {v_expr});"))
795 })
796 .try_collect::<_, Vec<_>, _>()?
797 .join("");
798 let new = if btree {
799 "::std::collections::BTreeMap::new()".to_string()
800 } else {
801 format!("::pilota::AHashMap::with_capacity({len})")
802 };
803 Ok(format! {r#"{{
804 let mut map = {new};
805 {kvs}
806 map
807 }}"#}
808 .into())
809 }
810
811 fn lit_as_rvalue(
812 &self,
813 lit: &Literal,
814 ty: &CodegenTy,
815 ) -> anyhow::Result<(FastStr, bool )> {
816 anyhow::Ok(match (lit, ty) {
817 (Literal::Map(m), CodegenTy::LazyStaticRef(map)) => match &**map {
818 CodegenTy::Map(k_ty, v_ty) => (self.map_literal_expr(m, k_ty, v_ty, false)?, false),
819 CodegenTy::BTreeMap(k_ty, v_ty) => {
820 (self.map_literal_expr(m, k_ty, v_ty, true)?, false)
821 }
822 _ => panic!("invalid map type {map:?}"),
823 },
824 (Literal::Map(m), CodegenTy::Map(k_ty, v_ty)) => {
825 (self.map_literal_expr(m, k_ty, v_ty, false)?, false)
826 }
827 (Literal::Map(m), CodegenTy::BTreeMap(k_ty, v_ty)) => {
828 (self.map_literal_expr(m, k_ty, v_ty, true)?, false)
829 }
830 (Literal::List(l), CodegenTy::LazyStaticRef(map)) => match &**map {
831 CodegenTy::Map(_, _) => {
832 assert!(l.is_empty());
833 ("::pilota::AHashMap::new()".into(), false)
834 }
835 CodegenTy::BTreeMap(_, _) => {
836 assert!(l.is_empty());
837 ("::std::collections::BTreeMap::new()".into(), false)
838 }
839 CodegenTy::Set(inner) => {
840 if l.is_empty() {
841 ("::pilota::AHashSet::new()".into(), false)
842 } else {
843 let stream = self.list_stream(l, inner)?;
844 (
845 format!("::pilota::AHashSet::from([{stream}])").into(),
846 false,
847 )
848 }
849 }
850 CodegenTy::BTreeSet(inner) => {
851 if l.is_empty() {
852 ("::std::collections::BTreeSet::new()".into(), false)
853 } else {
854 let stream = self.list_stream(l, inner)?;
855 (
856 format!("::std::collections::BTreeSet::from([{stream}])").into(),
857 false,
858 )
859 }
860 }
861 _ => panic!("invalid map type {map:?}"),
862 },
863 (Literal::List(l), CodegenTy::Map(_, _)) => {
864 assert!(l.is_empty());
865 ("::pilota::AHashMap::new()".into(), false)
866 }
867 (Literal::List(l), CodegenTy::BTreeMap(_, _)) => {
868 assert!(l.is_empty());
869 ("::std::collections::BTreeMap::new()".into(), false)
870 }
871 _ => self.lit_into_ty(lit, ty)?,
872 })
873 }
874
875 fn ident_into_ty(
876 &self,
877 did: DefId,
878 ident_ty: &CodegenTy,
879 target: &CodegenTy,
880 ) -> (FastStr, bool ) {
881 let stream = self.cur_related_item_path(did);
882 if ident_ty == target {
883 return (stream.clone(), true);
884 }
885
886 let ident_norm = self.normalize_codegen_ty(ident_ty);
887 let target_norm = self.normalize_codegen_ty(target);
888
889 if ident_norm == target_norm {
890 if let Some((converted, is_const)) =
891 self.convert_codegen_ty_expr(stream.clone(), ident_ty, target, true)
892 {
893 return (converted, is_const);
894 }
895 }
896
897 match (ident_ty, target) {
898 (CodegenTy::Str, CodegenTy::FastStr) => (
899 format!("::pilota::FastStr::from_static_str({stream})").into(),
900 true,
901 ),
902 (
903 CodegenTy::Adt(AdtDef {
904 did: _,
905 kind: AdtKind::Enum,
906 }),
907 CodegenTy::I64,
908 )
909 | (
910 CodegenTy::Adt(AdtDef {
911 did: _,
912 kind: AdtKind::Enum,
913 }),
914 CodegenTy::I32,
915 )
916 | (
917 CodegenTy::Adt(AdtDef {
918 did: _,
919 kind: AdtKind::Enum,
920 }),
921 CodegenTy::I16,
922 )
923 | (
924 CodegenTy::Adt(AdtDef {
925 did: _,
926 kind: AdtKind::Enum,
927 }),
928 CodegenTy::I8,
929 ) => {
930 let stream = self.cur_related_item_path(did);
931 let target = match target {
932 CodegenTy::I64 => "i64",
933 CodegenTy::I32 => "i32",
934 CodegenTy::I16 => "i16",
935 CodegenTy::I8 => "i8",
936 _ => unreachable!(),
937 };
938 (format!("({stream}.inner() as {target})").into(), true)
939 }
940 _ => panic!("invalid convert {ident_ty:?} to {target:?}"),
941 }
942 }
943
944 fn normalize_codegen_ty(&self, ty: &CodegenTy) -> CodegenTy {
946 match ty {
947 CodegenTy::Adt(AdtDef {
948 kind: AdtKind::NewType(inner),
949 ..
950 }) => self.normalize_codegen_ty(inner),
951 CodegenTy::LazyStaticRef(inner) | CodegenTy::StaticRef(inner) => {
952 self.normalize_codegen_ty(inner)
953 }
954 CodegenTy::Str => CodegenTy::FastStr,
955 CodegenTy::FastStr => CodegenTy::FastStr,
956 CodegenTy::Vec(inner) => CodegenTy::Vec(Arc::new(self.normalize_codegen_ty(inner))),
957 CodegenTy::Array(inner, size) => {
958 CodegenTy::Array(Arc::new(self.normalize_codegen_ty(inner)), *size)
959 }
960 CodegenTy::Set(inner) => CodegenTy::Set(Arc::new(self.normalize_codegen_ty(inner))),
961 CodegenTy::BTreeSet(inner) => {
962 CodegenTy::BTreeSet(Arc::new(self.normalize_codegen_ty(inner)))
963 }
964 CodegenTy::Map(k, v) => CodegenTy::Map(
965 Arc::new(self.normalize_codegen_ty(k)),
966 Arc::new(self.normalize_codegen_ty(v)),
967 ),
968 CodegenTy::BTreeMap(k, v) => CodegenTy::BTreeMap(
969 Arc::new(self.normalize_codegen_ty(k)),
970 Arc::new(self.normalize_codegen_ty(v)),
971 ),
972 CodegenTy::Arc(inner) => CodegenTy::Arc(Arc::new(self.normalize_codegen_ty(inner))),
973 _ => ty.clone(),
974 }
975 }
976
977 fn convert_codegen_ty_expr(
978 &self,
979 expr: FastStr,
980 from: &CodegenTy,
981 to: &CodegenTy,
982 owned: bool,
983 ) -> Option<(FastStr, bool)> {
984 if from == to {
985 if owned || self.is_copy_ty(from) {
986 return Some((expr, true));
987 }
988 let expr_str = expr.to_string();
989 return Some((format!("({expr_str}).clone()").into(), false));
990 }
991
992 match (from, to) {
993 (
994 CodegenTy::Adt(AdtDef {
995 kind: AdtKind::NewType(inner),
996 ..
997 }),
998 _,
999 ) => {
1000 if owned {
1001 let expr_str = expr.to_string();
1002 let inner_expr: FastStr = format!("({expr_str}).0").into();
1003 let (converted, is_const) =
1004 self.convert_codegen_ty_expr(inner_expr, inner, to, true)?;
1005 Some((converted, is_const))
1006 } else {
1007 let expr_str = expr.to_string();
1008 let cloned: FastStr = format!("{expr_str}.clone()").into();
1009 let (converted, _) = self.convert_codegen_ty_expr(cloned, inner, to, true)?;
1010 Some((converted, false))
1011 }
1012 }
1013 (
1014 _,
1015 CodegenTy::Adt(AdtDef {
1016 kind: AdtKind::NewType(inner),
1017 did,
1018 }),
1019 ) => {
1020 let (inner_expr, _) = self.convert_codegen_ty_expr(expr, from, inner, owned)?;
1021 let ident = self.cur_related_item_path(*did);
1022 Some((format!("{ident}({inner_expr})").into(), false))
1023 }
1024 (CodegenTy::Str, CodegenTy::FastStr) => {
1025 let expr_str = expr.to_string();
1026 Some((
1027 format!("::pilota::FastStr::from_static_str({expr_str})").into(),
1028 true,
1029 ))
1030 }
1031 (CodegenTy::LazyStaticRef(inner), _) => {
1032 let expr_str = expr.to_string();
1033 let needs_clone = !expr_str.trim_end().ends_with(".clone()");
1034 let owned_expr: FastStr = if needs_clone {
1035 format!("{expr_str}.clone()").into()
1036 } else {
1037 expr_str.into()
1038 };
1039 let (converted, _) = self.convert_codegen_ty_expr(owned_expr, inner, to, true)?;
1040 Some((converted, false))
1041 }
1042 (CodegenTy::StaticRef(inner), _) => {
1043 let expr_str = expr.to_string();
1044 let needs_clone = !expr_str.trim_end().ends_with(".clone()");
1045 let owned_expr: FastStr = if needs_clone {
1046 format!("{expr_str}.clone()").into()
1047 } else {
1048 expr_str.into()
1049 };
1050 let (converted, _) = self.convert_codegen_ty_expr(owned_expr, inner, to, true)?;
1051 Some((converted, false))
1052 }
1053 (CodegenTy::Vec(from_inner), CodegenTy::Vec(to_inner)) => {
1054 if from_inner == to_inner {
1055 return Some((expr, owned));
1056 }
1057 let expr_str = expr.to_string();
1058 if owned {
1059 let body = self.convert_owned_element_expr("el.clone()", from_inner, to_inner);
1060 let converted = format!(
1061 r#"{{
1062 {expr_str}
1063 .iter()
1064 .map(|el| {body})
1065 .collect::<::std::vec::Vec<_>>()
1066 }}"#
1067 )
1068 .into();
1069 Some((converted, false))
1070 } else {
1071 let body = self.convert_element_expr("el", from_inner, to_inner);
1072 let converted = format!(
1073 r#"{{
1074 {expr_str}
1075 .iter()
1076 .map(|el| {body})
1077 .collect::<::std::vec::Vec<_>>()
1078 }}"#
1079 )
1080 .into();
1081 Some((converted, false))
1082 }
1083 }
1084 (CodegenTy::Set(from_inner), CodegenTy::Set(to_inner)) => {
1085 if from_inner == to_inner {
1086 return Some((expr, owned));
1087 }
1088 let expr_str = expr.to_string();
1089 if owned {
1090 let body = self.convert_owned_element_expr("el.clone()", from_inner, to_inner);
1091 let converted = format!(
1092 r#"{{
1093 {expr_str}
1094 .iter()
1095 .map(|el| {body})
1096 .collect::<::pilota::AHashSet<_>>()
1097 }}"#
1098 )
1099 .into();
1100 Some((converted, false))
1101 } else {
1102 let body = self.convert_element_expr("el", from_inner, to_inner);
1103 let converted = format!(
1104 r#"{{
1105 {expr_str}
1106 .iter()
1107 .map(|el| {body})
1108 .collect::<::pilota::AHashSet<_>>()
1109 }}"#
1110 )
1111 .into();
1112 Some((converted, false))
1113 }
1114 }
1115 (CodegenTy::BTreeSet(from_inner), CodegenTy::BTreeSet(to_inner)) => {
1116 if from_inner == to_inner {
1117 return Some((expr, owned));
1118 }
1119 let expr_str = expr.to_string();
1120 if owned {
1121 let body = self.convert_owned_element_expr("el.clone()", from_inner, to_inner);
1122 let converted = format!(
1123 r#"{{
1124 {expr_str}.iter()
1125 .map(|el| {body})
1126 .collect::<::std::collections::BTreeSet<_>>()
1127 }}"#
1128 )
1129 .into();
1130 Some((converted, false))
1131 } else {
1132 let body = self.convert_element_expr("el", from_inner, to_inner);
1133 let converted = format!(
1134 r#"{{
1135 {expr_str}.iter()
1136 .map(|el| {body})
1137 .collect::<::std::collections::BTreeSet<_>>()
1138 }}"#
1139 )
1140 .into();
1141 Some((converted, false))
1142 }
1143 }
1144 (CodegenTy::Map(from_k, from_v), CodegenTy::Map(to_k, to_v)) => {
1145 if from_k == to_k && from_v == to_v {
1146 return Some((expr, owned));
1147 }
1148 let expr_str = expr.to_string();
1149 if owned {
1150 let k_body = self.convert_owned_element_expr("k.clone()", from_k, to_k);
1151 let v_body = self.convert_owned_element_expr("v.clone()", from_v, to_v);
1152 let converted = format!(
1153 r#"{{
1154 {expr_str}
1155 .iter()
1156 .map(|(k, v)| ({k_body}, {v_body}))
1157 .collect::<::pilota::AHashMap<_, _>>()
1158 }}"#
1159 )
1160 .into();
1161 Some((converted, false))
1162 } else {
1163 let k_body = self.convert_element_expr("k", from_k, to_k);
1164 let v_body = self.convert_element_expr("v", from_v, to_v);
1165 let converted = format!(
1166 r#"{{
1167 {expr_str}
1168 .iter()
1169 .map(|(k, v)| ({k_body}, {v_body}))
1170 .collect::<::pilota::AHashMap<_, _>>()
1171 }}"#
1172 )
1173 .into();
1174 Some((converted, false))
1175 }
1176 }
1177 (CodegenTy::BTreeMap(from_k, from_v), CodegenTy::BTreeMap(to_k, to_v)) => {
1178 if from_k == to_k && from_v == to_v {
1179 return Some((expr, owned));
1180 }
1181 let expr_str = expr.to_string();
1182 if owned {
1183 let k_body = self.convert_owned_element_expr("k.clone()", from_k, to_k);
1184 let v_body = self.convert_owned_element_expr("v.clone()", from_v, to_v);
1185 let converted = format!(
1186 r#"{{
1187 {expr_str}
1188 .iter()
1189 .map(|(k, v)| ({k_body}, {v_body}))
1190 .collect::<::std::collections::BTreeMap<_, _>>()
1191 }}"#
1192 )
1193 .into();
1194 Some((converted, false))
1195 } else {
1196 let k_body = self.convert_element_expr("k", from_k, to_k);
1197 let v_body = self.convert_element_expr("v", from_v, to_v);
1198 let converted = format!(
1199 r#"{{
1200 {expr_str}
1201 .iter()
1202 .map(|(k, v)| ({k_body}, {v_body}))
1203 .collect::<::std::collections::BTreeMap<_, _>>()
1204 }}"#
1205 )
1206 .into();
1207 Some((converted, false))
1208 }
1209 }
1210 _ => None,
1211 }
1212 }
1213
1214 fn lit_into_ty(
1215 &self,
1216 lit: &Literal,
1217 ty: &CodegenTy,
1218 ) -> anyhow::Result<(FastStr, bool )> {
1219 Ok(match (lit, ty) {
1220 (Literal::Path(p), ty) => {
1221 let ident_ty = self.get_codegen_ty_for_path(p.did);
1222
1223 if matches!(
1224 ty,
1225 CodegenTy::Map(_, _)
1226 | CodegenTy::BTreeMap(_, _)
1227 | CodegenTy::Set(_)
1228 | CodegenTy::BTreeSet(_)
1229 ) {
1230 let normalized_ident_ty = self.normalize_codegen_ty(&ident_ty);
1231 if matches!(
1232 normalized_ident_ty,
1233 CodegenTy::Map(_, _)
1234 | CodegenTy::BTreeMap(_, _)
1235 | CodegenTy::Set(_)
1236 | CodegenTy::BTreeSet(_)
1237 ) {
1238 let stream = self.cur_related_item_path(p.did);
1239 if let Some((converted, _)) =
1240 self.convert_codegen_ty_expr(stream.clone(), &ident_ty, ty, false)
1241 {
1242 return Ok((converted, false));
1243 }
1244 }
1245 }
1246
1247 self.ident_into_ty(p.did, &ident_ty, ty)
1248 }
1249 (Literal::String(s), CodegenTy::Str) => (format!("\"{s}\"").into(), true),
1250 (Literal::String(s), CodegenTy::String) => {
1251 (format! {"\"{s}\".to_string()"}.into(), false)
1252 }
1253 (Literal::String(s), CodegenTy::FastStr) => (
1254 format! { "::pilota::FastStr::from_static_str(\"{s}\")" }.into(),
1255 true,
1256 ),
1257 (Literal::Int(i), CodegenTy::I8) => (format! { "{i}i8" }.into(), true),
1258 (Literal::Int(i), CodegenTy::I16) => (format! { "{i}i16" }.into(), true),
1259 (Literal::Int(i), CodegenTy::I32) => (format! { "{i}i32" }.into(), true),
1260 (Literal::Int(i), CodegenTy::I64) => (format! { "{i}i64" }.into(), true),
1261 (Literal::Int(i), CodegenTy::F32) => {
1262 let f = (*i) as f32;
1263 (format!("{f}f32").into(), true)
1264 }
1265 (Literal::Int(i), CodegenTy::F64) => {
1266 let f = (*i) as f64;
1267 (format!("{f}f64").into(), true)
1268 }
1269 (
1270 Literal::Int(i),
1271 CodegenTy::Adt(AdtDef {
1272 did,
1273 kind: AdtKind::Enum,
1274 }),
1275 ) => {
1276 let item = self.item(*did).unwrap();
1277 let e = match &*item {
1278 Item::Enum(e) => e,
1279 _ => panic!("invalid enum"),
1280 };
1281
1282 (
1283 e.variants.iter().find(|v| v.discr == Some(*i)).map_or_else(
1284 || panic!("invalid enum value"),
1285 |v| self.cur_related_item_path(v.did),
1286 ),
1287 true,
1288 )
1289 }
1290 (Literal::Float(f), CodegenTy::F32) => {
1291 let f = f.parse::<f32>().unwrap();
1292 (format! { "{f}f32" }.into(), true)
1293 }
1294 (Literal::Float(f), CodegenTy::F64) => {
1295 let f = f.parse::<f64>().unwrap();
1296 (format! { "{f}f64" }.into(), true)
1297 }
1298 (Literal::Float(f), CodegenTy::OrderedF64) => {
1299 let f = f.parse::<f64>().unwrap();
1300 (format! { "::pilota::OrderedFloat({f}f64)" }.into(), true)
1301 }
1302 (
1303 l,
1304 CodegenTy::Adt(AdtDef {
1305 kind: AdtKind::NewType(inner_ty),
1306 did,
1307 }),
1308 ) => {
1309 let ident = self.cur_related_item_path(*did);
1310 let (stream, is_const) = self.lit_into_ty(l, inner_ty)?;
1311 (format! { "{ident}({stream})" }.into(), is_const)
1312 }
1313 (Literal::Map(_), CodegenTy::StaticRef(map)) => match &**map {
1314 CodegenTy::Map(_, _) | CodegenTy::BTreeMap(_, _) => {
1315 let lazy_map =
1316 self.def_lit("INNER_MAP", lit, &mut CodegenTy::LazyStaticRef(map.clone()))?;
1317 let stream = format! {
1318 r#"{{
1319 {lazy_map}
1320 &*INNER_MAP
1321 }}"#
1322 }
1323 .into();
1324 (stream, false)
1325 }
1326 _ => panic!("invalid map type {map:?}"),
1327 },
1328 (Literal::List(els), CodegenTy::Array(inner, _)) => {
1329 let stream = els
1330 .iter()
1331 .map(|el| self.lit_into_ty(el, inner))
1332 .try_collect::<_, Vec<_>, _>()?;
1333 let is_const = stream.iter().all(|(_, is_const)| *is_const);
1334 let stream = stream.into_iter().map(|(s, _)| s).join(",");
1335
1336 (format! {"[{stream}]" }.into(), is_const)
1337 }
1338 (Literal::List(els), CodegenTy::Vec(inner)) => {
1339 let stream = self.list_stream(els, inner)?;
1340 (format! { "::std::vec![{stream}]" }.into(), false)
1341 }
1342 (Literal::List(els), CodegenTy::Set(inner)) => {
1343 let stream = self.list_stream(els, inner)?;
1344 (
1345 format! { "::pilota::AHashSet::from([{stream}])" }.into(),
1346 false,
1347 )
1348 }
1349 (Literal::List(els), CodegenTy::BTreeSet(inner)) => {
1350 let stream = self.list_stream(els, inner)?;
1351 (
1352 format! { "::std::collections::BTreeSet::from([{stream}])" }.into(),
1353 false,
1354 )
1355 }
1356 (Literal::Bool(b), CodegenTy::Bool) => (format! { "{b}" }.into(), true),
1357 (Literal::Int(i), CodegenTy::Bool) => {
1358 let b = *i != 0;
1359 (format! { "{b}" }.into(), true)
1360 }
1361 (Literal::String(s), CodegenTy::Bytes) => {
1362 let s = &**s;
1363 (
1364 format! { "::pilota::Bytes::from_static(\"{s}\".as_bytes())" }.into(),
1365 true,
1366 )
1367 }
1368 (
1369 Literal::Map(m),
1370 CodegenTy::Adt(AdtDef {
1371 did,
1372 kind: AdtKind::Struct,
1373 }),
1374 ) => {
1375 let def = self.item(*did).unwrap();
1376 let def = match &*def {
1377 Item::Message(m) => m,
1378 _ => panic!(),
1379 };
1380
1381 let fields: Vec<_> = def
1382 .fields
1383 .iter()
1384 .map(|f| {
1385 let v = m.iter().find_map(|(k, v)| {
1386 let k = match k {
1387 Literal::String(s) => s,
1388 _ => panic!(),
1389 };
1390 if **k == **f.name { Some(v) } else { None }
1391 });
1392
1393 let name = self.rust_name(f.did);
1394
1395 if let Some(v) = v {
1396 let (mut v, is_const) =
1397 self.lit_into_ty(v, &self.codegen_item_ty(f.ty.kind.clone()))?;
1398
1399 if f.is_optional() {
1400 v = format!("Some({v})").into()
1401 }
1402 anyhow::Ok((format!("{name}: {v}"), is_const))
1403 } else if f.is_optional() {
1404 anyhow::Ok((format!("{name}: None"), true))
1405 } else {
1406 anyhow::Ok((format!("{name}: Default::default()"), false))
1407 }
1408 })
1409 .try_collect()?;
1410 let is_const = fields.iter().all(|(_, is_const)| *is_const);
1411 let fields = fields.into_iter().map(|f| f.0).join(",");
1412
1413 let name = self.cur_related_item_path(*did);
1414
1415 (
1416 format! {
1417 r#"{name} {{
1418 {fields}
1419 }}"#
1420 }
1421 .into(),
1422 is_const,
1423 )
1424 }
1425 (Literal::Map(m), CodegenTy::Map(k_ty, v_ty)) => {
1426 (self.map_literal_expr(m, k_ty, v_ty, false)?, false)
1427 }
1428 (Literal::Map(m), CodegenTy::BTreeMap(k_ty, v_ty)) => {
1429 (self.map_literal_expr(m, k_ty, v_ty, true)?, false)
1430 }
1431 (Literal::List(l), CodegenTy::Map(_, _)) => {
1432 assert!(l.is_empty());
1433 ("::pilota::AHashMap::new()".into(), false)
1434 }
1435 (Literal::List(l), CodegenTy::BTreeMap(_, _)) => {
1436 assert!(l.is_empty());
1437 ("::std::collections::BTreeMap::new()".into(), false)
1438 }
1439 _ => {
1440 let (def_path, idl_file) = with_cur_item(|def_id| {
1441 let def_path = self.item_path(def_id).iter().join("::");
1442 let file_path = self
1443 .db
1444 .node(def_id)
1445 .and_then(|node| {
1446 self.db
1447 .file_paths()
1448 .get(&node.file_id)
1449 .map(|path| path.display().to_string())
1450 })
1451 .unwrap_or_else(|| "<unknown>".to_string());
1452 (def_path, file_path)
1453 });
1454
1455 let error_message = format!(
1456 "unexpected literal {lit:?} with ty {ty}, def_path: {def_path}, idl_file: {idl_file}"
1457 );
1458
1459 panic!("{error_message}");
1460 }
1461 })
1462 }
1463
1464 #[inline]
1465 fn list_stream(&self, els: &[Literal], inner: &Arc<CodegenTy>) -> anyhow::Result<String> {
1466 Ok(els
1467 .iter()
1468 .map(|el| self.lit_into_ty(el, inner))
1469 .try_collect::<_, Vec<_>, _>()?
1470 .into_iter()
1471 .map(|(s, _)| s)
1472 .join(","))
1473 }
1474
1475 pub(crate) fn def_lit(
1476 &self,
1477 name: &str,
1478 lit: &Literal,
1479 ty: &mut CodegenTy,
1480 ) -> anyhow::Result<String> {
1481 let should_lazy_static = ty.should_lazy_static();
1482 if let (Literal::List(lit), CodegenTy::Array(_, size)) = (lit, &mut *ty) {
1483 *size = lit.len()
1484 }
1485 Ok(if should_lazy_static {
1486 let lit = self.lit_as_rvalue(lit, ty)?.0;
1487 format! {r#"
1488 pub static {name}: ::std::sync::LazyLock<{ty}> = ::std::sync::LazyLock::new(|| {{
1489 {lit}
1490 }});
1491 "#}
1492 } else {
1493 let (lit, is_const) = self.lit_into_ty(lit, ty)?;
1494 if is_const {
1495 format!(r#"pub const {name}: {ty} = {lit};"#)
1496 } else {
1497 format! {r#"
1498 pub static {name}: ::std::sync::LazyLock<{ty}> = ::std::sync::LazyLock::new(|| {{
1499 {lit}
1500 }});
1501 "#}
1502 }
1503 })
1504 }
1505
1506 pub fn rust_name(&self, def_id: DefId) -> Symbol {
1507 let node = self.node(def_id).unwrap();
1508
1509 if let Some(name) = self
1510 .tags(node.tags)
1511 .and_then(|tags| tags.get::<crate::tags::PilotaName>().cloned())
1512 {
1513 return name.0.into();
1514 }
1515
1516 if !self.config.change_case || self.cache.names.contains_key(&def_id) {
1517 return node.name();
1518 }
1519
1520 match self.node(def_id).unwrap().kind {
1521 NodeKind::Item(item) => match &*item {
1522 crate::rir::Item::Message(m) => (&**m.name).struct_ident(),
1523 crate::rir::Item::Enum(e) => (&**e.name).enum_ident(),
1524 crate::rir::Item::Service(s) => (&**s.name).trait_ident(),
1525 crate::rir::Item::NewType(t) => (&**t.name).newtype_ident(),
1526 crate::rir::Item::Const(c) => (&**c.name).const_ident(),
1527 crate::rir::Item::Mod(m) => (&**m.name).mod_ident(),
1528 },
1529 NodeKind::Variant(v) => {
1530 let parent = self.node(def_id).unwrap().parent.unwrap();
1531 let item = self.expect_item(parent);
1532 match &*item {
1533 rir::Item::Enum(e) => {
1534 if e.repr.is_some() {
1535 (&**v.name).const_ident()
1536 } else {
1537 (&**v.name).variant_ident()
1538 }
1539 }
1540 _ => unreachable!(),
1541 }
1542 }
1543 NodeKind::Field(f) => (&**f.name).field_ident(),
1544 NodeKind::Method(m) => (&**m.name).fn_ident(),
1545 NodeKind::Arg(a) => (&**a.name).field_ident(),
1546 }
1547 .into()
1548 }
1549
1550 pub fn mod_path(&self, def_id: DefId) -> Arc<[Symbol]> {
1551 self.source.path_resolver.mod_prefix(self, def_id)
1552 }
1553
1554 pub fn mod_index(&self, def_id: DefId) -> ModPath {
1555 let mod_path = self
1556 .mod_path(def_id)
1557 .iter()
1558 .map(|s| s.0.clone())
1559 .collect_vec();
1560
1561 match &*self.source.mode {
1562 Mode::Workspace(_) => ModPath::from(&mod_path[1..]),
1563 Mode::SingleFile { .. } => ModPath::from(mod_path),
1564 }
1565 }
1566
1567 pub fn item_path(&self, def_id: DefId) -> Arc<[Symbol]> {
1568 self.source.path_resolver.path_for_def_id(self, def_id)
1569 }
1570
1571 fn related_path(&self, p1: &[Symbol], p2: &[Symbol]) -> FastStr {
1572 self.source.path_resolver.related_path(p1, p2)
1573 }
1574
1575 pub fn cur_related_item_path(&self, did: DefId) -> FastStr {
1576 let a = with_cur_item(|def_id| def_id);
1577 self.related_item_path(a, did)
1578 }
1579
1580 pub fn related_item_path(&self, a: DefId, b: DefId) -> FastStr {
1581 let cur_item_path = self.item_path(a);
1582 let mut mod_segs = vec![];
1583
1584 cur_item_path[..cur_item_path.len() - 1]
1585 .iter()
1586 .for_each(|p| {
1587 mod_segs.push(p.clone());
1588 });
1589
1590 let other_item_path = self.item_path(b);
1591 self.related_path(&mod_segs, &other_item_path)
1592 }
1593
1594 #[allow(clippy::single_match)]
1595 pub fn exec_plugin<P: Plugin>(&self, mut p: P) {
1596 p.on_codegen_uint(self, &self.cache.codegen_items);
1597
1598 p.on_emit(self)
1599 }
1600
1601 pub(crate) fn workspace_info(&self) -> &WorkspaceInfo {
1602 let Mode::Workspace(info) = &*self.source.mode else {
1603 panic!(
1604 "can not access workspace info in mode `{:?}`",
1605 self.source.mode
1606 )
1607 };
1608 info
1609 }
1610
1611 pub fn config(&self, crate_id: &CrateId) -> &serde_yaml::Value {
1625 &self.find_service(crate_id.main_file).config
1626 }
1627
1628 pub(crate) fn crate_name(&self, location: &DefLocation) -> FastStr {
1629 match location {
1630 DefLocation::Fixed(crate_id, _) => {
1631 let main_file = crate_id.main_file;
1632 let service = self.find_service(main_file);
1633 self.config(crate_id)
1634 .get("crate_name")
1635 .and_then(|s| s.as_str().map(FastStr::new))
1636 .unwrap_or_else(|| {
1637 service
1638 .path
1639 .file_stem()
1640 .unwrap()
1641 .to_str()
1642 .unwrap()
1643 .replace('.', "_")
1644 .into()
1645 })
1646 }
1647 DefLocation::Dynamic => self.config.common_crate_name.clone(),
1648 }
1649 }
1650
1651 fn find_service(&self, file_id: FileId) -> &crate::IdlService {
1652 self.source
1653 .services
1654 .iter()
1655 .find(|s| {
1656 let path = s
1657 .path
1658 .normalize()
1659 .unwrap_or_else(|err| {
1660 panic!("normalize path {} failed: {:?}", s.path.display(), err)
1661 })
1662 .into_path_buf();
1663 self.file_id(path.clone()).unwrap_or_else(|| {
1664 panic!(
1665 "file_id not found for path {} in file_ids_map {:?}",
1666 path.display(),
1667 self.file_ids_map()
1668 )
1669 }) == file_id
1670 })
1671 .unwrap()
1672 }
1673}
1674
1675pub mod tls {
1676
1677 use scoped_tls::scoped_thread_local;
1678
1679 use super::Context;
1680 use crate::DefId;
1681
1682 scoped_thread_local!(pub static CONTEXT: Context);
1683 scoped_thread_local!(pub static CUR_ITEM: DefId);
1684
1685 pub fn with_cx<T, F>(f: F) -> T
1686 where
1687 F: FnOnce(&Context) -> T,
1688 {
1689 CONTEXT.with(|cx| f(cx))
1690 }
1691
1692 pub fn with_cur_item<T, F>(f: F) -> T
1693 where
1694 F: FnOnce(DefId) -> T,
1695 {
1696 CUR_ITEM.with(|def_id| f(*def_id))
1697 }
1698}
1699
1700#[cfg(test)]
1701mod tests {
1702 use std::{collections::HashMap, path::PathBuf, sync::Arc};
1703
1704 use anyhow::Result;
1705 use faststr::FastStr;
1706 use pilota::Bytes;
1707 use rustc_hash::{FxHashMap, FxHashSet};
1708
1709 use super::*;
1710 use crate::{
1711 middle::{
1712 ext::{FileExts, ItemExts},
1713 rir::{self, FieldKind, Message},
1714 ty::{CodegenTy, Ty, TyKind},
1715 },
1716 symbol::{Ident, Symbol},
1717 };
1718
1719 fn make_test_context() -> Context {
1720 let mode = Arc::new(Mode::SingleFile {
1721 file_path: PathBuf::from("dummy.rs"),
1722 });
1723 let services: Arc<[crate::IdlService]> =
1724 Arc::from(Vec::<crate::IdlService>::new().into_boxed_slice());
1725 Context {
1726 db: RootDatabase::default(),
1727 source: Source {
1728 source_type: SourceType::Thrift,
1729 services,
1730 mode: mode.clone(),
1731 path_resolver: Arc::new(DefaultPathResolver),
1732 },
1733 config: Config {
1734 change_case: false,
1735 split: false,
1736 with_descriptor: false,
1737 with_field_mask: false,
1738 touch_all: false,
1739 common_crate_name: "common".into(),
1740 with_comments: false,
1741 },
1742 cache: Cache {
1743 adjusts: Arc::new(DashMap::default()),
1744 mod_idxes: AHashMap::new(),
1745 codegen_items: Vec::new(),
1746 mod_items: AHashMap::new(),
1747 def_mod: HashMap::new(),
1748 mod_files: HashMap::new(),
1749 keep_unknown_fields: Arc::new(FxHashSet::default()),
1750 location_map: Arc::new(FxHashMap::default()),
1751 entry_map: Arc::new(HashMap::default()),
1752 plugin_gen: Arc::new(DashMap::default()),
1753 dedups: Vec::new(),
1754 names: FxHashMap::default(),
1755 },
1756 }
1757 }
1758
1759 #[test]
1760 fn collect_items_traverses_field_dependencies() {
1761 let file_id = FileId::from_u32(0);
1762 let root_id = DefId::from_u32(0);
1763 let dep_id = DefId::from_u32(1);
1764 let field_id = DefId::from_u32(2);
1765 let tag_id = TagId::from_u32(0);
1766
1767 let dep_message = Arc::new(rir::Item::Message(Message {
1768 name: Ident::from("Dep"),
1769 fields: Vec::new(),
1770 is_wrapper: false,
1771 item_exts: ItemExts::Thrift,
1772 leading_comments: FastStr::new(""),
1773 trailing_comments: FastStr::new(""),
1774 }));
1775
1776 let dep_node = rir::Node {
1777 file_id,
1778 kind: rir::NodeKind::Item(dep_message.clone()),
1779 parent: None,
1780 tags: tag_id,
1781 related_nodes: Vec::new(),
1782 };
1783
1784 let field_ty = Ty {
1785 kind: TyKind::Path(rir::Path {
1786 kind: rir::DefKind::Type,
1787 did: dep_id,
1788 }),
1789 tags_id: tag_id,
1790 };
1791
1792 let field = Arc::new(rir::Field {
1793 did: field_id,
1794 name: Ident::from("dep"),
1795 id: 1,
1796 ty: field_ty,
1797 kind: FieldKind::Required,
1798 tags_id: tag_id,
1799 default: None,
1800 item_exts: ItemExts::Thrift,
1801 leading_comments: FastStr::new(""),
1802 trailing_comments: FastStr::new(""),
1803 });
1804
1805 let root_message = Arc::new(rir::Item::Message(Message {
1806 name: Ident::from("Root"),
1807 fields: vec![field],
1808 is_wrapper: false,
1809 item_exts: ItemExts::Thrift,
1810 leading_comments: FastStr::new(""),
1811 trailing_comments: FastStr::new(""),
1812 }));
1813
1814 let root_node = rir::Node {
1815 file_id,
1816 kind: rir::NodeKind::Item(root_message.clone()),
1817 parent: None,
1818 tags: tag_id,
1819 related_nodes: Vec::new(),
1820 };
1821
1822 let mut nodes = FxHashMap::default();
1823 nodes.insert(root_id, root_node);
1824 nodes.insert(dep_id, dep_node);
1825
1826 let package = ItemPath::from(Arc::<[Symbol]>::from(
1827 Vec::<Symbol>::new().into_boxed_slice(),
1828 ));
1829 let file = rir::File {
1830 package,
1831 items: vec![root_id, dep_id],
1832 file_id,
1833 uses: Vec::new(),
1834 descriptor: Bytes::new(),
1835 extensions: FileExts::Thrift,
1836 comments: FastStr::new(""),
1837 };
1838
1839 let file_arc = Arc::new(file);
1840
1841 let mut file_ids_map = FxHashMap::default();
1842 let normalized = Arc::new(PathBuf::from("/tmp/test.thrift"));
1843 file_ids_map.insert(normalized.clone(), file_id);
1844
1845 let mut file_paths = FxHashMap::default();
1846 file_paths.insert(file_id, normalized);
1847
1848 let mut file_names = FxHashMap::default();
1849 file_names.insert(file_id, FastStr::from_static_str("test.thrift"));
1850
1851 let db = RootDatabase::default()
1852 .with_nodes(nodes)
1853 .with_files(vec![(file_id, file_arc)].into_iter())
1854 .with_file_ids_map(file_ids_map)
1855 .with_file_paths(file_paths)
1856 .with_file_names(file_names)
1857 .with_input_files(vec![file_id]);
1858
1859 let builder = ContextBuilder::new(
1860 db,
1861 Mode::SingleFile {
1862 file_path: PathBuf::from("/tmp/output.rs"),
1863 },
1864 vec![root_id],
1865 );
1866
1867 let collected = builder.collect_items(&[root_id]);
1868
1869 assert!(collected.contains(&root_id));
1870 assert!(collected.contains(&dep_id));
1871 assert_eq!(collected.len(), 2);
1872 }
1873
1874 #[test]
1875 fn lit_into_ty_handles_basic_literals() -> Result<()> {
1876 let cx = make_test_context();
1877
1878 let (expr, is_const) =
1879 cx.lit_into_ty(&Literal::String(Arc::from("hello")), &CodegenTy::FastStr)?;
1880 assert_eq!(&*expr, "::pilota::FastStr::from_static_str(\"hello\")");
1881 assert!(is_const);
1882
1883 let list_lit = Literal::List(vec![Literal::Int(1), Literal::Int(2)]);
1884 let vec_ty = CodegenTy::Vec(Arc::new(CodegenTy::I32));
1885 let (expr, is_const) = cx.lit_into_ty(&list_lit, &vec_ty)?;
1886 assert_eq!(&*expr, "::std::vec![1i32,2i32]");
1887 assert!(!is_const);
1888
1889 Ok(())
1890 }
1891
1892 #[test]
1893 fn convert_codegen_vec_owned_iter_clone() {
1894 let cx = make_test_context();
1895 let from_ty = CodegenTy::Vec(Arc::new(CodegenTy::Str));
1896 let to_ty = CodegenTy::Vec(Arc::new(CodegenTy::FastStr));
1897 let (expr, _) = cx
1898 .convert_codegen_ty_expr(FastStr::from_static_str("items"), &from_ty, &to_ty, true)
1899 .expect("vec conversion should succeed");
1900 let rendered = expr.to_string();
1901 assert!(rendered.contains(".iter()"));
1902 assert!(!rendered.contains("into_iter"));
1903 assert!(rendered.contains("::pilota::FastStr::from_static_str"));
1904 }
1905
1906 #[test]
1907 fn convert_codegen_set_owned_iter_clone() {
1908 let cx = make_test_context();
1909 let from_ty = CodegenTy::Set(Arc::new(CodegenTy::Str));
1910 let to_ty = CodegenTy::Set(Arc::new(CodegenTy::FastStr));
1911 let (expr, _) = cx
1912 .convert_codegen_ty_expr(
1913 FastStr::from_static_str("set_items"),
1914 &from_ty,
1915 &to_ty,
1916 true,
1917 )
1918 .expect("set conversion should succeed");
1919 let rendered = expr.to_string();
1920 assert!(rendered.contains(".iter()"));
1921 assert!(!rendered.contains("into_iter"));
1922 assert!(rendered.contains("::pilota::FastStr::from_static_str"));
1923 }
1924
1925 #[test]
1926 fn convert_codegen_map_owned_iter_clone() {
1927 let cx = make_test_context();
1928 let from_ty = CodegenTy::Map(Arc::new(CodegenTy::Str), Arc::new(CodegenTy::Str));
1929 let to_ty = CodegenTy::Map(Arc::new(CodegenTy::FastStr), Arc::new(CodegenTy::FastStr));
1930 let (expr, _) = cx
1931 .convert_codegen_ty_expr(
1932 FastStr::from_static_str("map_items"),
1933 &from_ty,
1934 &to_ty,
1935 true,
1936 )
1937 .expect("map conversion should succeed");
1938 let rendered = expr.to_string();
1939 assert!(rendered.contains(".iter()"));
1940 assert!(!rendered.contains("into_iter"));
1941 assert!(rendered.contains("::pilota::FastStr::from_static_str(k.clone())"));
1942 assert!(rendered.contains("::pilota::FastStr::from_static_str(v.clone())"));
1943 }
1944}