use std::fmt::Display;
use std::io;
use std::vec::Vec;
use bytes::Bytes;
use crate::base::iana::Class;
use crate::base::{Name, Rtype, ToName};
use crate::zonefile::inplace;
use super::types::{StoredName, StoredRecord};
#[derive(Clone, Copy, Debug)]
pub enum ZoneCutError {
OutOfZone,
ZoneCutAtApex,
}
impl From<OutOfZone> for ZoneCutError {
fn from(_: OutOfZone) -> ZoneCutError {
ZoneCutError::OutOfZone
}
}
impl Display for ZoneCutError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
ZoneCutError::OutOfZone => write!(f, "Out of zone"),
ZoneCutError::ZoneCutAtApex => write!(f, "Zone cut at apex"),
}
}
}
#[derive(Clone, Copy, Debug)]
pub enum CnameError {
OutOfZone,
CnameAtApex,
}
impl From<OutOfZone> for CnameError {
fn from(_: OutOfZone) -> CnameError {
CnameError::OutOfZone
}
}
impl Display for CnameError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
CnameError::OutOfZone => write!(f, "Out of zone"),
CnameError::CnameAtApex => write!(f, "CNAME at apex"),
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct OutOfZone;
#[derive(Clone, Debug)]
pub enum RecordError {
ClassMismatch(StoredRecord, Class),
IllegalZoneCut(StoredRecord, Rtype),
IllegalRecord(StoredRecord, Rtype),
IllegalCname(StoredRecord, Rtype),
MultipleCnames(StoredRecord),
MalformedRecord(inplace::Error),
InvalidRecord(ContextError),
MissingSoa(StoredRecord),
}
impl RecordError {
pub fn owner(&self) -> Option<&Name<Bytes>> {
match self {
RecordError::ClassMismatch(rec, _)
| RecordError::IllegalZoneCut(rec, _)
| RecordError::IllegalRecord(rec, _)
| RecordError::IllegalCname(rec, _)
| RecordError::MultipleCnames(rec)
| RecordError::MissingSoa(rec) => Some(rec.owner()),
RecordError::MalformedRecord(_)
| RecordError::InvalidRecord(_) => None,
}
}
}
impl Display for RecordError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
RecordError::ClassMismatch(rec, zone_class) => {
write!(f, "The class of the record does not match the class {zone_class} of the zone: {rec}")
}
RecordError::IllegalZoneCut(rec, existing_rtype) => {
write!(f, "Attempted to add zone cut records where non-zone cut records ({existing_rtype}) already exist: {rec}")
}
RecordError::IllegalRecord(rec, existing_rtype) => {
write!(f, "Attempted to add a normal record where a {existing_rtype} record already exists: {rec}")
}
RecordError::IllegalCname(rec, existing_rtype) => {
write!(f, "Attempted to add a CNAME record where a {existing_rtype} record already exists: {rec}")
}
RecordError::MultipleCnames(rec) => {
write!(f, "Attempted to add a CNAME record a CNAME record already exists: {rec}")
}
RecordError::MalformedRecord(err) => {
write!(f, "The record could not be parsed: {err}")
}
RecordError::InvalidRecord(err) => {
write!(f, "The record is parseable but not valid: {err}")
}
RecordError::MissingSoa(rec) => {
write!(f, "The SOA record was not found: {rec}")
}
}
}
}
#[derive(Clone, Debug)]
pub struct ZoneErrors<T> {
errors: Vec<(StoredName, T)>,
}
impl<T> ZoneErrors<T> {
pub fn add_error(&mut self, name: impl ToName, error: T) {
self.errors.push((name.to_name(), error))
}
pub fn unwrap(self) -> Result<(), Self> {
if self.errors.is_empty() {
Ok(())
} else {
Err(self)
}
}
pub fn is_empty(&self) -> bool {
self.errors.is_empty()
}
pub fn len(&self) -> usize {
self.errors.len()
}
}
impl<T> IntoIterator for ZoneErrors<T> {
type Item = (StoredName, T);
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.errors.into_iter()
}
}
impl<T> Default for ZoneErrors<T> {
fn default() -> Self {
Self {
errors: Default::default(),
}
}
}
impl<T: Display> Display for ZoneErrors<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "Zone file errors: [")?;
for err in &self.errors {
write!(f, "'{}': {},", err.0, err.1)?;
}
write!(f, "]")
}
}
#[derive(Clone, Debug)]
pub enum ContextError {
MissingNs,
InvalidZonecut(ZoneCutError),
InvalidCname(CnameError),
OutOfZone(Rtype),
}
impl Display for ContextError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
ContextError::MissingNs => write!(f, "Missing NS"),
ContextError::InvalidZonecut(err) => {
write!(f, "Invalid zone cut: {err}")
}
ContextError::InvalidCname(err) => {
write!(f, "Invalid CNAME: {err}")
}
ContextError::OutOfZone(err) => write!(f, "Out of zone: {err}"),
}
}
}
#[derive(Debug)]
pub enum ZoneTreeModificationError {
ZoneExists,
ZoneDoesNotExist,
Io(io::Error),
}
impl From<io::Error> for ZoneTreeModificationError {
fn from(src: io::Error) -> Self {
ZoneTreeModificationError::Io(src)
}
}
impl From<ZoneTreeModificationError> for io::Error {
fn from(src: ZoneTreeModificationError) -> Self {
match src {
ZoneTreeModificationError::Io(err) => err,
ZoneTreeModificationError::ZoneDoesNotExist => {
io::Error::other("zone does not exist")
}
ZoneTreeModificationError::ZoneExists => {
io::Error::other("zone exists")
}
}
}
}
impl Display for ZoneTreeModificationError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
ZoneTreeModificationError::ZoneExists => {
write!(f, "Zone already exists")
}
ZoneTreeModificationError::ZoneDoesNotExist => {
write!(f, "Zone does not exist")
}
ZoneTreeModificationError::Io(err) => {
write!(f, "Io error: {err}")
}
}
}
}