rama_http_headers/exotic/
x_clacks_overhead.rs1use std::fmt;
4use std::str::FromStr;
5
6use rama_utils::time::now_unix_ms;
7
8use crate::util::HeaderValueString;
9
10#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
11pub struct XClacksOverhead(HeaderValueString);
40
41derive_header! {
42 XClacksOverhead(_),
43 name: X_CLACKS_OVERHEAD
44}
45
46macro_rules! name_list {
47 ($($name:literal),+ $(,)?) => {
48 const NAMES: &[&str] = &[
49 $(
50 concat!("GNU ", $name),
51 )+
52 ];
53 };
54}
55
56name_list![
57 "Karen Sparck Jones",
58 "Grant Imahara",
59 "Douglas Adams",
60 "Ian Murdock",
61 "Sir Terry Pratchett",
62 "Kevin Mitnick",
63 "Radia Perlman",
64 "Sophie Wilson",
65 "Grace Hopper",
66 "Terry Davis",
67 "Paul Allen",
68 "Edsger Dijkstra",
69 "Joe Armstrong",
70 "David Bowie",
71 "Barbara Liskov",
72 "Kris Nova",
73 "Alan Turing",
74 "Sir Clive Sinclair",
75 "Ada Lovelace",
76 "John Conway",
77 "Satoru Iwata",
78 "Dennis Ritchie",
79 "Ruth Bader Ginsburg",
80 "Matt Trout",
81 "Bram Moolenaar",
82 "Aaron Swartz",
83 "Steven Hawking",
84];
85
86impl XClacksOverhead {
87 #[must_use]
91 pub fn new() -> Self {
92 Self::new_with_index(now_unix_ms().wrapping_abs() as usize)
93 }
94
95 #[inline(always)]
96 fn new_with_index(n: usize) -> Self {
97 let index = n % NAMES.len();
98 Self(HeaderValueString::from_static(NAMES[index]))
99 }
100
101 #[must_use]
107 pub const fn from_static(s: &'static str) -> Self {
108 Self(HeaderValueString::from_static(s))
109 }
110
111 pub fn as_str(&self) -> &str {
112 self.0.as_str()
113 }
114}
115
116rama_utils::macros::error::static_str_error! {
117 #[doc = "invalid X-Clacks-Overhead header value"]
118 pub struct InvalidXClacksOverhead;
119}
120
121impl FromStr for XClacksOverhead {
122 type Err = InvalidXClacksOverhead;
123
124 fn from_str(src: &str) -> Result<Self, Self::Err> {
125 HeaderValueString::from_str(src)
126 .map(XClacksOverhead)
127 .map_err(|_e| InvalidXClacksOverhead)
128 }
129}
130
131impl Default for XClacksOverhead {
132 #[inline(always)]
133 fn default() -> Self {
134 Self::new()
135 }
136}
137
138impl fmt::Display for XClacksOverhead {
139 #[inline(always)]
140 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
141 fmt::Display::fmt(&self.0, f)
142 }
143}
144
145#[cfg(test)]
146mod tests {
147 use super::*;
148 use crate::HeaderEncode;
149
150 use ahash::{HashSet, HashSetExt as _};
151
152 fn test_value(value: &XClacksOverhead) -> String {
153 _ = value.encode_to_value();
154
155 let s = value.to_string();
156 _ = XClacksOverhead::from_str(&s).unwrap();
157
158 s
159 }
160
161 #[test]
162 fn test_new() {
163 let value = XClacksOverhead::new();
164 _ = test_value(&value);
165 }
166
167 #[test]
168 fn test_default() {
169 let value = XClacksOverhead::default();
170 _ = test_value(&value);
171 }
172
173 #[test]
174 fn test_random_values() {
175 let mut unique_values = HashSet::new();
176 for index in 0..NAMES.len() * 2 {
177 let value = XClacksOverhead::new_with_index(index);
178 let s = test_value(&value);
179 unique_values.insert(s);
180 }
181 assert_eq!(NAMES.len(), unique_values.len());
182 }
183}