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 }, } 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 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> { 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); impl From 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> { 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 { 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 })) } } }