Logo for Sneaky Crow, LLC (vector art of a crow with a green baseball cap on)

Brain Juice

beautiful rust

There isn't much to this post other than me just admiring how beautiful of a language rust is. I love it so much.

1
use axum::Json;
2
use serde::{Deserialize, Serialize};
3
use serde_json::{json, Value};
4
use std::io::Error;
5
use tracing::{debug, instrument};
6

7
#[derive(Serialize, Deserialize, Debug)]
8
#[serde(tag = "type")]
9
/// Event is the base type for different kinds of inputs and outputs for the processor. Events are
10
/// things like text matching requests, trigger requests, status requests, and whatever else.
11
/// Each type of processable request is in this enumerator
12
pub(crate) enum Event {
13
    Check(TextCount),
14
}
15

16
impl Event {
17
    pub(crate) fn process(self) -> impl Serialize {
18
        match self {
19
            Self::Check(text_count) => text_count.digest().unwrap(),
20
        }
21
    }
22
}
23

24
/// Consume is meant to imply an event is to be processed
25
trait Consume {
26
    /// SerializableResult is generally the data that will be serialized into the axum response body
27
    type SerializableResult: Serialize;
28
    /// collect is for gathering the data of a consumable event, like reading in local json
29
    fn collect(&mut self) -> &mut Self;
30
    /// digest is for digesting the data gathered and processing it, like counting the matches of text
31
    fn digest(self) -> Result<Self::SerializableResult, Error>;
32
}
33

34
/// Trigger is meant to imply an event that is going to take an action, such as sending a POST to a webhook
35
trait Trigger {
36
    /// SerializableResult is generally the data that will be serialized into the axum response body
37
    type SerializableResult: Serialize;
38
    /// dispatch is the action to take when this event (read action) is executed/dispatched
39
    fn dispatch(self) -> Result<Self::SerializableResult, Error>;
40
}
41

42
/// A function to give to the axum router for handling http POST requests for events
43
pub(crate) async fn post_event(Json(payload): Json<Event>) -> Json<Value> {
44
    debug!("Received payload {:?}", payload);
45
    let result = payload.process();
46
    Json(json!(result))
47
}
48

49
#[derive(Serialize, Deserialize, Debug)]
50
/// TextCount is representative of a grouping of patterns of text to check against for each message
51
pub(crate) struct TextCount {
52
    target: String,
53
    patterns: Vec<String>,
54
}
55

56
impl Consume for TextCount {
57
    type SerializableResult = TextCountResult;
58
    #[instrument]
59
    fn collect(&mut self) -> &mut Self {
60
        debug!("Collecting data for text count");
61
        self
62
    }
63

64
    #[instrument]
65
    fn digest(self) -> Result<Self::SerializableResult, Error> {
66
        // This event consumes itself and processes its associated patterns
67
        let mut pattern_results: Vec<PatternResult> = vec![];
68

69
        for pattern in self.patterns {
70
            pattern_results.push(PatternResult::new(pattern, 0))
71
        }
72

73
        let result = TextCountResult::new(self.target, pattern_results);
74
        Ok(result)
75
    }
76
}
77

78
#[derive(Serialize, Deserialize, Debug)]
79
/// PatternResult contains the text that was checked for each message and the amount of times it was found
80
pub(crate) struct PatternResult {
81
    text: String,
82
    count: i32,
83
}
84

85
impl PatternResult {
86
    /// Utility function for generating self
87
    fn new(text: String, count: i32) -> Self {
88
        Self { text, count }
89
    }
90
}
91

92
#[derive(Serialize, Deserialize, Debug)]
93
/// TextCountResult is the processed result of a [Check(TextCount)](Event::Check)
94
pub(crate) struct TextCountResult {
95
    target: String,
96
    patterns: Vec<PatternResult>,
97
}
98

99
impl TextCountResult {
100
    /// Utility function for generating self
101
    fn new(target: String, patterns: Vec<PatternResult>) -> Self {
102
        Self { target, patterns }
103
    }
104
}