Skip to main content

rivet_logger/processors/
uid.rs

1use std::io;
2use std::sync::Mutex;
3
4use getrandom::getrandom;
5
6use crate::logger::{BoxError, LogRecord, LogValue, LoggerError, Processor};
7
8pub struct Uid {
9    uid: Mutex<String>,
10}
11
12impl Uid {
13    pub fn new(length: usize) -> Result<Self, LoggerError> {
14        if !(1..=32).contains(&length) {
15            return Err(LoggerError::InvalidProcessorConfig(
16                "The uid length must be an integer between 1 and 32".to_string(),
17            ));
18        }
19
20        Ok(Self {
21            uid: Mutex::new(generate_uid(length)?),
22        })
23    }
24
25    pub fn get_uid(&self) -> String {
26        match self.uid.lock() {
27            Ok(uid) => uid.clone(),
28            Err(poisoned) => poisoned.into_inner().clone(),
29        }
30    }
31}
32
33impl Default for Uid {
34    fn default() -> Self {
35        // SAFETY: The default length of 7 is in the validated accepted range.
36        Self::new(7).expect("default uid length must be valid")
37    }
38}
39
40impl Processor for Uid {
41    fn process(&self, mut record: LogRecord) -> Result<LogRecord, BoxError> {
42        record
43            .extra
44            .insert("uid".to_string(), LogValue::String(self.get_uid()));
45        Ok(record)
46    }
47
48    fn reset(&self) -> Result<(), BoxError> {
49        let mut uid = self
50            .uid
51            .lock()
52            .map_err(|_| Box::new(io::Error::other("uid lock poisoned")) as BoxError)?;
53        let length = uid.len();
54        *uid = generate_uid(length)
55            .map_err(|err| Box::new(io::Error::other(err.to_string())) as BoxError)?;
56        Ok(())
57    }
58}
59
60fn generate_uid(length: usize) -> Result<String, LoggerError> {
61    let mut bytes = vec![0_u8; length.div_ceil(2)];
62    getrandom(&mut bytes).map_err(|err| LoggerError::InvalidProcessorConfig(err.to_string()))?;
63
64    let mut hex = String::with_capacity(bytes.len() * 2);
65    for byte in bytes {
66        let _ = std::fmt::Write::write_fmt(&mut hex, format_args!("{byte:02x}"));
67    }
68    hex.truncate(length);
69
70    Ok(hex)
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76
77    #[test]
78    fn validates_uid_length() {
79        assert!(Uid::new(0).is_err());
80        assert!(Uid::new(33).is_err());
81        assert!(Uid::new(7).is_ok());
82    }
83
84    #[test]
85    fn reset_regenerates_uid() {
86        let processor = Uid::new(8).expect("uid processor should initialize");
87        let first = processor.get_uid();
88        processor.reset().expect("reset should succeed");
89        let second = processor.get_uid();
90
91        assert_eq!(first.len(), 8);
92        assert_eq!(second.len(), 8);
93        assert_ne!(first, second);
94    }
95}