nomilo/src/resources/dns/external/record.rs

120 lines
3.1 KiB
Rust
Raw Normal View History

2025-03-25 22:20:36 +00:00
use serde::{Deserialize, Serialize};
use super::rdata::RData;
use crate::resources::dns::internal;
use crate::{errors::Error, validation};
use crate::macros::{append_errors, push_error};
pub enum RecordError {
Validation { suberrors: Vec<Error> },
}
pub enum RecordValidationError {
NotInZone { name: String, zone: String },
}
#[derive(Debug, Deserialize, Serialize)]
pub struct Record {
pub name: String,
pub ttl: u32,
#[serde(flatten)]
pub rdata: RData
}
impl From<internal::Record> for Record {
fn from(value: internal::Record) -> Self {
Record {
name: value.name.to_string(),
ttl: value.ttl,
rdata: value.rdata.into(),
}
}
}
impl Record {
fn validate(self, zone_name: &internal::Name) -> Result<internal::Record, Vec<Error>> {
let mut errors = Vec::new();
let name = push_error!(validation::normalize_domain(&self.name), errors, "/name").map(internal::Name::new);
let name = name.and_then(|name| {
if !name.ends_with(zone_name) {
errors.push(
Error::from(RecordValidationError::NotInZone { name: self.name, zone: zone_name.to_string() })
.with_path("/name")
);
None
} else {
Some(name)
}
});
// TODO: validate ttl
let rdata = append_errors!(self.rdata.validate(), errors, "/rdata");
if errors.is_empty() {
Ok(internal::Record {
name: name.unwrap(),
ttl: self.ttl,
rdata: rdata.unwrap(),
})
} else {
Err(errors)
}
}
}
#[derive(Debug, Deserialize, Serialize)]
pub struct RecordList(pub Vec<Record>);
impl From<internal::RecordList> for RecordList {
fn from(value: internal::RecordList) -> Self {
let records = value.records.into_iter().map(Record::from).collect();
RecordList(records)
}
}
impl RecordList {
fn validate(self, zone_name: &internal::Name) -> Result<internal::RecordList, Vec<Error>> {
let mut errors = Vec::new();
let mut records = Vec::new();
for (index, record) in self.0.into_iter().enumerate() {
let record = append_errors!(record.validate(zone_name), errors, &format!("/{index}"));
if let Some(record) = record {
records.push(record)
}
}
if errors.is_empty() {
Ok(internal::RecordList { records })
} else {
Err(errors)
}
}
}
#[derive(Debug,Deserialize)]
pub struct AddRecords {
pub new_records: RecordList
}
impl AddRecords {
pub fn validate(self, zone_name: &internal::Name) -> Result<internal::AddRecords, Error> {
let mut errors = Vec::new();
let records = append_errors!(self.new_records.validate(zone_name), errors, "/new_records");
if errors.is_empty() {
Ok(internal::AddRecords {
new_records: records.unwrap(),
})
} else {
Err(Error::from(RecordError::Validation { suberrors: errors }))
}
}
}