Skip to main content

ballistics_engine/
drag_model.rs

1/// Drag model enum
2#[derive(Debug, Clone, Copy, PartialEq)]
3#[cfg_attr(feature = "cli", derive(clap::ValueEnum))]
4pub enum DragModel {
5    G1,
6    G2,
7    G5,
8    G6,
9    G7,
10    G8,
11    GI,
12    GS,
13    /// British RA 1929 reference drag function (McCoy, Modern Exterior Ballistics).
14    RA4,
15}
16
17impl DragModel {
18    #[allow(clippy::should_implement_trait)] // Returns Option for the established public API.
19    pub fn from_str(s: &str) -> Option<Self> {
20        match s.to_uppercase().as_str() {
21            "G1" => Some(DragModel::G1),
22            "G2" => Some(DragModel::G2),
23            "G5" => Some(DragModel::G5),
24            "G6" => Some(DragModel::G6),
25            "G7" => Some(DragModel::G7),
26            "G8" => Some(DragModel::G8),
27            "GI" => Some(DragModel::GI),
28            "GS" => Some(DragModel::GS),
29            "RA4" => Some(DragModel::RA4),
30            _ => None,
31        }
32    }
33}
34
35impl std::fmt::Display for DragModel {
36    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37        write!(f, "{:?}", self)
38    }
39}
40
41#[cfg(test)]
42mod tests {
43    use super::*;
44
45    #[test]
46    fn test_drag_model_from_str() {
47        // Test valid drag models
48        assert_eq!(DragModel::from_str("G1"), Some(DragModel::G1));
49        assert_eq!(DragModel::from_str("G2"), Some(DragModel::G2));
50        assert_eq!(DragModel::from_str("G5"), Some(DragModel::G5));
51        assert_eq!(DragModel::from_str("G6"), Some(DragModel::G6));
52        assert_eq!(DragModel::from_str("G7"), Some(DragModel::G7));
53        assert_eq!(DragModel::from_str("G8"), Some(DragModel::G8));
54        assert_eq!(DragModel::from_str("GI"), Some(DragModel::GI));
55        assert_eq!(DragModel::from_str("GS"), Some(DragModel::GS));
56        assert_eq!(DragModel::from_str("RA4"), Some(DragModel::RA4));
57    }
58
59    #[test]
60    fn test_drag_model_from_str_case_insensitive() {
61        // Test case insensitivity
62        assert_eq!(DragModel::from_str("g1"), Some(DragModel::G1));
63        assert_eq!(DragModel::from_str("G1"), Some(DragModel::G1));
64        assert_eq!(DragModel::from_str("g7"), Some(DragModel::G7));
65        assert_eq!(DragModel::from_str("G7"), Some(DragModel::G7));
66        assert_eq!(DragModel::from_str("gi"), Some(DragModel::GI));
67        assert_eq!(DragModel::from_str("GI"), Some(DragModel::GI));
68        assert_eq!(DragModel::from_str("Gs"), Some(DragModel::GS));
69        assert_eq!(DragModel::from_str("ra4"), Some(DragModel::RA4));
70        assert_eq!(DragModel::from_str("Ra4"), Some(DragModel::RA4));
71    }
72
73    #[test]
74    fn ra4_from_str_roundtrip() {
75        let model = DragModel::from_str("RA4").expect("RA4 must parse");
76        assert_eq!(model, DragModel::RA4);
77        assert_eq!(format!("{model}"), "RA4");
78        assert_eq!(format!("{model:?}"), "RA4");
79        assert_eq!(DragModel::from_str("ra4"), Some(DragModel::RA4));
80    }
81
82    #[test]
83    fn test_drag_model_from_str_invalid() {
84        // Test invalid inputs
85        assert_eq!(DragModel::from_str("G9"), None);
86        assert_eq!(DragModel::from_str("G10"), None);
87        assert_eq!(DragModel::from_str("X1"), None);
88        assert_eq!(DragModel::from_str(""), None);
89        assert_eq!(DragModel::from_str("invalid"), None);
90        assert_eq!(DragModel::from_str("123"), None);
91    }
92
93    #[test]
94    fn test_drag_model_display() {
95        // Test Display implementation
96        assert_eq!(format!("{}", DragModel::G1), "G1");
97        assert_eq!(format!("{}", DragModel::G2), "G2");
98        assert_eq!(format!("{}", DragModel::G5), "G5");
99        assert_eq!(format!("{}", DragModel::G6), "G6");
100        assert_eq!(format!("{}", DragModel::G7), "G7");
101        assert_eq!(format!("{}", DragModel::G8), "G8");
102        assert_eq!(format!("{}", DragModel::GI), "GI");
103        assert_eq!(format!("{}", DragModel::GS), "GS");
104        assert_eq!(format!("{}", DragModel::RA4), "RA4");
105    }
106
107    #[test]
108    fn test_drag_model_equality() {
109        // Test PartialEq implementation
110        assert_eq!(DragModel::G1, DragModel::G1);
111        assert_eq!(DragModel::G7, DragModel::G7);
112        assert_ne!(DragModel::G1, DragModel::G7);
113        assert_ne!(DragModel::G5, DragModel::G6);
114
115        // Test that from_str produces equal values
116        let g1_from_str = DragModel::from_str("G1").unwrap();
117        assert_eq!(g1_from_str, DragModel::G1);
118    }
119
120    #[test]
121    fn test_drag_model_clone() {
122        // Test Clone implementation
123        let original = DragModel::G7;
124        let cloned = original;
125        assert_eq!(original, cloned);
126
127        // Both should be independent
128        assert_eq!(format!("{}", original), "G7");
129        assert_eq!(format!("{}", cloned), "G7");
130    }
131
132    #[test]
133    fn test_drag_model_copy() {
134        // Test Copy implementation
135        let original = DragModel::G1;
136        let copied = original; // This uses Copy
137        let also_copied = original; // Can still use original
138
139        assert_eq!(original, copied);
140        assert_eq!(original, also_copied);
141        assert_eq!(copied, also_copied);
142    }
143
144    #[test]
145    fn test_drag_model_debug() {
146        // Test Debug implementation
147        assert_eq!(format!("{:?}", DragModel::G1), "G1");
148        assert_eq!(format!("{:?}", DragModel::G7), "G7");
149        assert_eq!(format!("{:?}", DragModel::GI), "GI");
150        assert_eq!(format!("{:?}", DragModel::GS), "GS");
151        assert_eq!(format!("{:?}", DragModel::RA4), "RA4");
152    }
153}