use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use super::super::node::id::NodeId;
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct MacroNodeMetadata {
pub macro_generated: Option<bool>,
pub macro_source: Option<String>,
pub cfg_condition: Option<String>,
pub cfg_active: Option<bool>,
pub proc_macro_kind: Option<ProcMacroFunctionKind>,
pub expansion_cached: Option<bool>,
pub unresolved_attributes: Vec<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ProcMacroFunctionKind {
Derive,
Attribute,
FunctionLike,
}
#[derive(Debug, Clone, Default)]
pub struct NodeMetadataStore {
entries: HashMap<(u32, u64), MacroNodeMetadata>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
struct NodeMetadataEntry {
index: u32,
generation: u64,
metadata: MacroNodeMetadata,
}
impl Serialize for NodeMetadataStore {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let entries: Vec<NodeMetadataEntry> = self
.entries
.iter()
.map(|(&(index, generation), metadata)| NodeMetadataEntry {
index,
generation,
metadata: metadata.clone(),
})
.collect();
entries.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for NodeMetadataStore {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let entries: Vec<NodeMetadataEntry> = Vec::deserialize(deserializer)?;
let map = entries
.into_iter()
.map(|e| ((e.index, e.generation), e.metadata))
.collect();
Ok(Self { entries: map })
}
}
impl NodeMetadataStore {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn get(&self, node_id: NodeId) -> Option<&MacroNodeMetadata> {
self.entries.get(&(node_id.index(), node_id.generation()))
}
#[must_use]
pub fn get_mut(&mut self, node_id: NodeId) -> Option<&mut MacroNodeMetadata> {
self.entries
.get_mut(&(node_id.index(), node_id.generation()))
}
pub fn insert(&mut self, node_id: NodeId, metadata: MacroNodeMetadata) {
self.entries
.insert((node_id.index(), node_id.generation()), metadata);
}
pub fn get_or_insert_default(&mut self, node_id: NodeId) -> &mut MacroNodeMetadata {
self.entries
.entry((node_id.index(), node_id.generation()))
.or_default()
}
pub fn remove(&mut self, node_id: NodeId) -> Option<MacroNodeMetadata> {
self.entries
.remove(&(node_id.index(), node_id.generation()))
}
#[must_use]
pub fn len(&self) -> usize {
self.entries.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = ((u32, u64), &MacroNodeMetadata)> {
self.entries.iter().map(|(&k, v)| (k, v))
}
pub fn merge(&mut self, other: &NodeMetadataStore) {
for (&key, value) in &other.entries {
self.entries.insert(key, value.clone());
}
}
}
impl PartialEq for NodeMetadataStore {
fn eq(&self, other: &Self) -> bool {
self.entries == other.entries
}
}
impl Eq for NodeMetadataStore {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_metadata_store_basic_operations() {
let mut store = NodeMetadataStore::new();
assert!(store.is_empty());
assert_eq!(store.len(), 0);
let node = NodeId::new(5, 1);
let metadata = MacroNodeMetadata {
macro_generated: Some(true),
macro_source: Some("derive_Debug".to_string()),
..Default::default()
};
store.insert(node, metadata.clone());
assert_eq!(store.len(), 1);
assert!(!store.is_empty());
let retrieved = store.get(node).unwrap();
assert_eq!(retrieved.macro_generated, Some(true));
assert_eq!(retrieved.macro_source.as_deref(), Some("derive_Debug"));
}
#[test]
fn test_metadata_full_nodeid_key() {
let mut store = NodeMetadataStore::new();
let node_gen1 = NodeId::new(5, 1);
let node_gen2 = NodeId::new(5, 2);
store.insert(
node_gen1,
MacroNodeMetadata {
macro_generated: Some(true),
..Default::default()
},
);
assert!(store.get(node_gen2).is_none());
assert!(store.get(node_gen1).is_some());
}
#[test]
fn test_metadata_slot_reuse_no_stale_data() {
let mut store = NodeMetadataStore::new();
let old_node = NodeId::new(5, 1);
store.insert(
old_node,
MacroNodeMetadata {
cfg_condition: Some("test".to_string()),
..Default::default()
},
);
let new_node = NodeId::new(5, 2);
assert!(store.get(new_node).is_none());
assert_eq!(
store.get(old_node).unwrap().cfg_condition.as_deref(),
Some("test")
);
}
#[test]
fn test_metadata_store_postcard_roundtrip() {
let mut store = NodeMetadataStore::new();
store.insert(
NodeId::new(1, 0),
MacroNodeMetadata {
macro_generated: Some(true),
macro_source: Some("derive_Debug".to_string()),
cfg_condition: Some("test".to_string()),
cfg_active: Some(true),
proc_macro_kind: Some(ProcMacroFunctionKind::Derive),
expansion_cached: Some(false),
unresolved_attributes: vec!["my_attr".to_string()],
},
);
store.insert(
NodeId::new(42, 3),
MacroNodeMetadata {
cfg_condition: Some("feature = \"serde\"".to_string()),
..Default::default()
},
);
let bytes = postcard::to_allocvec(&store).expect("serialize");
let deserialized: NodeMetadataStore = postcard::from_bytes(&bytes).expect("deserialize");
assert_eq!(store, deserialized);
}
#[test]
fn test_empty_metadata_store_zero_overhead() {
let store = NodeMetadataStore::new();
let bytes = postcard::to_allocvec(&store).expect("serialize");
assert!(
bytes.len() <= 2,
"Empty store should serialize to minimal bytes, got {} bytes",
bytes.len()
);
}
#[test]
fn test_metadata_store_merge() {
let mut store1 = NodeMetadataStore::new();
let mut store2 = NodeMetadataStore::new();
store1.insert(
NodeId::new(1, 0),
MacroNodeMetadata {
macro_generated: Some(true),
..Default::default()
},
);
store2.insert(
NodeId::new(2, 0),
MacroNodeMetadata {
cfg_condition: Some("test".to_string()),
..Default::default()
},
);
store1.merge(&store2);
assert_eq!(store1.len(), 2);
assert!(store1.get(NodeId::new(1, 0)).is_some());
assert!(store1.get(NodeId::new(2, 0)).is_some());
}
#[test]
fn test_proc_macro_function_kind_serde() {
let kinds = [
ProcMacroFunctionKind::Derive,
ProcMacroFunctionKind::Attribute,
ProcMacroFunctionKind::FunctionLike,
];
for kind in kinds {
let bytes = postcard::to_allocvec(&kind).expect("serialize");
let deserialized: ProcMacroFunctionKind =
postcard::from_bytes(&bytes).expect("deserialize");
assert_eq!(kind, deserialized);
}
}
#[test]
fn test_metadata_get_or_insert_default() {
let mut store = NodeMetadataStore::new();
let node = NodeId::new(10, 0);
let meta = store.get_or_insert_default(node);
meta.cfg_condition = Some("test".to_string());
let meta = store.get(node).unwrap();
assert_eq!(meta.cfg_condition.as_deref(), Some("test"));
}
#[test]
fn test_metadata_remove() {
let mut store = NodeMetadataStore::new();
let node = NodeId::new(1, 0);
store.insert(
node,
MacroNodeMetadata {
macro_generated: Some(true),
..Default::default()
},
);
assert!(store.get(node).is_some());
let removed = store.remove(node);
assert!(removed.is_some());
assert!(store.get(node).is_none());
assert!(store.is_empty());
}
#[test]
fn test_metadata_store_large_scale() {
let mut store = NodeMetadataStore::new();
for i in 0..10_000u32 {
store.insert(
NodeId::new(i, 0),
MacroNodeMetadata {
cfg_condition: Some(format!("feature_{i}")),
..Default::default()
},
);
}
assert_eq!(store.len(), 10_000);
assert!(store.get(NodeId::new(0, 0)).is_some());
assert!(store.get(NodeId::new(5_000, 0)).is_some());
assert!(store.get(NodeId::new(9_999, 0)).is_some());
assert!(store.get(NodeId::new(10_000, 0)).is_none());
let bytes = postcard::to_allocvec(&store).expect("serialize");
let deserialized: NodeMetadataStore = postcard::from_bytes(&bytes).expect("deserialize");
assert_eq!(store, deserialized);
}
}