Skip to main content

ballistics_engine/
drag_model.rs

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