///|
pub enum Ipv4Scope {
  Private
  Loopback
  LinkLocal
  Multicast
  Documentation
  CarrierGradeNat
  Benchmark
  LimitedBroadcast
  Unspecified
  Reserved
  Public
} derive(Eq, Debug)

///|
pub fn Ipv4Scope::to_string(self : Ipv4Scope) -> String {
  match self {
    Private => "private"
    Loopback => "loopback"
    LinkLocal => "link-local"
    Multicast => "multicast"
    Documentation => "documentation"
    CarrierGradeNat => "carrier-grade-nat"
    Benchmark => "benchmark"
    LimitedBroadcast => "limited-broadcast"
    Unspecified => "unspecified"
    Reserved => "reserved"
    Public => "public"
  }
}

///|
pub fn Ipv4Scope::private() -> Ipv4Scope {
  Private
}

///|
pub fn Ipv4Scope::loopback() -> Ipv4Scope {
  Loopback
}

///|
pub fn Ipv4Scope::link_local() -> Ipv4Scope {
  LinkLocal
}

///|
pub fn Ipv4Scope::multicast() -> Ipv4Scope {
  Multicast
}

///|
pub fn Ipv4Scope::documentation() -> Ipv4Scope {
  Documentation
}

///|
pub fn Ipv4Scope::public() -> Ipv4Scope {
  Public
}

///|
pub fn Ipv4Address::scope(self : Ipv4Address) -> Ipv4Scope {
  if self.is_unspecified() {
    Unspecified
  } else if self.is_limited_broadcast() {
    LimitedBroadcast
  } else if self.is_loopback() {
    Loopback
  } else if self.is_private() {
    Private
  } else if self.is_link_local() {
    LinkLocal
  } else if self.is_multicast() {
    Multicast
  } else if self.is_documentation() {
    Documentation
  } else if self.is_carrier_grade_nat() {
    CarrierGradeNat
  } else if self.is_benchmark() {
    Benchmark
  } else if self.is_reserved() {
    Reserved
  } else {
    Public
  }
}

///|
pub fn Ipv4Address::is_unspecified(self : Ipv4Address) -> Bool {
  self.value == 0U
}

///|
pub fn Ipv4Address::is_limited_broadcast(self : Ipv4Address) -> Bool {
  self.value == 4294967295U
}

///|
pub fn Ipv4Address::is_loopback(self : Ipv4Address) -> Bool {
  must_ipv4_network("127.0.0.0/8").contains(self)
}

///|
pub fn Ipv4Address::is_private(self : Ipv4Address) -> Bool {
  must_ipv4_network("10.0.0.0/8").contains(self) ||
  must_ipv4_network("172.16.0.0/12").contains(self) ||
  must_ipv4_network("192.168.0.0/16").contains(self)
}

///|
pub fn Ipv4Address::is_link_local(self : Ipv4Address) -> Bool {
  must_ipv4_network("169.254.0.0/16").contains(self)
}

///|
pub fn Ipv4Address::is_multicast(self : Ipv4Address) -> Bool {
  must_ipv4_network("224.0.0.0/4").contains(self)
}

///|
pub fn Ipv4Address::is_documentation(self : Ipv4Address) -> Bool {
  must_ipv4_network("192.0.2.0/24").contains(self) ||
  must_ipv4_network("198.51.100.0/24").contains(self) ||
  must_ipv4_network("203.0.113.0/24").contains(self)
}

///|
pub fn Ipv4Address::is_carrier_grade_nat(self : Ipv4Address) -> Bool {
  must_ipv4_network("100.64.0.0/10").contains(self)
}

///|
pub fn Ipv4Address::is_benchmark(self : Ipv4Address) -> Bool {
  must_ipv4_network("198.18.0.0/15").contains(self)
}

///|
pub fn Ipv4Address::is_reserved(self : Ipv4Address) -> Bool {
  must_ipv4_network("240.0.0.0/4").contains(self) &&
  !self.is_limited_broadcast()
}

///|
pub fn Ipv4Address::is_global_unicast(self : Ipv4Address) -> Bool {
  self.scope() == Public
}

///|
pub fn Ipv4Address::is_special_purpose(self : Ipv4Address) -> Bool {
  self.scope() != Public
}

///|
pub fn Ipv4Network::scope(self : Ipv4Network) -> Ipv4Scope {
  if self.contains_network(must_ipv4_network("0.0.0.0/32")) && self.prefix == 32 {
    Unspecified
  } else {
    self.address.scope()
  }
}

///|
fn must_ipv4_network(text : String) -> Ipv4Network {
  Ipv4Network::parse(text).unwrap()
}