1use std::time::Duration;
4
5use sha2::{Digest, Sha256};
6
7#[derive(Clone, Copy, Debug, PartialEq, Eq)]
9pub enum IgnoreField {
10 Status,
12 Headers,
14 Body,
16}
17
18#[derive(Clone, Debug)]
20pub struct ShadowConfig {
21 sample_rate: u32,
23 pub shadow_timeout: Duration,
26 pub ignore: Vec<IgnoreField>,
28}
29
30impl ShadowConfig {
31 pub fn full_sample() -> Self {
33 Self {
34 sample_rate: 100,
35 shadow_timeout: Duration::from_secs(2),
36 ignore: Vec::new(),
37 }
38 }
39
40 #[must_use]
42 pub fn sample_rate(mut self, percent: u32) -> Self {
43 self.sample_rate = percent.min(100);
44 self
45 }
46
47 #[must_use]
49 pub fn shadow_timeout(mut self, d: Duration) -> Self {
50 self.shadow_timeout = d;
51 self
52 }
53
54 #[must_use]
56 pub fn ignore(mut self, field: IgnoreField) -> Self {
57 self.ignore.push(field);
58 self
59 }
60
61 pub fn sample_rate_percent(&self) -> u32 {
63 self.sample_rate
64 }
65
66 pub fn should_shadow(&self, key: &[u8]) -> bool {
70 if self.sample_rate >= 100 {
71 return true;
72 }
73 if self.sample_rate == 0 {
74 return false;
75 }
76 let mut hasher = Sha256::new();
77 hasher.update(key);
78 let digest = hasher.finalize();
79 let bucket = u32::from_be_bytes([digest[0], digest[1], digest[2], digest[3]]) % 100;
80 bucket < self.sample_rate
81 }
82}
83
84impl Default for ShadowConfig {
85 fn default() -> Self {
86 Self::full_sample()
87 }
88}