Another project
0

Configure Feed

Select the types of activity you want to include in your feed.

at main 4.1 kB View raw
1use core::num::NonZeroU64; 2 3use serde::{Deserialize, Serialize}; 4 5#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)] 6#[serde(transparent)] 7pub struct WidgetId(NonZeroU64); 8 9const FNV_OFFSET: u64 = 0xcbf2_9ce4_8422_2325; 10const FNV_PRIME: u64 = 0x0000_0100_0000_01b3; 11 12impl WidgetId { 13 pub const ROOT: Self = match NonZeroU64::new(0xB0FE_B0FE_B0FE_B0FE) { 14 Some(n) => Self(n), 15 None => panic!("ROOT seed must be non-zero"), 16 }; 17 18 #[must_use] 19 pub const fn from_raw(raw: NonZeroU64) -> Self { 20 Self(raw) 21 } 22 23 #[must_use] 24 pub const fn raw(self) -> NonZeroU64 { 25 self.0 26 } 27 28 #[must_use] 29 pub fn child(self, key: WidgetKey) -> Self { 30 self.mix(key.as_str(), 0) 31 } 32 33 #[must_use] 34 pub fn child_indexed(self, key: WidgetKey, index: u64) -> Self { 35 self.mix(key.as_str(), index) 36 } 37 38 #[must_use] 39 pub fn child_named(self, key: WidgetKey, name: &str) -> Self { 40 self.child(key).mix(name, 0) 41 } 42 43 fn mix(self, key: &str, index: u64) -> Self { 44 let parent = self.0.get().to_le_bytes(); 45 let suffix = index.to_le_bytes(); 46 let raw = parent 47 .iter() 48 .chain(key.as_bytes().iter()) 49 .chain(suffix.iter()) 50 .fold(FNV_OFFSET, |h, b| { 51 (h ^ u64::from(*b)).wrapping_mul(FNV_PRIME) 52 }) 53 | 1; 54 match NonZeroU64::new(raw) { 55 Some(n) => Self(n), 56 None => panic!("`| 1` forces non-zero"), 57 } 58 } 59} 60 61#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)] 62#[serde(transparent)] 63pub struct WidgetKey(&'static str); 64 65impl WidgetKey { 66 #[must_use] 67 pub const fn new(s: &'static str) -> Self { 68 Self(s) 69 } 70 71 #[must_use] 72 pub const fn as_str(self) -> &'static str { 73 self.0 74 } 75} 76 77#[cfg(test)] 78mod tests { 79 use super::{WidgetId, WidgetKey}; 80 81 const PANEL: WidgetKey = WidgetKey::new("panel"); 82 const ITEM: WidgetKey = WidgetKey::new("item"); 83 84 #[test] 85 fn root_is_stable() { 86 assert_eq!(WidgetId::ROOT, WidgetId::ROOT); 87 } 88 89 #[test] 90 fn child_is_deterministic() { 91 let a = WidgetId::ROOT.child(PANEL); 92 let b = WidgetId::ROOT.child(PANEL); 93 assert_eq!(a, b); 94 } 95 96 #[test] 97 fn child_distinguishes_keys() { 98 let a = WidgetId::ROOT.child(PANEL); 99 let b = WidgetId::ROOT.child(ITEM); 100 assert_ne!(a, b); 101 } 102 103 #[test] 104 fn child_indexed_distinguishes_indices() { 105 let a = WidgetId::ROOT.child_indexed(ITEM, 0); 106 let b = WidgetId::ROOT.child_indexed(ITEM, 1); 107 assert_ne!(a, b); 108 } 109 110 #[test] 111 fn child_path_depends_on_parent() { 112 let a = WidgetId::ROOT.child(PANEL).child(ITEM); 113 let b = WidgetId::ROOT.child(ITEM); 114 assert_ne!(a, b); 115 } 116 117 #[test] 118 fn child_zero_indexed_matches_child_unindexed() { 119 let a = WidgetId::ROOT.child(PANEL); 120 let b = WidgetId::ROOT.child_indexed(PANEL, 0); 121 assert_eq!(a, b); 122 } 123 124 #[test] 125 fn child_named_distinguishes_names() { 126 let a = WidgetId::ROOT.child_named(ITEM, "alpha"); 127 let b = WidgetId::ROOT.child_named(ITEM, "beta"); 128 assert_ne!(a, b); 129 } 130 131 #[test] 132 fn child_named_is_deterministic() { 133 let a = WidgetId::ROOT.child_named(ITEM, "alpha"); 134 let b = WidgetId::ROOT.child_named(ITEM, "alpha"); 135 assert_eq!(a, b); 136 } 137 138 #[test] 139 fn child_named_differs_from_child_indexed() { 140 let a = WidgetId::ROOT.child_named(ITEM, "alpha"); 141 let b = WidgetId::ROOT.child_indexed(ITEM, 0); 142 assert_ne!(a, b); 143 } 144 145 #[test] 146 fn child_value_is_pinned_to_fnv() { 147 let id = WidgetId::ROOT.child(PANEL); 148 let raw = id.raw().get(); 149 assert_eq!(raw & 1, 1, "low bit must be forced on"); 150 assert_eq!(raw, 0x185e_529f_9def_b5c5, "FNV-1a hash must stay frozen"); 151 let other = WidgetId::ROOT.child(ITEM).raw().get(); 152 assert_ne!(raw, other); 153 } 154}