use crate::enums::RecordClass;
use crate::resourcerecord::InternalResourceRecord;
use log::*;
use serde::{Deserialize, Serialize};
use std::fmt::Display;
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::str::from_utf8;
#[derive(Clone, Debug, Default, Deserialize, Serialize, Eq, PartialEq)]
#[serde(rename(serialize = "UPPERCASE"))]
pub struct FileZone {
pub id: Option<i64>,
pub name: String,
#[serde(default = "rname_default")]
pub rname: String,
#[serde(default)]
pub serial: u32,
#[serde(default)]
pub refresh: u32,
#[serde(default)]
pub retry: u32,
#[serde(default)]
pub expire: u32,
#[serde(default)]
pub minimum: u32,
pub records: Vec<FileZoneRecord>,
}
impl FileZone {
pub fn matching_data(&self, cmp: &FileZone) -> bool {
self.expire == cmp.expire
&& self.minimum == cmp.minimum
&& self.name == cmp.name
&& self.refresh == cmp.refresh
&& self.retry == cmp.retry
&& self.rname == cmp.rname
&& self.serial == cmp.serial
}
}
pub fn rname_default() -> String {
String::from("barry.dot.goat")
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct FileZoneRecord {
pub zoneid: Option<i64>,
#[serde(default)]
pub id: Option<i64>,
#[serde(default = "default_record_name")]
pub name: String,
pub rrtype: String,
#[serde(default = "default_record_class")]
pub class: RecordClass,
pub rdata: String,
pub ttl: u32,
}
fn default_record_name() -> String {
String::from("@")
}
fn default_record_class() -> RecordClass {
RecordClass::Internet
}
impl Display for FileZoneRecord {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!(
"FileZoneRecord {{ name={} class={} rrtype={}, ttl={}, zoneid={:#?}, id={:#?}, rdata={} }}",
self.name, self.class, self.rrtype, self.ttl, self.zoneid, self.id, self.rdata
))
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct ZoneRecord {
pub name: Vec<u8>,
pub typerecords: Vec<InternalResourceRecord>,
}
impl Display for ZoneRecord {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!(
"Name: {:?} Name Bytes: {:?} Records: {:?}",
from_utf8(&self.name),
&self.name,
self.typerecords,
))
}
}
pub fn load_zone_from_file(filename: &Path) -> Result<FileZone, String> {
let mut file = match File::open(filename) {
Ok(value) => value,
Err(error) => {
return Err(format!("Failed to open zone file: {:?}", error));
}
};
let mut buf: String = String::new();
file.read_to_string(&mut buf)
.map_err(|err| format!("Failed to read {}: {:?}", &filename.display(), err))?;
let jsonstruct: FileZone = match json5::from_str(&buf) {
Ok(value) => value,
Err(error) => {
let emsg = format!("Failed to read JSON file: {:?}", error);
error!("{}", emsg);
return Err(emsg);
}
};
Ok(jsonstruct)
}
pub fn load_zones(filename: &str) -> Result<Vec<FileZone>, String> {
let mut file = match File::open(filename) {
Ok(value) => value,
Err(error) => {
return Err(format!("Failed to open zone file: {:?}", error));
}
};
let mut buf: String = String::new();
file.read_to_string(&mut buf)
.map_err(|err| format!("Failed to read {}: {:?}", filename, err))?;
let jsonstruct: Result<Vec<FileZone>, String> =
json5::from_str(&buf).map_err(|e| format!("Failed to read JSON file: {e:?}"));
jsonstruct
}