nntp_proxy/protocol/
commands.rs

1//! NNTP command construction helpers
2//!
3//! This module provides functions for constructing well-formed NNTP commands
4//! according to RFC 3977 and RFC 4643.
5
6/// QUIT command (RFC 3977 Section 5.4)
7pub const QUIT: &[u8] = b"QUIT\r\n";
8
9/// DATE command (RFC 3977 Section 7.1)
10///
11/// The DATE command returns the server's current date and time in UTC.
12pub const DATE: &[u8] = b"DATE\r\n";
13
14/// Construct AUTHINFO USER command (RFC 4643 Section 2.3)
15///
16/// Returns a properly formatted AUTHINFO USER command with CRLF termination.
17#[inline]
18pub fn authinfo_user(username: &str) -> String {
19    format!("AUTHINFO USER {}\r\n", username)
20}
21
22/// Construct AUTHINFO PASS command (RFC 4643 Section 2.4)
23///
24/// Returns a properly formatted AUTHINFO PASS command with CRLF termination.
25#[inline]
26pub fn authinfo_pass(password: &str) -> String {
27    format!("AUTHINFO PASS {}\r\n", password)
28}
29
30/// Construct ARTICLE command with message-ID (RFC 3977 Section 6.2.1)
31///
32/// Returns a properly formatted ARTICLE command for retrieving an article by message-ID.
33#[inline]
34pub fn article_by_msgid(msgid: &str) -> String {
35    format!("ARTICLE {}\r\n", msgid)
36}
37
38/// Construct BODY command with message-ID (RFC 3977 Section 6.2.3)
39///
40/// Returns a properly formatted BODY command for retrieving article body by message-ID.
41#[inline]
42pub fn body_by_msgid(msgid: &str) -> String {
43    format!("BODY {}\r\n", msgid)
44}
45
46/// Construct HEAD command with message-ID (RFC 3977 Section 6.2.2)
47///
48/// Returns a properly formatted HEAD command for retrieving article headers by message-ID.
49#[inline]
50pub fn head_by_msgid(msgid: &str) -> String {
51    format!("HEAD {}\r\n", msgid)
52}
53
54/// Construct STAT command with message-ID (RFC 3977 Section 6.2.4)
55///
56/// Returns a properly formatted STAT command for checking article existence by message-ID.
57#[inline]
58pub fn stat_by_msgid(msgid: &str) -> String {
59    format!("STAT {}\r\n", msgid)
60}
61
62#[cfg(test)]
63mod tests {
64    use super::*;
65
66    #[test]
67    fn test_quit_command() {
68        assert_eq!(QUIT, b"QUIT\r\n");
69    }
70
71    #[test]
72    fn test_date_command() {
73        assert_eq!(DATE, b"DATE\r\n");
74    }
75
76    #[test]
77    fn test_authinfo_user() {
78        assert_eq!(authinfo_user("testuser"), "AUTHINFO USER testuser\r\n");
79        assert_eq!(authinfo_user(""), "AUTHINFO USER \r\n");
80        assert_eq!(
81            authinfo_user("user with spaces"),
82            "AUTHINFO USER user with spaces\r\n"
83        );
84    }
85
86    #[test]
87    fn test_authinfo_pass() {
88        assert_eq!(authinfo_pass("secret"), "AUTHINFO PASS secret\r\n");
89        assert_eq!(authinfo_pass(""), "AUTHINFO PASS \r\n");
90        assert_eq!(authinfo_pass("p@ssw0rd!"), "AUTHINFO PASS p@ssw0rd!\r\n");
91    }
92
93    #[test]
94    fn test_article_by_msgid() {
95        assert_eq!(
96            article_by_msgid("<test@example.com>"),
97            "ARTICLE <test@example.com>\r\n"
98        );
99        assert_eq!(
100            article_by_msgid("<msg123@news.server.com>"),
101            "ARTICLE <msg123@news.server.com>\r\n"
102        );
103    }
104
105    #[test]
106    fn test_body_by_msgid() {
107        assert_eq!(
108            body_by_msgid("<test@example.com>"),
109            "BODY <test@example.com>\r\n"
110        );
111    }
112
113    #[test]
114    fn test_head_by_msgid() {
115        assert_eq!(
116            head_by_msgid("<test@example.com>"),
117            "HEAD <test@example.com>\r\n"
118        );
119    }
120
121    #[test]
122    fn test_stat_by_msgid() {
123        assert_eq!(
124            stat_by_msgid("<test@example.com>"),
125            "STAT <test@example.com>\r\n"
126        );
127    }
128
129    #[test]
130    fn test_commands_end_with_crlf() {
131        assert!(authinfo_user("test").ends_with("\r\n"));
132        assert!(authinfo_pass("test").ends_with("\r\n"));
133        assert!(article_by_msgid("<test@example.com>").ends_with("\r\n"));
134        assert!(body_by_msgid("<test@example.com>").ends_with("\r\n"));
135        assert!(head_by_msgid("<test@example.com>").ends_with("\r\n"));
136        assert!(stat_by_msgid("<test@example.com>").ends_with("\r\n"));
137    }
138}