roowho2_lib/proto/
finger_protocol.rs1use 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}