waddling_sequences/
metadata.rs

1//! Metadata and documentation for sequences
2//!
3//! This module provides rich metadata for each sequence according to WDP-6 conventions.
4//! Available only when the `metadata` feature is enabled.
5//!
6//! **Reference:** WDP Part 6: Sequence Conventions (WDP-6)
7
8use core::fmt;
9
10#[cfg(test)]
11extern crate std;
12#[cfg(test)]
13use std::prelude::rust_2021::*;
14
15/// Rich metadata for a sequence number according to WDP-6
16///
17/// Provides comprehensive information about a sequence's semantic meaning,
18/// typical usage, and examples based on the Waddling Diagnostic Protocol.
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub struct SequenceMeta {
21    /// The numeric sequence value (001-999)
22    pub number: u16,
23
24    /// The semantic name (e.g., "MISSING", "INVALID")
25    pub name: &'static str,
26
27    /// Human-readable description from WDP-6
28    pub docs: &'static str,
29
30    /// Typical severity level for this sequence
31    pub typical_severity: &'static str,
32
33    /// When to use this sequence (from WDP-6)
34    pub when_to_use: &'static str,
35
36    /// WDP-6 category (e.g., "Input/Data Validation", "State/Lifecycle")
37    pub category: &'static str,
38
39    /// WDP-6 range (e.g., "001-010", "011-020")
40    pub range: &'static str,
41}
42
43impl SequenceMeta {
44    /// Check if this sequence is in the reserved range (001-030)
45    pub const fn is_reserved(&self) -> bool {
46        self.number >= 1 && self.number <= 30
47    }
48
49    /// Check if this sequence is for success/completion (998-999)
50    pub const fn is_success(&self) -> bool {
51        self.number >= 998 && self.number <= 999
52    }
53
54    /// Check if this sequence is available for project-specific use (031-897)
55    pub const fn is_project_specific(&self) -> bool {
56        self.number >= 31 && self.number <= 897
57    }
58}
59
60impl fmt::Display for SequenceMeta {
61    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62        write!(f, "{:03} {} - {}", self.number, self.name, self.docs)
63    }
64}
65
66/// All standard sequences from WDP-6
67///
68/// This registry contains all sequences defined in the WDP-6 specification.
69pub static ALL_SEQUENCES: &[SequenceMeta] = &[
70    // ═══════════════════════════════════════════════════════════════════════════
71    // Input/Data Validation (001-010)
72    // ═══════════════════════════════════════════════════════════════════════════
73    SequenceMeta {
74        number: 1,
75        name: "MISSING",
76        docs: "Required data not provided",
77        typical_severity: "Error",
78        when_to_use: "Parameter missing, field empty, resource not supplied",
79        category: "Input/Data Validation",
80        range: "001-010",
81    },
82    SequenceMeta {
83        number: 2,
84        name: "MISMATCH",
85        docs: "Type or length mismatch",
86        typical_severity: "Error",
87        when_to_use: "Type error, size mismatch, format disagreement",
88        category: "Input/Data Validation",
89        range: "001-010",
90    },
91    SequenceMeta {
92        number: 3,
93        name: "INVALID",
94        docs: "Validation check failed",
95        typical_severity: "Error",
96        when_to_use: "Format invalid, checksum failed, constraint violated",
97        category: "Input/Data Validation",
98        range: "001-010",
99    },
100    SequenceMeta {
101        number: 4,
102        name: "OVERFLOW",
103        docs: "Value too large",
104        typical_severity: "Error",
105        when_to_use: "Buffer overflow, numeric overflow, size exceeded",
106        category: "Input/Data Validation",
107        range: "001-010",
108    },
109    SequenceMeta {
110        number: 5,
111        name: "UNDERFLOW",
112        docs: "Value too small",
113        typical_severity: "Error",
114        when_to_use: "Buffer underflow, numeric underflow, below minimum",
115        category: "Input/Data Validation",
116        range: "001-010",
117    },
118    SequenceMeta {
119        number: 6,
120        name: "OUT_OF_BOUNDS",
121        docs: "Outside valid range",
122        typical_severity: "Error",
123        when_to_use: "Index out of bounds, value outside range",
124        category: "Input/Data Validation",
125        range: "001-010",
126    },
127    SequenceMeta {
128        number: 7,
129        name: "DUPLICATE",
130        docs: "Duplicate entry",
131        typical_severity: "Error",
132        when_to_use: "Unique constraint violation, duplicate key",
133        category: "Input/Data Validation",
134        range: "001-010",
135    },
136    SequenceMeta {
137        number: 8,
138        name: "DENIED",
139        docs: "Permission denied",
140        typical_severity: "Error",
141        when_to_use: "Authorization failed, access forbidden",
142        category: "Input/Data Validation",
143        range: "001-010",
144    },
145    SequenceMeta {
146        number: 9,
147        name: "UNSUPPORTED",
148        docs: "Feature not supported",
149        typical_severity: "Error",
150        when_to_use: "Unsupported operation, not implemented",
151        category: "Input/Data Validation",
152        range: "001-010",
153    },
154    SequenceMeta {
155        number: 10,
156        name: "DEPRECATED",
157        docs: "Feature deprecated",
158        typical_severity: "Warning",
159        when_to_use: "Deprecated API, obsolete feature (warning)",
160        category: "Input/Data Validation",
161        range: "001-010",
162    },
163    // ═══════════════════════════════════════════════════════════════════════════
164    // State/Lifecycle (011-020)
165    // ═══════════════════════════════════════════════════════════════════════════
166    SequenceMeta {
167        number: 11,
168        name: "UNINITIALIZED",
169        docs: "Not initialized",
170        typical_severity: "Error",
171        when_to_use: "Object not initialized, setup incomplete",
172        category: "State/Lifecycle",
173        range: "011-020",
174    },
175    SequenceMeta {
176        number: 12,
177        name: "ALREADY_INIT",
178        docs: "Already initialized",
179        typical_severity: "Error",
180        when_to_use: "Double initialization attempted",
181        category: "State/Lifecycle",
182        range: "011-020",
183    },
184    SequenceMeta {
185        number: 13,
186        name: "CLOSED",
187        docs: "Resource closed",
188        typical_severity: "Error",
189        when_to_use: "Connection closed, file closed, stream ended",
190        category: "State/Lifecycle",
191        range: "011-020",
192    },
193    SequenceMeta {
194        number: 14,
195        name: "CANCELLED",
196        docs: "Operation cancelled",
197        typical_severity: "Error",
198        when_to_use: "User cancelled, timeout cancelled, abort requested",
199        category: "State/Lifecycle",
200        range: "011-020",
201    },
202    SequenceMeta {
203        number: 15,
204        name: "IN_PROGRESS",
205        docs: "Already in progress",
206        typical_severity: "Blocked",
207        when_to_use: "Operation already running, duplicate action",
208        category: "State/Lifecycle",
209        range: "011-020",
210    },
211    SequenceMeta {
212        number: 16,
213        name: "NOT_READY",
214        docs: "Not ready",
215        typical_severity: "Error",
216        when_to_use: "Precondition not met, dependencies unavailable",
217        category: "State/Lifecycle",
218        range: "011-020",
219    },
220    SequenceMeta {
221        number: 17,
222        name: "TIMEOUT",
223        docs: "Operation timed out",
224        typical_severity: "Error",
225        when_to_use: "Deadline exceeded, response timeout",
226        category: "State/Lifecycle",
227        range: "011-020",
228    },
229    SequenceMeta {
230        number: 18,
231        name: "STALE",
232        docs: "Resource stale",
233        typical_severity: "Warning",
234        when_to_use: "Cache stale, data outdated, session expired",
235        category: "State/Lifecycle",
236        range: "011-020",
237    },
238    // ═══════════════════════════════════════════════════════════════════════════
239    // Resource/Storage (021-030)
240    // ═══════════════════════════════════════════════════════════════════════════
241    SequenceMeta {
242        number: 21,
243        name: "NOT_FOUND",
244        docs: "Resource not found",
245        typical_severity: "Error",
246        when_to_use: "File not found, record missing, endpoint 404",
247        category: "Resource/Storage",
248        range: "021-030",
249    },
250    SequenceMeta {
251        number: 22,
252        name: "ALREADY_EXISTS",
253        docs: "Resource already exists",
254        typical_severity: "Error",
255        when_to_use: "File exists, duplicate key, conflict 409",
256        category: "Resource/Storage",
257        range: "021-030",
258    },
259    SequenceMeta {
260        number: 23,
261        name: "CONFLICT",
262        docs: "Version or data conflict",
263        typical_severity: "Error",
264        when_to_use: "Optimistic lock failed, merge conflict",
265        category: "Resource/Storage",
266        range: "021-030",
267    },
268    SequenceMeta {
269        number: 24,
270        name: "LOCKED",
271        docs: "Resource locked",
272        typical_severity: "Blocked",
273        when_to_use: "File locked, row locked, mutex held",
274        category: "Resource/Storage",
275        range: "021-030",
276    },
277    SequenceMeta {
278        number: 25,
279        name: "CORRUPTED",
280        docs: "Data corrupted",
281        typical_severity: "Critical",
282        when_to_use: "Checksum failed, integrity error, malformed data",
283        category: "Resource/Storage",
284        range: "021-030",
285    },
286    SequenceMeta {
287        number: 26,
288        name: "EXHAUSTED",
289        docs: "Resource exhausted",
290        typical_severity: "Critical",
291        when_to_use: "Out of memory, disk full, quota exceeded",
292        category: "Resource/Storage",
293        range: "021-030",
294    },
295    SequenceMeta {
296        number: 27,
297        name: "UNAVAILABLE",
298        docs: "Temporarily unavailable",
299        typical_severity: "Error",
300        when_to_use: "Service down, maintenance mode, circuit breaker open",
301        category: "Resource/Storage",
302        range: "021-030",
303    },
304    SequenceMeta {
305        number: 28,
306        name: "UNREACHABLE",
307        docs: "Cannot reach resource",
308        typical_severity: "Error",
309        when_to_use: "Network unreachable, DNS failed, host down",
310        category: "Resource/Storage",
311        range: "021-030",
312    },
313    SequenceMeta {
314        number: 29,
315        name: "DISCONNECTED",
316        docs: "Connection lost",
317        typical_severity: "Error",
318        when_to_use: "Network dropped, peer disconnected",
319        category: "Resource/Storage",
320        range: "021-030",
321    },
322    // ═══════════════════════════════════════════════════════════════════════════
323    // Success/Completion (998-999)
324    // ═══════════════════════════════════════════════════════════════════════════
325    SequenceMeta {
326        number: 998,
327        name: "PARTIAL",
328        docs: "Partial success",
329        typical_severity: "Success",
330        when_to_use: "Some items succeeded, some failed",
331        category: "Success/Completion",
332        range: "998-999",
333    },
334    SequenceMeta {
335        number: 999,
336        name: "COMPLETE",
337        docs: "Full success",
338        typical_severity: "Success",
339        when_to_use: "All operations completed successfully",
340        category: "Success/Completion",
341        range: "998-999",
342    },
343];
344
345/// Get metadata for a sequence number
346///
347/// # Examples
348/// ```
349/// use waddling_sequences::metadata::get;
350///
351/// let meta = get(1).unwrap();
352/// assert_eq!(meta.name, "MISSING");
353/// assert_eq!(meta.docs, "Required data not provided");
354/// ```
355pub fn get(number: u16) -> Option<&'static SequenceMeta> {
356    ALL_SEQUENCES.iter().find(|m| m.number == number)
357}
358
359/// Get metadata by name (case-insensitive)
360///
361/// # Examples
362/// ```
363/// use waddling_sequences::metadata::get_by_name;
364///
365/// let meta = get_by_name("MISSING").unwrap();
366/// assert_eq!(meta.number, 1);
367///
368/// let meta2 = get_by_name("missing").unwrap(); // case-insensitive
369/// assert_eq!(meta2.number, 1);
370/// ```
371pub fn get_by_name(name: &str) -> Option<&'static SequenceMeta> {
372    ALL_SEQUENCES
373        .iter()
374        .find(|m| m.name.eq_ignore_ascii_case(name))
375}
376
377/// Get all sequences in a specific category
378///
379/// Returns an array of matching sequences. Only available in std or test environments.
380///
381/// # Examples
382/// ```
383/// #[cfg(feature = "metadata")]
384/// use waddling_sequences::metadata::by_category;
385///
386/// #[cfg(feature = "metadata")]
387/// {
388///     let input_validation = by_category("Input/Data Validation");
389///     assert_eq!(input_validation.len(), 10); // 001-010
390/// }
391/// ```
392#[cfg(test)]
393pub fn by_category(category: &str) -> Vec<&'static SequenceMeta> {
394    ALL_SEQUENCES
395        .iter()
396        .filter(|m| m.category == category)
397        .collect()
398}
399
400/// Get all sequences in a specific range
401///
402/// Returns an array of matching sequences. Only available in test environments.
403///
404/// # Examples
405/// ```
406/// #[cfg(feature = "metadata")]
407/// use waddling_sequences::metadata::by_range;
408///
409/// #[cfg(feature = "metadata")]
410/// {
411///     let state_lifecycle = by_range("011-020");
412///     assert_eq!(state_lifecycle.len(), 8); // 011-018
413/// }
414/// ```
415#[cfg(test)]
416pub fn by_range(range: &str) -> Vec<&'static SequenceMeta> {
417    ALL_SEQUENCES.iter().filter(|m| m.range == range).collect()
418}
419
420/// Predefined category constants for convenience
421pub mod categories {
422    /// Input/Data Validation category (001-010)
423    pub const INPUT_VALIDATION: &str = "Input/Data Validation";
424
425    /// State/Lifecycle category (011-020)
426    pub const STATE_LIFECYCLE: &str = "State/Lifecycle";
427
428    /// Resource/Storage category (021-030)
429    pub const RESOURCE_STORAGE: &str = "Resource/Storage";
430
431    /// Success/Completion category (998-999)
432    pub const SUCCESS_COMPLETION: &str = "Success/Completion";
433}
434
435#[cfg(test)]
436mod tests {
437    use super::*;
438
439    #[test]
440    fn test_get_metadata() {
441        let meta = get(1).unwrap();
442        assert_eq!(meta.name, "MISSING");
443        assert_eq!(meta.number, 1);
444        assert_eq!(meta.category, "Input/Data Validation");
445    }
446
447    #[test]
448    fn test_get_by_name() {
449        let meta = get_by_name("MISMATCH").unwrap();
450        assert_eq!(meta.number, 2);
451
452        // Case insensitive
453        let meta2 = get_by_name("mismatch").unwrap();
454        assert_eq!(meta2.number, 2);
455    }
456
457    #[test]
458    fn test_get_by_name_with_underscores() {
459        let meta = get_by_name("OUT_OF_BOUNDS").unwrap();
460        assert_eq!(meta.number, 6);
461
462        let meta2 = get_by_name("NOT_FOUND").unwrap();
463        assert_eq!(meta2.number, 21);
464    }
465
466    #[test]
467    fn test_all_sequences_unique() {
468        for (i, meta1) in ALL_SEQUENCES.iter().enumerate() {
469            for meta2 in ALL_SEQUENCES.iter().skip(i + 1) {
470                assert_ne!(meta1.number, meta2.number);
471                assert_ne!(meta1.name, meta2.name);
472            }
473        }
474    }
475
476    #[test]
477    fn test_by_category() {
478        let input = by_category(categories::INPUT_VALIDATION);
479        assert_eq!(input.len(), 10);
480
481        let state = by_category(categories::STATE_LIFECYCLE);
482        assert_eq!(state.len(), 8);
483
484        let resource = by_category(categories::RESOURCE_STORAGE);
485        assert_eq!(resource.len(), 9);
486
487        let success = by_category(categories::SUCCESS_COMPLETION);
488        assert_eq!(success.len(), 2);
489    }
490
491    #[test]
492    fn test_sequence_flags() {
493        let missing = get(1).unwrap();
494        assert!(missing.is_reserved());
495        assert!(!missing.is_success());
496        assert!(!missing.is_project_specific());
497
498        let complete = get(999).unwrap();
499        assert!(!complete.is_reserved());
500        assert!(complete.is_success());
501        assert!(!complete.is_project_specific());
502    }
503
504    #[test]
505    fn test_wdp6_sequences_present() {
506        // Test all WDP-6 defined sequences are present
507        use std::vec::Vec;
508        let expected = Vec::from([
509            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, // Input/Data Validation
510            11, 12, 13, 14, 15, 16, 17, 18, // State/Lifecycle
511            21, 22, 23, 24, 25, 26, 27, 28, 29, // Resource/Storage
512            998, 999, // Success/Completion
513        ]);
514
515        for num in expected {
516            assert!(get(num).is_some(), "Sequence {} should be present", num);
517        }
518    }
519}