1use std::collections::HashMap;
11
12use serde::{Deserialize, Serialize};
13
14use super::super::node::id::NodeId;
15
16#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
24pub struct MacroNodeMetadata {
25 pub macro_generated: Option<bool>,
27
28 pub macro_source: Option<String>,
30
31 pub cfg_condition: Option<String>,
33
34 pub cfg_active: Option<bool>,
36
37 pub proc_macro_kind: Option<ProcMacroFunctionKind>,
39
40 pub expansion_cached: Option<bool>,
42
43 pub unresolved_attributes: Vec<String>,
46}
47
48#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
50#[serde(rename_all = "snake_case")]
51pub enum ProcMacroFunctionKind {
52 Derive,
54 Attribute,
56 FunctionLike,
58}
59
60#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
62pub struct ClasspathNodeMetadata {
63 pub coordinates: Option<String>,
65 pub jar_path: String,
67 pub fqn: String,
69 pub is_direct_dependency: bool,
71}
72
73#[derive(Debug, Clone, PartialEq, Eq)]
83pub enum NodeMetadata {
84 Macro(MacroNodeMetadata),
86 Classpath(ClasspathNodeMetadata),
88}
89
90const NODE_METADATA_MACRO: u8 = 0;
92const NODE_METADATA_CLASSPATH: u8 = 1;
93
94#[derive(Debug, Clone, Default)]
118pub struct NodeMetadataStore {
119 entries: HashMap<(u32, u64), NodeMetadata>,
121}
122
123#[derive(Debug, Clone, Serialize, Deserialize)]
125struct NodeMetadataEntryV7 {
126 index: u32,
127 generation: u64,
128 kind: u8,
130 macro_data: Option<MacroNodeMetadata>,
132 classpath_data: Option<ClasspathNodeMetadata>,
134}
135
136impl Serialize for NodeMetadataStore {
137 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
138 let entries: Vec<NodeMetadataEntryV7> = self
139 .entries
140 .iter()
141 .map(|(&(index, generation), metadata)| match metadata {
142 NodeMetadata::Macro(m) => NodeMetadataEntryV7 {
143 index,
144 generation,
145 kind: NODE_METADATA_MACRO,
146 macro_data: Some(m.clone()),
147 classpath_data: None,
148 },
149 NodeMetadata::Classpath(c) => NodeMetadataEntryV7 {
150 index,
151 generation,
152 kind: NODE_METADATA_CLASSPATH,
153 macro_data: None,
154 classpath_data: Some(c.clone()),
155 },
156 })
157 .collect();
158 entries.serialize(serializer)
159 }
160}
161
162impl<'de> Deserialize<'de> for NodeMetadataStore {
163 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
164 let entries: Vec<NodeMetadataEntryV7> = Vec::deserialize(deserializer)?;
170 let mut map = HashMap::with_capacity(entries.len());
171 for e in entries {
172 let metadata = match e.kind {
173 NODE_METADATA_CLASSPATH => {
174 let data = e.classpath_data.ok_or_else(|| {
175 serde::de::Error::custom(
176 "missing classpath_data for Classpath metadata entry",
177 )
178 })?;
179 NodeMetadata::Classpath(data)
180 }
181 _ => {
182 let data = e.macro_data.unwrap_or_default();
184 NodeMetadata::Macro(data)
185 }
186 };
187 map.insert((e.index, e.generation), metadata);
188 }
189 Ok(Self { entries: map })
190 }
191}
192
193impl NodeMetadataStore {
194 #[must_use]
196 pub fn new() -> Self {
197 Self::default()
198 }
199
200 #[must_use]
205 pub fn get(&self, node_id: NodeId) -> Option<&MacroNodeMetadata> {
206 match self.entries.get(&(node_id.index(), node_id.generation()))? {
207 NodeMetadata::Macro(m) => Some(m),
208 NodeMetadata::Classpath(_) => None,
209 }
210 }
211
212 #[must_use]
217 pub fn get_metadata(&self, node_id: NodeId) -> Option<&NodeMetadata> {
218 self.entries.get(&(node_id.index(), node_id.generation()))
219 }
220
221 #[must_use]
223 pub fn get_mut(&mut self, node_id: NodeId) -> Option<&mut MacroNodeMetadata> {
224 match self
225 .entries
226 .get_mut(&(node_id.index(), node_id.generation()))?
227 {
228 NodeMetadata::Macro(m) => Some(m),
229 NodeMetadata::Classpath(_) => None,
230 }
231 }
232
233 pub fn insert(&mut self, node_id: NodeId, metadata: MacroNodeMetadata) {
237 self.entries.insert(
238 (node_id.index(), node_id.generation()),
239 NodeMetadata::Macro(metadata),
240 );
241 }
242
243 pub fn insert_metadata(&mut self, node_id: NodeId, metadata: NodeMetadata) {
245 self.entries
246 .insert((node_id.index(), node_id.generation()), metadata);
247 }
248
249 pub fn get_or_insert_default(&mut self, node_id: NodeId) -> &mut MacroNodeMetadata {
251 let entry = self
252 .entries
253 .entry((node_id.index(), node_id.generation()))
254 .or_insert_with(|| NodeMetadata::Macro(MacroNodeMetadata::default()));
255 match entry {
256 NodeMetadata::Macro(m) => m,
257 NodeMetadata::Classpath(_) => {
261 panic!("get_or_insert_default called on a Classpath metadata entry")
262 }
263 }
264 }
265
266 pub fn remove(&mut self, node_id: NodeId) -> Option<MacroNodeMetadata> {
268 match self
269 .entries
270 .remove(&(node_id.index(), node_id.generation()))?
271 {
272 NodeMetadata::Macro(m) => Some(m),
273 NodeMetadata::Classpath(_) => None,
274 }
275 }
276
277 pub fn remove_metadata(&mut self, node_id: NodeId) -> Option<NodeMetadata> {
279 self.entries
280 .remove(&(node_id.index(), node_id.generation()))
281 }
282
283 #[must_use]
285 pub fn len(&self) -> usize {
286 self.entries.len()
287 }
288
289 #[must_use]
291 pub fn is_empty(&self) -> bool {
292 self.entries.is_empty()
293 }
294
295 pub fn iter(&self) -> impl Iterator<Item = ((u32, u64), &MacroNodeMetadata)> {
297 self.entries.iter().filter_map(|(&k, v)| match v {
298 NodeMetadata::Macro(m) => Some((k, m)),
299 NodeMetadata::Classpath(_) => None,
300 })
301 }
302
303 pub fn iter_all(&self) -> impl Iterator<Item = ((u32, u64), &NodeMetadata)> {
305 self.entries.iter().map(|(&k, v)| (k, v))
306 }
307
308 pub fn merge(&mut self, other: &NodeMetadataStore) {
312 for (&key, value) in &other.entries {
313 self.entries.insert(key, value.clone());
314 }
315 }
316}
317
318impl PartialEq for NodeMetadataStore {
319 fn eq(&self, other: &Self) -> bool {
320 self.entries == other.entries
321 }
322}
323
324impl Eq for NodeMetadataStore {}
325
326#[cfg(test)]
327mod tests {
328 use super::*;
329
330 #[test]
331 fn test_metadata_store_basic_operations() {
332 let mut store = NodeMetadataStore::new();
333 assert!(store.is_empty());
334 assert_eq!(store.len(), 0);
335
336 let node = NodeId::new(5, 1);
337 let metadata = MacroNodeMetadata {
338 macro_generated: Some(true),
339 macro_source: Some("derive_Debug".to_string()),
340 ..Default::default()
341 };
342
343 store.insert(node, metadata.clone());
344 assert_eq!(store.len(), 1);
345 assert!(!store.is_empty());
346
347 let retrieved = store.get(node).unwrap();
348 assert_eq!(retrieved.macro_generated, Some(true));
349 assert_eq!(retrieved.macro_source.as_deref(), Some("derive_Debug"));
350 }
351
352 #[test]
353 fn test_metadata_full_nodeid_key() {
354 let mut store = NodeMetadataStore::new();
355
356 let node_gen1 = NodeId::new(5, 1);
357 let node_gen2 = NodeId::new(5, 2);
358
359 store.insert(
360 node_gen1,
361 MacroNodeMetadata {
362 macro_generated: Some(true),
363 ..Default::default()
364 },
365 );
366
367 assert!(store.get(node_gen2).is_none());
369
370 assert!(store.get(node_gen1).is_some());
372 }
373
374 #[test]
375 fn test_metadata_slot_reuse_no_stale_data() {
376 let mut store = NodeMetadataStore::new();
377
378 let old_node = NodeId::new(5, 1);
380 store.insert(
381 old_node,
382 MacroNodeMetadata {
383 cfg_condition: Some("test".to_string()),
384 ..Default::default()
385 },
386 );
387
388 let new_node = NodeId::new(5, 2);
390
391 assert!(store.get(new_node).is_none());
393
394 assert_eq!(
396 store.get(old_node).unwrap().cfg_condition.as_deref(),
397 Some("test")
398 );
399 }
400
401 #[test]
402 fn test_metadata_store_postcard_roundtrip() {
403 let mut store = NodeMetadataStore::new();
404
405 store.insert(
406 NodeId::new(1, 0),
407 MacroNodeMetadata {
408 macro_generated: Some(true),
409 macro_source: Some("derive_Debug".to_string()),
410 cfg_condition: Some("test".to_string()),
411 cfg_active: Some(true),
412 proc_macro_kind: Some(ProcMacroFunctionKind::Derive),
413 expansion_cached: Some(false),
414 unresolved_attributes: vec!["my_attr".to_string()],
415 },
416 );
417
418 store.insert(
419 NodeId::new(42, 3),
420 MacroNodeMetadata {
421 cfg_condition: Some("feature = \"serde\"".to_string()),
422 ..Default::default()
423 },
424 );
425
426 let bytes = postcard::to_allocvec(&store).expect("serialize");
427 let deserialized: NodeMetadataStore = postcard::from_bytes(&bytes).expect("deserialize");
428
429 assert_eq!(store, deserialized);
430 }
431
432 #[test]
433 fn test_empty_metadata_store_zero_overhead() {
434 let store = NodeMetadataStore::new();
435 let bytes = postcard::to_allocvec(&store).expect("serialize");
436
437 assert!(
439 bytes.len() <= 2,
440 "Empty store should serialize to minimal bytes, got {} bytes",
441 bytes.len()
442 );
443 }
444
445 #[test]
446 fn test_metadata_store_merge() {
447 let mut store1 = NodeMetadataStore::new();
448 let mut store2 = NodeMetadataStore::new();
449
450 store1.insert(
451 NodeId::new(1, 0),
452 MacroNodeMetadata {
453 macro_generated: Some(true),
454 ..Default::default()
455 },
456 );
457
458 store2.insert(
459 NodeId::new(2, 0),
460 MacroNodeMetadata {
461 cfg_condition: Some("test".to_string()),
462 ..Default::default()
463 },
464 );
465
466 store1.merge(&store2);
467 assert_eq!(store1.len(), 2);
468 assert!(store1.get(NodeId::new(1, 0)).is_some());
469 assert!(store1.get(NodeId::new(2, 0)).is_some());
470 }
471
472 #[test]
473 fn test_proc_macro_function_kind_serde() {
474 let kinds = [
475 ProcMacroFunctionKind::Derive,
476 ProcMacroFunctionKind::Attribute,
477 ProcMacroFunctionKind::FunctionLike,
478 ];
479
480 for kind in kinds {
481 let bytes = postcard::to_allocvec(&kind).expect("serialize");
482 let deserialized: ProcMacroFunctionKind =
483 postcard::from_bytes(&bytes).expect("deserialize");
484 assert_eq!(kind, deserialized);
485 }
486 }
487
488 #[test]
489 fn test_metadata_get_or_insert_default() {
490 let mut store = NodeMetadataStore::new();
491 let node = NodeId::new(10, 0);
492
493 let meta = store.get_or_insert_default(node);
495 meta.cfg_condition = Some("test".to_string());
496
497 let meta = store.get(node).unwrap();
499 assert_eq!(meta.cfg_condition.as_deref(), Some("test"));
500 }
501
502 #[test]
503 fn test_metadata_remove() {
504 let mut store = NodeMetadataStore::new();
505 let node = NodeId::new(1, 0);
506
507 store.insert(
508 node,
509 MacroNodeMetadata {
510 macro_generated: Some(true),
511 ..Default::default()
512 },
513 );
514
515 assert!(store.get(node).is_some());
516 let removed = store.remove(node);
517 assert!(removed.is_some());
518 assert!(store.get(node).is_none());
519 assert!(store.is_empty());
520 }
521
522 #[test]
523 fn test_metadata_store_large_scale() {
524 let mut store = NodeMetadataStore::new();
525
526 for i in 0..10_000u32 {
528 store.insert(
529 NodeId::new(i, 0),
530 MacroNodeMetadata {
531 cfg_condition: Some(format!("feature_{i}")),
532 ..Default::default()
533 },
534 );
535 }
536
537 assert_eq!(store.len(), 10_000);
538
539 assert!(store.get(NodeId::new(0, 0)).is_some());
541 assert!(store.get(NodeId::new(5_000, 0)).is_some());
542 assert!(store.get(NodeId::new(9_999, 0)).is_some());
543 assert!(store.get(NodeId::new(10_000, 0)).is_none());
544
545 let bytes = postcard::to_allocvec(&store).expect("serialize");
547 let deserialized: NodeMetadataStore = postcard::from_bytes(&bytes).expect("deserialize");
548 assert_eq!(store, deserialized);
549 }
550
551 #[test]
552 fn test_classpath_metadata_insert_and_get() {
553 let mut store = NodeMetadataStore::new();
554 let node = NodeId::new(100, 0);
555
556 let cp_meta = ClasspathNodeMetadata {
557 coordinates: Some("com.google.guava:guava:33.0.0".to_string()),
558 jar_path: "/home/user/.m2/repository/guava-33.0.0.jar".to_string(),
559 fqn: "com.google.common.collect.ImmutableList".to_string(),
560 is_direct_dependency: true,
561 };
562
563 store.insert_metadata(node, NodeMetadata::Classpath(cp_meta.clone()));
564 assert_eq!(store.len(), 1);
565
566 assert!(store.get(node).is_none());
568
569 let retrieved = store.get_metadata(node).unwrap();
571 match retrieved {
572 NodeMetadata::Classpath(cp) => {
573 assert_eq!(cp.fqn, "com.google.common.collect.ImmutableList");
574 assert_eq!(
575 cp.coordinates.as_deref(),
576 Some("com.google.guava:guava:33.0.0")
577 );
578 assert!(cp.is_direct_dependency);
579 }
580 NodeMetadata::Macro(_) => panic!("expected Classpath variant"),
581 }
582 }
583
584 #[test]
585 fn test_classpath_metadata_postcard_roundtrip() {
586 let mut store = NodeMetadataStore::new();
587
588 store.insert(
590 NodeId::new(1, 0),
591 MacroNodeMetadata {
592 macro_generated: Some(true),
593 ..Default::default()
594 },
595 );
596
597 store.insert_metadata(
598 NodeId::new(2, 0),
599 NodeMetadata::Classpath(ClasspathNodeMetadata {
600 coordinates: Some("org.slf4j:slf4j-api:2.0.0".to_string()),
601 jar_path: "slf4j-api-2.0.0.jar".to_string(),
602 fqn: "org.slf4j.Logger".to_string(),
603 is_direct_dependency: false,
604 }),
605 );
606
607 let bytes = postcard::to_allocvec(&store).expect("serialize");
608 let deserialized: NodeMetadataStore = postcard::from_bytes(&bytes).expect("deserialize");
609 assert_eq!(store, deserialized);
610 assert_eq!(deserialized.len(), 2);
611
612 assert!(deserialized.get(NodeId::new(1, 0)).is_some());
614
615 let cp = deserialized.get_metadata(NodeId::new(2, 0)).unwrap();
617 assert!(matches!(cp, NodeMetadata::Classpath(_)));
618 }
619
620 #[test]
621 fn test_node_metadata_store_json_roundtrip() {
622 let mut store = NodeMetadataStore::new();
623
624 store.insert(
625 NodeId::new(1, 0),
626 MacroNodeMetadata {
627 macro_generated: Some(true),
628 macro_source: Some("serde_derive".to_string()),
629 ..Default::default()
630 },
631 );
632
633 store.insert_metadata(
634 NodeId::new(2, 0),
635 NodeMetadata::Classpath(ClasspathNodeMetadata {
636 coordinates: None,
637 jar_path: "rt.jar".to_string(),
638 fqn: "java.lang.String".to_string(),
639 is_direct_dependency: true,
640 }),
641 );
642
643 let json = serde_json::to_string(&store).unwrap();
644 let deserialized: NodeMetadataStore = serde_json::from_str(&json).unwrap();
645 assert_eq!(store, deserialized);
646 }
647
648 #[test]
649 fn test_iter_all_includes_both_types() {
650 let mut store = NodeMetadataStore::new();
651
652 store.insert(
653 NodeId::new(1, 0),
654 MacroNodeMetadata {
655 macro_generated: Some(true),
656 ..Default::default()
657 },
658 );
659
660 store.insert_metadata(
661 NodeId::new(2, 0),
662 NodeMetadata::Classpath(ClasspathNodeMetadata {
663 coordinates: None,
664 jar_path: "test.jar".to_string(),
665 fqn: "com.example.Test".to_string(),
666 is_direct_dependency: true,
667 }),
668 );
669
670 let macro_entries: Vec<_> = store.iter().collect();
672 assert_eq!(macro_entries.len(), 1);
673
674 let all_entries: Vec<_> = store.iter_all().collect();
676 assert_eq!(all_entries.len(), 2);
677 }
678
679 #[test]
680 fn test_remove_metadata_classpath() {
681 let mut store = NodeMetadataStore::new();
682 let node = NodeId::new(50, 0);
683
684 store.insert_metadata(
685 node,
686 NodeMetadata::Classpath(ClasspathNodeMetadata {
687 coordinates: None,
688 jar_path: "test.jar".to_string(),
689 fqn: "Test".to_string(),
690 is_direct_dependency: true,
691 }),
692 );
693
694 assert_eq!(store.len(), 1);
695
696 let removed = store.remove(node);
698 assert!(removed.is_none());
699 assert!(store.is_empty());
701 }
702
703 #[test]
704 fn test_remove_metadata_typed() {
705 let mut store = NodeMetadataStore::new();
706 let node = NodeId::new(50, 0);
707
708 store.insert_metadata(
709 node,
710 NodeMetadata::Classpath(ClasspathNodeMetadata {
711 coordinates: None,
712 jar_path: "test.jar".to_string(),
713 fqn: "Test".to_string(),
714 is_direct_dependency: true,
715 }),
716 );
717
718 let removed = store.remove_metadata(node);
720 assert!(matches!(removed, Some(NodeMetadata::Classpath(_))));
721 assert!(store.is_empty());
722 }
723}