120 lines
3.1 KiB
Rust
120 lines
3.1 KiB
Rust
|
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 }))
|
||
|
}
|
||
|
}
|
||
|
}
|