Skip to main content

roowho2_lib/proto/
finger_protocol.rs

1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
4pub struct FingerRequest {
5    long: bool,
6    name: String,
7}
8
9impl FingerRequest {
10    pub fn new(long: bool, name: String) -> Self {
11        Self { long, name }
12    }
13
14    pub fn to_bytes(&self) -> Vec<u8> {
15        let mut result = Vec::new();
16        if self.long {
17            result.extend(b"/W ");
18        }
19
20        result.extend(self.name.as_bytes());
21        result.extend(b"\r\n");
22
23        result
24    }
25
26    pub fn from_bytes(bytes: &[u8]) -> Self {
27        let (long, name) = if &bytes[..3] == b"/W " {
28            (true, &bytes[3..])
29        } else {
30            (false, bytes)
31        };
32
33        let name = match name.strip_suffix(b"\r\n") {
34            Some(new_name) => new_name,
35            None => name,
36        };
37
38        Self::new(long, String::from_utf8_lossy(name).to_string())
39    }
40}
41
42#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
43pub struct FingerResponse(String);
44
45impl FingerResponse {
46    pub fn new(content: String) -> Self {
47        Self(content)
48    }
49
50    pub fn get_inner(&self) -> &str {
51        &self.0
52    }
53
54    pub fn into_inner(self) -> String {
55        self.0
56    }
57
58    pub fn from_bytes(bytes: &[u8]) -> Self {
59        if bytes.is_empty() {
60            return Self(String::new());
61        }
62
63        fn normalize(c: u8) -> u8 {
64            if c == (b'\r' | 0x80) || c == (b'\n' | 0x80) {
65                c & 0x7f
66            } else {
67                c
68            }
69        }
70
71        let normalized: Vec<u8> = bytes
72            .iter()
73            .copied()
74            .map(normalize)
75            .chain(std::iter::once(normalize(*bytes.last().unwrap())))
76            .map_windows(|[a, b]| {
77                if *a == b'\r' && *b == b'\n' {
78                    None
79                } else {
80                    Some(*a)
81                }
82            })
83            .flatten()
84            .collect();
85
86        let result = String::from_utf8_lossy(&normalized).to_string();
87
88        Self(result)
89    }
90
91    pub fn to_bytes(&self) -> Vec<u8> {
92        let mut out = Vec::with_capacity(self.0.len() + 2);
93
94        for &b in self.0.as_bytes() {
95            if b == b'\n' {
96                out.extend_from_slice(b"\r\n");
97            } else {
98                out.push(b);
99            }
100        }
101
102        if !self.0.ends_with('\n') {
103            out.extend_from_slice(b"\r\n");
104        }
105
106        out
107    }
108}
109
110#[cfg(test)]
111mod tests {
112    use super::*;
113
114    #[test]
115    fn test_finger_serialization_roundrip() {
116        let request = FingerRequest::new(true, "alice".to_string());
117        let bytes = request.to_bytes();
118        let deserialized = FingerRequest::from_bytes(&bytes);
119        assert_eq!(request, deserialized);
120
121        let request2 = FingerRequest::new(false, "bob".to_string());
122        let bytes2 = request2.to_bytes();
123        let deserialized2 = FingerRequest::from_bytes(&bytes2);
124        assert_eq!(request2, deserialized2);
125
126        let response = FingerResponse::new("Hello, World!\nThis is a test.\n".to_string());
127        let response_bytes = response.to_bytes();
128        let deserialized_response = FingerResponse::from_bytes(&response_bytes);
129        assert_eq!(response, deserialized_response);
130
131        let response2 = FingerResponse::new("Single line response\n".to_string());
132        let response_bytes2 = response2.to_bytes();
133        let deserialized_response2 = FingerResponse::from_bytes(&response_bytes2);
134        assert_eq!(response2, deserialized_response2);
135    }
136}