Skip to main content

bindizr_core/model/
record.rs

1use chrono::{DateTime, Utc};
2use serde::Serialize;
3use sqlx::FromRow;
4
5#[derive(Debug, PartialEq, Eq, Clone, FromRow)]
6pub struct Record {
7    pub id: i32,
8    pub name: String, // domain name (e.g.: "www.example.com")
9    #[sqlx(try_from = "String")]
10    pub record_type: RecordType, // record type
11    pub value: String, // record value (e.g.: IP address, CNAME, etc.)
12    pub ttl: Option<i32>, // TTL (seconds)
13    pub priority: Option<i32>, // priority (for MX and SRV records)
14    pub created_at: DateTime<Utc>,
15    pub zone_id: i32,
16}
17
18#[derive(Debug, PartialEq, Eq, Clone, FromRow)]
19pub struct RecordWithZone {
20    pub id: i32,
21    pub name: String,
22    #[sqlx(try_from = "String")]
23    pub record_type: RecordType,
24    pub value: String,
25    pub ttl: Option<i32>,
26    pub priority: Option<i32>,
27    pub created_at: DateTime<Utc>,
28    pub zone_id: i32,
29    pub zone_name: String,
30}
31
32impl RecordWithZone {
33    pub fn new(record: Record, zone_name: String) -> Self {
34        Self {
35            id: record.id,
36            name: record.name,
37            record_type: record.record_type,
38            value: record.value,
39            ttl: record.ttl,
40            priority: record.priority,
41            created_at: record.created_at,
42            zone_id: record.zone_id,
43            zone_name,
44        }
45    }
46
47    pub fn record(&self) -> Record {
48        Record {
49            id: self.id,
50            name: self.name.clone(),
51            record_type: self.record_type.clone(),
52            value: self.value.clone(),
53            ttl: self.ttl,
54            priority: self.priority,
55            created_at: self.created_at,
56            zone_id: self.zone_id,
57        }
58    }
59}
60
61#[allow(clippy::upper_case_acronyms)]
62#[derive(Debug, PartialEq, Eq, Serialize, Clone)]
63pub enum RecordType {
64    A,
65    AAAA,
66    CNAME,
67    MX,
68    TXT,
69    NS,
70    SOA,
71    SRV,
72    PTR,
73}
74impl std::fmt::Display for RecordType {
75    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
76        write!(f, "{}", self.as_str())
77    }
78}
79impl TryFrom<String> for RecordType {
80    type Error = String;
81    fn try_from(s: String) -> Result<Self, Self::Error> {
82        s.parse()
83    }
84}
85
86impl std::str::FromStr for RecordType {
87    type Err = String;
88
89    fn from_str(s: &str) -> Result<Self, Self::Err> {
90        match s.to_uppercase().as_str() {
91            "A" => Ok(RecordType::A),
92            "AAAA" => Ok(RecordType::AAAA),
93            "CNAME" => Ok(RecordType::CNAME),
94            "MX" => Ok(RecordType::MX),
95            "TXT" => Ok(RecordType::TXT),
96            "NS" => Ok(RecordType::NS),
97            "SOA" => Ok(RecordType::SOA),
98            "SRV" => Ok(RecordType::SRV),
99            "PTR" => Ok(RecordType::PTR),
100            _ => Err(format!("Invalid record type: {}", s)),
101        }
102    }
103}
104
105impl RecordType {
106    pub fn as_str(&self) -> &str {
107        match self {
108            RecordType::A => "A",
109            RecordType::AAAA => "AAAA",
110            RecordType::CNAME => "CNAME",
111            RecordType::MX => "MX",
112            RecordType::TXT => "TXT",
113            RecordType::NS => "NS",
114            RecordType::SOA => "SOA",
115            RecordType::SRV => "SRV",
116            RecordType::PTR => "PTR",
117        }
118    }
119
120    pub fn is_name_like_value(&self) -> bool {
121        matches!(
122            self,
123            RecordType::CNAME | RecordType::NS | RecordType::PTR | RecordType::MX | RecordType::SRV
124        )
125    }
126}