Skip to main content

fprint/
lib.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4pub use macros::*;
5
6use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
7use std::num::NonZeroU64;
8use std::ops::Range;
9
10/// A TypeFingerprint is able to return a string that represents the layout of a type.
11/// It is intended to capture any structure that will affect serialization via Serde.
12pub trait TypeFingerprint {
13    fn fingerprint() -> String;
14}
15
16// Basic types just return themselves as a string.
17macro_rules! impl_fprint_simple {
18    ($($($type: ident)::*),*) => {
19        $(
20            impl TypeFingerprint for $($type)::* {
21                fn fingerprint() -> String { stringify!($($type)::*).to_string() }
22            }
23        )*
24    };
25}
26
27impl_fprint_simple!(
28    bool, i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, f32, f64, str, String,
29    NonZeroU64
30);
31
32// Arrays return their inner type and length (up to a maximum).
33macro_rules! impl_fprint_array {
34    ($n: literal) => {
35        impl<T: TypeFingerprint> TypeFingerprint for [T; $n] {
36            fn fingerprint() -> String {
37                "[".to_string() + &T::fingerprint() + ";" + stringify!($n) + "]"
38            }
39        }
40    };
41}
42
43impl_fprint_array!(0);
44impl_fprint_array!(1);
45impl_fprint_array!(2);
46impl_fprint_array!(3);
47impl_fprint_array!(4);
48impl_fprint_array!(5);
49impl_fprint_array!(6);
50impl_fprint_array!(7);
51impl_fprint_array!(8);
52impl_fprint_array!(9);
53impl_fprint_array!(10);
54impl_fprint_array!(11);
55impl_fprint_array!(12);
56impl_fprint_array!(13);
57impl_fprint_array!(14);
58impl_fprint_array!(15);
59impl_fprint_array!(16);
60impl_fprint_array!(17);
61impl_fprint_array!(18);
62impl_fprint_array!(19);
63impl_fprint_array!(20);
64impl_fprint_array!(21);
65impl_fprint_array!(22);
66impl_fprint_array!(23);
67impl_fprint_array!(24);
68impl_fprint_array!(25);
69impl_fprint_array!(26);
70impl_fprint_array!(27);
71impl_fprint_array!(28);
72impl_fprint_array!(29);
73impl_fprint_array!(30);
74impl_fprint_array!(31);
75impl_fprint_array!(32);
76
77macro_rules! impl_fprint_one_generic {
78    ($($($type: ident)::*),*) => {
79        $(
80            impl<T: TypeFingerprint> TypeFingerprint for $($type)::*<T> {
81                fn fingerprint() -> String {
82                    "".to_owned() + stringify!($($type)::*) + "<" + &T::fingerprint() + ">"
83                }
84            }
85        )*
86    };
87}
88
89impl_fprint_one_generic!(BTreeSet, HashSet, Range, Option, Vec, bit_vec::BitVec);
90
91macro_rules! impl_fprint_two_generic {
92    ($($($type: ident)::*),*) => {
93        $(
94            impl<A: TypeFingerprint, B: TypeFingerprint> TypeFingerprint for $($type)::*<A,B> {
95                fn fingerprint() -> String {
96                    "".to_owned() + stringify!($($type)::*) +
97                        "<" + &A::fingerprint() + "," + &B::fingerprint() + ">"
98                }
99            }
100        )*
101    };
102}
103
104impl_fprint_two_generic!(BTreeMap);
105
106macro_rules! impl_fprint_tuple {
107    (($($type: ident,)*)) => {
108        impl<$($type: TypeFingerprint),*> TypeFingerprint for ($($type,)*) {
109            fn fingerprint() -> String {
110                "(".to_owned() + $(&$type::fingerprint() + "," +)* ")"
111            }
112        }
113    };
114}
115
116impl_fprint_tuple!(());
117impl_fprint_tuple!((A,));
118impl_fprint_tuple!((A, B,));
119impl_fprint_tuple!((A, B, C,));
120impl_fprint_tuple!((A, B, C, D,));
121impl_fprint_tuple!((A, B, C, D, E,));
122impl_fprint_tuple!((A, B, C, D, E, F,));
123impl_fprint_tuple!((A, B, C, D, E, F, G,));
124impl_fprint_tuple!((A, B, C, D, E, F, G, H,));
125impl_fprint_tuple!((A, B, C, D, E, F, G, H, I,));
126
127impl<'a, T: TypeFingerprint + ?Sized> TypeFingerprint for &'a T {
128    fn fingerprint() -> String {
129        "&".to_owned() + &T::fingerprint()
130    }
131}
132
133impl<'a, T: TypeFingerprint + ?Sized> TypeFingerprint for &'a mut T {
134    fn fingerprint() -> String {
135        "&mut".to_owned() + &T::fingerprint()
136    }
137}
138
139impl<T: TypeFingerprint> TypeFingerprint for [T] {
140    fn fingerprint() -> String {
141        "[".to_owned() + &T::fingerprint() + "]"
142    }
143}
144
145impl<T: TypeFingerprint + ?Sized> TypeFingerprint for Box<T> {
146    fn fingerprint() -> String {
147        "Box<".to_owned() + &T::fingerprint() + ">"
148    }
149}
150
151impl<K: TypeFingerprint, V: TypeFingerprint, S> TypeFingerprint for HashMap<K, V, S> {
152    fn fingerprint() -> String {
153        // Serde doesn't store any information about the hash function and all entries are re-hashed
154        // when deserialized.
155        "HashMap<".to_owned() + &K::fingerprint() + "," + &V::fingerprint() + ">"
156    }
157}
158
159#[cfg(test)]
160mod tests {
161    use crate::*;
162    struct Foo {}
163    impl TypeFingerprint for Foo {
164        fn fingerprint() -> String {
165            "Foo".to_string()
166        }
167    }
168
169    #[derive(TypeFingerprint)]
170    struct Bar(Foo);
171
172    // Example
173    #[allow(dead_code)]
174    #[derive(TypeFingerprint)]
175    struct Baz {
176        foo: Foo,
177        bar: Bar,
178        bizz: u64,
179    }
180
181    // Example
182    #[allow(dead_code)]
183    #[derive(TypeFingerprint)]
184    enum Buzz {
185        A(Foo),
186        B(Bar),
187        C(Baz),
188    }
189
190    #[test]
191    fn test_simple() {
192        assert_eq!(u32::fingerprint(), "u32");
193    }
194
195    #[test]
196    fn test_array() {
197        assert_eq!(<[u32; 3]>::fingerprint(), "[u32;3]");
198    }
199
200    #[test]
201    fn test_vec() {
202        assert_eq!(Vec::<[u32; 3]>::fingerprint(), "Vec<[u32;3]>");
203    }
204
205    #[test]
206    fn test_hashmap_and_tuple() {
207        assert_eq!(
208            std::collections::HashMap::<[u32; 3], (bool, [u64; 8])>::fingerprint(),
209            "HashMap<[u32;3],(bool,[u64;8],)>"
210        );
211    }
212
213    #[test]
214    fn test_hand_implemented() {
215        assert_eq!(Foo::fingerprint(), "Foo");
216    }
217
218    #[test]
219    fn test_struct() {
220        assert_eq!(Bar::fingerprint(), "struct {Foo}");
221        assert_eq!(Baz::fingerprint(), "struct {foo:Foo,bar:struct {Foo},bizz:u64}");
222    }
223
224    #[test]
225    fn test_enum() {
226        assert_eq!(
227            Buzz::fingerprint(),
228            "enum {A(Foo),B(struct {Foo}),C(struct {foo:Foo,bar:struct {Foo},bizz:u64})}"
229        );
230    }
231
232    mod custom_serializer {
233        pub fn fingerprint<T>() -> String {
234            "custom_fingerprint".to_string()
235        }
236    }
237
238    #[allow(dead_code)]
239    #[derive(TypeFingerprint)]
240    struct TestWithSerde {
241        #[serde(with = "custom_serializer")]
242        value: u64,
243    }
244
245    #[allow(dead_code)]
246    #[derive(TypeFingerprint)]
247    struct TestWithMultipleSerdeAttrs {
248        #[serde(default, rename = "some_value", with = "custom_serializer")]
249        value: u64,
250    }
251
252    #[allow(dead_code)]
253    #[derive(TypeFingerprint)]
254    struct TestWithListSerdeAttrs {
255        #[serde(rename(serialize = "foo", deserialize = "bar"), with = "custom_serializer")]
256        value: u64,
257    }
258
259    #[test]
260    fn test_serde_with() {
261        assert_eq!(TestWithSerde::fingerprint(), "struct {value:custom_fingerprint}");
262        assert_eq!(TestWithMultipleSerdeAttrs::fingerprint(), "struct {value:custom_fingerprint}");
263        assert_eq!(TestWithListSerdeAttrs::fingerprint(), "struct {value:custom_fingerprint}");
264    }
265}