1use std::fmt;
2
3#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
12pub enum IpStack {
13 #[default]
15 None,
16 V4Only,
18 V6Only,
20 DualStack,
22}
23
24impl IpStack {
25 pub(crate) const fn from_flags(have_v4: bool, have_v6: bool) -> Self {
27 match (have_v4, have_v6) {
28 (true, true) => IpStack::DualStack,
29 (true, false) => IpStack::V4Only,
30 (false, true) => IpStack::V6Only,
31 (false, false) => IpStack::None,
32 }
33 }
34
35 pub const fn name(&self) -> &'static str {
40 match self {
41 IpStack::None => "None",
42 IpStack::V4Only => "V4Only",
43 IpStack::V6Only => "V6Only",
44 IpStack::DualStack => "DualStack",
45 }
46 }
47
48 pub const fn has_ipv4(&self) -> bool {
51 matches!(self, IpStack::V4Only | IpStack::DualStack)
52 }
53
54 pub const fn has_ipv6(&self) -> bool {
57 matches!(self, IpStack::V6Only | IpStack::DualStack)
58 }
59
60 pub const fn is_dual_stack(&self) -> bool {
62 matches!(self, IpStack::DualStack)
63 }
64}
65
66impl fmt::Display for IpStack {
67 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70 f.write_str(self.name())
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use super::IpStack;
77
78 #[test]
79 fn from_flags_covers_all_combinations() {
80 assert_eq!(IpStack::from_flags(false, false), IpStack::None);
81 assert_eq!(IpStack::from_flags(true, false), IpStack::V4Only);
82 assert_eq!(IpStack::from_flags(false, true), IpStack::V6Only);
83 assert_eq!(IpStack::from_flags(true, true), IpStack::DualStack);
84 }
85
86 #[test]
87 fn default_is_none() {
88 assert_eq!(IpStack::default(), IpStack::None);
89 }
90
91 #[test]
92 fn has_ipv4_is_true_only_for_v4_and_dual() {
93 assert!(!IpStack::None.has_ipv4());
94 assert!(IpStack::V4Only.has_ipv4());
95 assert!(!IpStack::V6Only.has_ipv4());
96 assert!(IpStack::DualStack.has_ipv4());
97 }
98
99 #[test]
100 fn has_ipv6_is_true_only_for_v6_and_dual() {
101 assert!(!IpStack::None.has_ipv6());
102 assert!(!IpStack::V4Only.has_ipv6());
103 assert!(IpStack::V6Only.has_ipv6());
104 assert!(IpStack::DualStack.has_ipv6());
105 }
106
107 #[test]
108 fn is_dual_stack_is_true_only_for_dual() {
109 assert!(!IpStack::None.is_dual_stack());
110 assert!(!IpStack::V4Only.is_dual_stack());
111 assert!(!IpStack::V6Only.is_dual_stack());
112 assert!(IpStack::DualStack.is_dual_stack());
113 }
114
115 #[test]
116 fn name_and_display_agree() {
117 for variant in [
118 IpStack::None,
119 IpStack::V4Only,
120 IpStack::V6Only,
121 IpStack::DualStack,
122 ] {
123 assert_eq!(variant.name(), variant.to_string());
124 }
125 assert_eq!(IpStack::DualStack.name(), "DualStack");
126 }
127
128 #[test]
129 fn serde_round_trips() {
130 for variant in [
131 IpStack::None,
132 IpStack::V4Only,
133 IpStack::V6Only,
134 IpStack::DualStack,
135 ] {
136 }
137 }
138}