use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub enum IpStack {
#[default]
None,
V4Only,
V6Only,
DualStack,
}
impl IpStack {
pub(crate) const fn from_flags(have_v4: bool, have_v6: bool) -> Self {
match (have_v4, have_v6) {
(true, true) => IpStack::DualStack,
(true, false) => IpStack::V4Only,
(false, true) => IpStack::V6Only,
(false, false) => IpStack::None,
}
}
pub const fn name(&self) -> &'static str {
match self {
IpStack::None => "None",
IpStack::V4Only => "V4Only",
IpStack::V6Only => "V6Only",
IpStack::DualStack => "DualStack",
}
}
pub const fn has_ipv4(&self) -> bool {
matches!(self, IpStack::V4Only | IpStack::DualStack)
}
pub const fn has_ipv6(&self) -> bool {
matches!(self, IpStack::V6Only | IpStack::DualStack)
}
pub const fn is_dual_stack(&self) -> bool {
matches!(self, IpStack::DualStack)
}
}
impl fmt::Display for IpStack {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.name())
}
}
#[cfg(test)]
mod tests {
use super::IpStack;
#[test]
fn from_flags_covers_all_combinations() {
assert_eq!(IpStack::from_flags(false, false), IpStack::None);
assert_eq!(IpStack::from_flags(true, false), IpStack::V4Only);
assert_eq!(IpStack::from_flags(false, true), IpStack::V6Only);
assert_eq!(IpStack::from_flags(true, true), IpStack::DualStack);
}
#[test]
fn default_is_none() {
assert_eq!(IpStack::default(), IpStack::None);
}
#[test]
fn has_ipv4_is_true_only_for_v4_and_dual() {
assert!(!IpStack::None.has_ipv4());
assert!(IpStack::V4Only.has_ipv4());
assert!(!IpStack::V6Only.has_ipv4());
assert!(IpStack::DualStack.has_ipv4());
}
#[test]
fn has_ipv6_is_true_only_for_v6_and_dual() {
assert!(!IpStack::None.has_ipv6());
assert!(!IpStack::V4Only.has_ipv6());
assert!(IpStack::V6Only.has_ipv6());
assert!(IpStack::DualStack.has_ipv6());
}
#[test]
fn is_dual_stack_is_true_only_for_dual() {
assert!(!IpStack::None.is_dual_stack());
assert!(!IpStack::V4Only.is_dual_stack());
assert!(!IpStack::V6Only.is_dual_stack());
assert!(IpStack::DualStack.is_dual_stack());
}
#[test]
fn name_and_display_agree() {
for variant in [
IpStack::None,
IpStack::V4Only,
IpStack::V6Only,
IpStack::DualStack,
] {
assert_eq!(variant.name(), variant.to_string());
}
assert_eq!(IpStack::DualStack.name(), "DualStack");
}
#[test]
fn serde_round_trips() {
for variant in [
IpStack::None,
IpStack::V4Only,
IpStack::V6Only,
IpStack::DualStack,
] {
let json = serde_json::to_string(&variant).expect("serialize");
let back: IpStack = serde_json::from_str(&json).expect("deserialize");
assert_eq!(variant, back);
}
}
}