rjango 0.1.1

A full-stack Rust backend framework inspired by Django
Documentation
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,
}

/// A GenericIPAddressField stores IPv4 or IPv6 addresses.
#[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)
            }
        }
    }
}

/// An IPAddressField stores IPv4 addresses only.
#[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");
    }
}