1use serde::Serialize;
8
9pub trait IdBrand {
14 const BRAND: &'static str;
15}
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
19#[serde(tag = "seg", content = "v")]
20pub enum PathSeg {
21 Field(&'static str),
23 Variant(&'static str),
25 Index(u32),
27 Elem,
29}
30
31#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
33pub struct FieldRef {
34 pub path: Vec<PathSeg>,
35 pub brand: &'static str,
36}
37
38impl FieldRef {
39 pub fn leaf(brand: &'static str) -> Self {
41 Self {
42 path: Vec::new(),
43 brand,
44 }
45 }
46
47 #[must_use]
49 pub fn prefixed(mut self, seg: PathSeg) -> Self {
50 self.path.insert(0, seg);
51 self
52 }
53}
54
55pub trait GetRefs {
62 fn refs() -> Vec<FieldRef>
64 where
65 Self: Sized,
66 {
67 Vec::new()
68 }
69
70 fn visit_ids(&self, f: &mut dyn FnMut(&'static str, u64)) {
72 let _ = f;
73 }
74}
75
76macro_rules! empty_refs {
78 ($($t:ty),* $(,)?) => {
79 $(impl GetRefs for $t {})*
80 };
81}
82
83empty_refs!(
84 bool,
85 u8,
86 u16,
87 u32,
88 u64,
89 usize,
90 i32,
91 i64,
92 f32,
93 f64,
94 core::num::NonZeroU32,
95 core::num::NonZeroU64,
96 String,
97 time::Duration,
98 time::OffsetDateTime,
99 time::PrimitiveDateTime,
100 time::Date,
101 time::Time,
102 std::net::Ipv6Addr,
103 std::net::IpAddr,
104 std::net::SocketAddrV6,
105 uuid::Uuid,
106);
107
108impl<const CAP: usize> GetRefs for [u8; CAP] {}
109impl<const CAP: usize> GetRefs for arrayvec::ArrayString<CAP> {}
110
111#[cfg(feature = "std")]
112impl GetRefs for bytes::Bytes {}
113#[cfg(feature = "std")]
114impl GetRefs for serde_json::Value {}
115#[cfg(feature = "rust_decimal")]
116impl GetRefs for rust_decimal::Decimal {}
117#[cfg(feature = "compact_str")]
118impl GetRefs for compact_str::CompactString {}
119#[cfg(feature = "smol_str")]
120impl GetRefs for smol_str::SmolStr {}
121#[cfg(feature = "solana")]
122impl GetRefs for solana_pubkey::Pubkey {}
123#[cfg(feature = "solana")]
124impl GetRefs for solana_signature::Signature {}
125
126fn elem_refs<T: GetRefs>() -> Vec<FieldRef> {
127 T::refs()
128 .into_iter()
129 .map(|r| r.prefixed(PathSeg::Elem))
130 .collect()
131}
132
133impl<T: GetRefs> GetRefs for Vec<T> {
134 fn refs() -> Vec<FieldRef> {
135 elem_refs::<T>()
136 }
137 fn visit_ids(&self, f: &mut dyn FnMut(&'static str, u64)) {
138 for item in self {
139 item.visit_ids(f);
140 }
141 }
142}
143
144impl<T: GetRefs, const CAP: usize> GetRefs for arrayvec::ArrayVec<T, CAP> {
145 fn refs() -> Vec<FieldRef> {
146 elem_refs::<T>()
147 }
148 fn visit_ids(&self, f: &mut dyn FnMut(&'static str, u64)) {
149 for item in self {
150 item.visit_ids(f);
151 }
152 }
153}
154
155#[cfg(feature = "smallvec")]
156impl<T: GetRefs, const CAP: usize> GetRefs for smallvec::SmallVec<[T; CAP]> {
157 fn refs() -> Vec<FieldRef> {
158 elem_refs::<T>()
159 }
160 fn visit_ids(&self, f: &mut dyn FnMut(&'static str, u64)) {
161 for item in self {
162 item.visit_ids(f);
163 }
164 }
165}
166
167impl<T: GetRefs> GetRefs for Option<T> {
168 fn refs() -> Vec<FieldRef> {
169 elem_refs::<T>()
170 }
171 fn visit_ids(&self, f: &mut dyn FnMut(&'static str, u64)) {
172 if let Some(v) = self {
173 v.visit_ids(f);
174 }
175 }
176}
177
178impl<T: GetRefs> GetRefs for Box<T> {
179 fn refs() -> Vec<FieldRef> {
180 T::refs()
181 }
182 fn visit_ids(&self, f: &mut dyn FnMut(&'static str, u64)) {
183 (**self).visit_ids(f);
184 }
185}
186
187macro_rules! tuple_refs {
188 ($(($($t:ident . $i:tt),+)),+ $(,)?) => {
189 $(
190 impl<$($t: GetRefs),+> GetRefs for ($($t,)+) {
191 fn refs() -> Vec<FieldRef> {
192 let mut out = Vec::new();
193 $(out.extend($t::refs().into_iter().map(|r| r.prefixed(PathSeg::Index($i))));)+
194 out
195 }
196 fn visit_ids(&self, f: &mut dyn FnMut(&'static str, u64)) {
197 $(self.$i.visit_ids(f);)+
198 }
199 }
200 )+
201 };
202}
203
204tuple_refs!((T1.0, T2.1), (T1.0, T2.1, T3.2), (T1.0, T2.1, T3.2, T4.3),);
205
206impl<K: GetRefs, V: GetRefs> GetRefs for std::collections::HashMap<K, V> {
207 fn refs() -> Vec<FieldRef> {
208 elem_refs::<(K, V)>()
209 }
210 fn visit_ids(&self, f: &mut dyn FnMut(&'static str, u64)) {
211 for (k, v) in self {
212 k.visit_ids(f);
213 v.visit_ids(f);
214 }
215 }
216}
217
218impl<K: GetRefs, V: GetRefs> GetRefs for std::collections::BTreeMap<K, V> {
219 fn refs() -> Vec<FieldRef> {
220 elem_refs::<(K, V)>()
221 }
222 fn visit_ids(&self, f: &mut dyn FnMut(&'static str, u64)) {
223 for (k, v) in self {
224 k.visit_ids(f);
225 v.visit_ids(f);
226 }
227 }
228}
229
230#[cfg(test)]
231mod tests {
232 use super::*;
233
234 struct TestBrand;
235 impl IdBrand for TestBrand {
236 const BRAND: &'static str = "TEST";
237 }
238
239 struct FakeId(u64);
241 impl GetRefs for FakeId {
242 fn refs() -> Vec<FieldRef> {
243 vec![FieldRef::leaf(TestBrand::BRAND)]
244 }
245 fn visit_ids(&self, f: &mut dyn FnMut(&'static str, u64)) {
246 f(TestBrand::BRAND, self.0);
247 }
248 }
249
250 #[test]
251 fn refs_leaf_and_prefix() {
252 let r = FieldRef::leaf("TEST").prefixed(PathSeg::Field("a"));
253 assert_eq!(r.path, vec![PathSeg::Field("a")]);
254 assert_eq!(r.brand, "TEST");
255 }
256
257 #[test]
258 fn refs_default_empty() {
259 assert!(u64::refs().is_empty());
260 assert!(String::refs().is_empty());
261 let mut seen = Vec::new();
262 42u64.visit_ids(&mut |b, v| seen.push((b, v)));
263 assert!(seen.is_empty());
264 }
265
266 #[test]
267 fn refs_containers_forward() {
268 let refs = <Vec<Option<FakeId>>>::refs();
269 assert_eq!(refs.len(), 1);
270 assert_eq!(refs[0].path, vec![PathSeg::Elem, PathSeg::Elem]);
271
272 let v: Vec<Option<FakeId>> = vec![Some(FakeId(7)), None, Some(FakeId(9))];
273 let mut seen = Vec::new();
274 v.visit_ids(&mut |b, id| seen.push((b, id)));
275 assert_eq!(seen, vec![("TEST", 7), ("TEST", 9)]);
276 }
277
278 #[test]
279 fn refs_tuple_paths() {
280 let refs = <(u64, FakeId)>::refs();
281 assert_eq!(refs.len(), 1);
282 assert_eq!(refs[0].path, vec![PathSeg::Index(1)]);
283 }
284}