use crate::core::validators::{
validate_ipv4_address, validate_ipv6_address, validate_ipv46_address,
};
use super::mixins::{FieldCacheMixin, FieldValidationMixin};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GenericIPAddressProtocol {
Both,
Ipv4,
Ipv6,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GenericIPAddressField {
pub protocol: GenericIPAddressProtocol,
pub unpack_ipv4: bool,
pub max_length: usize,
}
impl Default for GenericIPAddressField {
fn default() -> Self {
Self {
protocol: GenericIPAddressProtocol::Both,
unpack_ipv4: false,
max_length: 39,
}
}
}
impl GenericIPAddressField {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn with_protocol(mut self, protocol: GenericIPAddressProtocol) -> Self {
self.protocol = protocol;
self
}
#[must_use]
pub fn unpack_ipv4(mut self) -> Self {
self.unpack_ipv4 = true;
self
}
#[must_use]
pub fn protocol(&self) -> GenericIPAddressProtocol {
self.protocol
}
#[must_use]
pub fn db_type(&self) -> &str {
"VARCHAR"
}
}
impl FieldCacheMixin for GenericIPAddressField {
fn get_cache_name(&self) -> String {
"generic_ip_address_field".to_string()
}
fn is_cached(&self) -> bool {
false
}
}
impl FieldValidationMixin for GenericIPAddressField {
fn validate(&self, value: &str) -> Result<(), String> {
match self.protocol {
GenericIPAddressProtocol::Both => {
validate_ipv46_address(value).map_err(|error| error.message)
}
GenericIPAddressProtocol::Ipv4 => {
validate_ipv4_address(value).map_err(|error| error.message)
}
GenericIPAddressProtocol::Ipv6 => {
validate_ipv6_address(value).map_err(|error| error.message)
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IPAddressField {
pub protocol: GenericIPAddressProtocol,
pub max_length: usize,
}
impl Default for IPAddressField {
fn default() -> Self {
Self {
protocol: GenericIPAddressProtocol::Ipv4,
max_length: 15,
}
}
}
impl IPAddressField {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn protocol(&self) -> GenericIPAddressProtocol {
self.protocol
}
#[must_use]
pub fn max_length(&self) -> usize {
self.max_length
}
#[must_use]
pub fn db_type(&self) -> &str {
"VARCHAR"
}
}
impl FieldCacheMixin for IPAddressField {
fn get_cache_name(&self) -> String {
"ip_address_field".to_string()
}
fn is_cached(&self) -> bool {
false
}
}
impl FieldValidationMixin for IPAddressField {
fn validate(&self, value: &str) -> Result<(), String> {
validate_ipv4_address(value).map_err(|error| error.message)
}
}
#[cfg(test)]
mod tests {
use super::{GenericIPAddressField, GenericIPAddressProtocol, IPAddressField};
#[test]
fn generic_ip_address_field_defaults_to_both_protocols() {
let field = GenericIPAddressField::default();
assert_eq!(field.protocol(), GenericIPAddressProtocol::Both);
assert_eq!(field.max_length, 39);
assert!(!field.unpack_ipv4);
}
#[test]
fn generic_ip_address_field_supports_protocol_variants() {
let ipv6 = GenericIPAddressField::new().with_protocol(GenericIPAddressProtocol::Ipv6);
let ipv4 = GenericIPAddressField::new().with_protocol(GenericIPAddressProtocol::Ipv4);
assert_eq!(ipv6.protocol(), GenericIPAddressProtocol::Ipv6);
assert_eq!(ipv4.protocol(), GenericIPAddressProtocol::Ipv4);
}
#[test]
fn ip_address_field_defaults_to_ipv4() {
let field = IPAddressField::default();
assert_eq!(field.protocol(), GenericIPAddressProtocol::Ipv4);
assert_eq!(field.max_length(), 15);
assert_eq!(field.db_type(), "VARCHAR");
}
}