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