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