Skip to main content

nntp_proxy/protocol/
codes.rs

1//! NNTP status code constants per RFC 3977 and RFC 4643
2//!
3//! Provides named constants for all standard NNTP response codes,
4//! organized by category (informational, success, continuation, error).
5
6// 1xx - Informational (RFC 3977 §3.2.1.1)
7
8/// Help text follows (RFC 3977 §7.2)
9pub const HELP_TEXT: u16 = 100;
10/// Capability list follows (RFC 3977 §5.2)
11pub const CAPABILITY_LIST: u16 = 101;
12/// Server date and time (RFC 3977 §7.1)
13pub const SERVER_DATE: u16 = 111;
14
15// 2xx - Success (RFC 3977 §3.2.1.2)
16
17/// Server ready, posting allowed (RFC 3977 §5.1.1)
18pub const POSTING_ALLOWED: u16 = 200;
19/// Server ready, no posting (RFC 3977 §5.1.1)
20pub const NO_POSTING: u16 = 201;
21/// Connection closing (RFC 3977 §5.4)
22pub const CONNECTION_CLOSING: u16 = 205;
23/// Group selected (RFC 3977 §6.1.1)
24pub const GROUP_SELECTED: u16 = 211;
25/// Information follows (RFC 3977 §7.6.1)
26pub const INFORMATION_FOLLOWS: u16 = 215;
27/// Article follows (RFC 3977 §6.2.1)
28pub const ARTICLE_FOLLOWS: u16 = 220;
29/// Head follows (RFC 3977 §6.2.2)
30pub const HEAD_FOLLOWS: u16 = 221;
31/// Body follows (RFC 3977 §6.2.3)
32pub const BODY_FOLLOWS: u16 = 222;
33/// Article exists (RFC 3977 §6.2.4)
34pub const ARTICLE_EXISTS: u16 = 223;
35/// Overview follows (RFC 3977 §8.3)
36pub const OVERVIEW_FOLLOWS: u16 = 224;
37/// Headers follow (RFC 3977 §8.5)
38pub const HEADERS_FOLLOW: u16 = 225;
39/// New articles follow (RFC 3977 §7.4)
40pub const NEW_ARTICLES_FOLLOW: u16 = 230;
41/// New groups follow (RFC 3977 §7.3)
42pub const NEW_GROUPS_FOLLOW: u16 = 231;
43/// Authentication accepted (RFC 4643 §2.3)
44pub const AUTH_ACCEPTED: u16 = 281;
45/// Compression active (RFC 8054 §2.2)
46pub const COMPRESSION_ACTIVE: u16 = 206;
47
48// 3xx - Continuation (RFC 3977 §3.2.1.3)
49
50/// Send article to be transferred (RFC 3977 §6.3.1)
51pub const SEND_ARTICLE_TRANSFER: u16 = 335;
52/// Send article to be posted (RFC 3977 §6.3.2)
53pub const SEND_ARTICLE_POST: u16 = 340;
54/// Password required (RFC 4643 §2.3)
55pub const PASSWORD_REQUIRED: u16 = 381;
56/// SASL continue (RFC 4643 §2.4)
57pub const SASL_CONTINUE: u16 = 383;
58
59// 4xx - Temporary errors (RFC 3977 §3.2.1.4)
60
61/// Service temporarily unavailable (RFC 3977 §3.2.1)
62pub const SERVICE_UNAVAILABLE: u16 = 400;
63/// Internal fault (RFC 3977 §3.2.1)
64pub const INTERNAL_FAULT: u16 = 403;
65/// No such newsgroup (RFC 3977 §6.1.1)
66pub const NO_SUCH_GROUP: u16 = 411;
67/// No newsgroup selected (RFC 3977 §6.1.1)
68pub const NO_GROUP_SELECTED: u16 = 412;
69/// No current article selected (RFC 3977 §6.2.4)
70pub const NO_CURRENT_ARTICLE: u16 = 420;
71/// No next article (RFC 3977 §6.3.1)
72pub const NO_NEXT_ARTICLE: u16 = 421;
73/// No previous article (RFC 3977 §6.3.2)
74pub const NO_PREV_ARTICLE: u16 = 422;
75/// No article with that number (RFC 3977 §6.2.1)
76pub const NO_SUCH_ARTICLE_NUMBER: u16 = 423;
77/// No article with that message-id (RFC 3977 §6.2.1)
78pub const NO_SUCH_ARTICLE_ID: u16 = 430;
79/// Authentication required (RFC 4643 §2.3)
80pub const AUTH_REQUIRED: u16 = 480;
81/// Authentication rejected (RFC 4643 §2.3)
82pub const AUTH_REJECTED: u16 = 481;
83/// Authentication out of sequence (RFC 4643 §2.3)
84pub const AUTH_OUT_OF_SEQUENCE: u16 = 482;
85/// Encryption required (RFC 4643 §2.3)
86pub const ENCRYPTION_REQUIRED: u16 = 483;
87
88// 5xx - Permanent errors (RFC 3977 §3.2.1.5)
89
90/// Command not recognized (RFC 3977 §3.2.1)
91pub const COMMAND_NOT_RECOGNIZED: u16 = 500;
92/// Command syntax error (RFC 3977 §3.2.1)
93pub const COMMAND_SYNTAX_ERROR: u16 = 501;
94/// Access denied / no permission (RFC 3977 §3.2.1)
95pub const ACCESS_DENIED: u16 = 502;
96/// Feature not supported (RFC 3977 §3.2.1)
97pub const FEATURE_NOT_SUPPORTED: u16 = 503;
98
99#[cfg(test)]
100mod tests {
101    use super::*;
102
103    #[test]
104    fn test_1xx_informational_range() {
105        assert!((100..200).contains(&HELP_TEXT));
106        assert!((100..200).contains(&CAPABILITY_LIST));
107        assert!((100..200).contains(&SERVER_DATE));
108    }
109
110    #[test]
111    fn test_2xx_success_range() {
112        let codes = [
113            POSTING_ALLOWED,
114            NO_POSTING,
115            CONNECTION_CLOSING,
116            GROUP_SELECTED,
117            INFORMATION_FOLLOWS,
118            ARTICLE_FOLLOWS,
119            HEAD_FOLLOWS,
120            BODY_FOLLOWS,
121            ARTICLE_EXISTS,
122            OVERVIEW_FOLLOWS,
123            HEADERS_FOLLOW,
124            NEW_ARTICLES_FOLLOW,
125            NEW_GROUPS_FOLLOW,
126            AUTH_ACCEPTED,
127            COMPRESSION_ACTIVE,
128        ];
129        for code in codes {
130            assert!((200..300).contains(&code), "Code {code} should be 2xx");
131        }
132    }
133
134    #[test]
135    fn test_3xx_continuation_range() {
136        let codes = [
137            SEND_ARTICLE_TRANSFER,
138            SEND_ARTICLE_POST,
139            PASSWORD_REQUIRED,
140            SASL_CONTINUE,
141        ];
142        for code in codes {
143            assert!((300..400).contains(&code), "Code {code} should be 3xx");
144        }
145    }
146
147    #[test]
148    fn test_4xx_temporary_error_range() {
149        let codes = [
150            SERVICE_UNAVAILABLE,
151            INTERNAL_FAULT,
152            NO_SUCH_GROUP,
153            NO_GROUP_SELECTED,
154            NO_CURRENT_ARTICLE,
155            NO_NEXT_ARTICLE,
156            NO_PREV_ARTICLE,
157            NO_SUCH_ARTICLE_NUMBER,
158            NO_SUCH_ARTICLE_ID,
159            AUTH_REQUIRED,
160            AUTH_REJECTED,
161            AUTH_OUT_OF_SEQUENCE,
162            ENCRYPTION_REQUIRED,
163        ];
164        for code in codes {
165            assert!((400..500).contains(&code), "Code {code} should be 4xx");
166        }
167    }
168
169    #[test]
170    fn test_5xx_permanent_error_range() {
171        let codes = [
172            COMMAND_NOT_RECOGNIZED,
173            COMMAND_SYNTAX_ERROR,
174            ACCESS_DENIED,
175            FEATURE_NOT_SUPPORTED,
176        ];
177        for code in codes {
178            assert!((500..600).contains(&code), "Code {code} should be 5xx");
179        }
180    }
181
182    #[test]
183    fn test_specific_code_values() {
184        // 4xx codes
185        assert_eq!(SERVICE_UNAVAILABLE, 400);
186        assert_eq!(INTERNAL_FAULT, 403);
187        assert_eq!(NO_SUCH_GROUP, 411);
188        assert_eq!(NO_GROUP_SELECTED, 412);
189        assert_eq!(NO_CURRENT_ARTICLE, 420);
190        assert_eq!(NO_NEXT_ARTICLE, 421);
191        assert_eq!(NO_PREV_ARTICLE, 422);
192        assert_eq!(NO_SUCH_ARTICLE_NUMBER, 423);
193        assert_eq!(NO_SUCH_ARTICLE_ID, 430);
194        assert_eq!(AUTH_REQUIRED, 480);
195        assert_eq!(AUTH_REJECTED, 481);
196        assert_eq!(AUTH_OUT_OF_SEQUENCE, 482);
197        assert_eq!(ENCRYPTION_REQUIRED, 483);
198
199        // 5xx codes
200        assert_eq!(COMMAND_NOT_RECOGNIZED, 500);
201        assert_eq!(COMMAND_SYNTAX_ERROR, 501);
202        assert_eq!(ACCESS_DENIED, 502);
203        assert_eq!(FEATURE_NOT_SUPPORTED, 503);
204    }
205}