nomilo/src/resources/dns/friendly/base.rs
2025-05-13 20:23:26 +02:00

207 lines
5.3 KiB
Rust

use std::net::IpAddr;
use serde::Serialize;
use serde_json::Value;
use crate::errors::Error;
use crate::macros::{push_error, append_errors, check_type};
use crate::resources::dns::external::rdata::RDataValidationError;
use crate::resources::dns::internal::base::{Name, Text};
use crate::validation;
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum ValueType {
Object,
Array,
String,
Number,
Null,
Bool,
}
impl ValueType {
pub fn from_value(value: &Value) -> ValueType {
match value {
Value::Array(_) => ValueType::Array,
Value::Bool(_) => ValueType::Bool,
Value::Null => ValueType::Null,
Value::Number(_) => ValueType::Number,
Value::Object(_) => ValueType::Object,
Value::String(_) => ValueType::String,
}
}
}
pub enum InputDataError {
TypeError { expected: ValueType, found: ValueType },
MissingValue,
}
pub trait FromValue: Sized {
fn from_value(value: Value) -> Result<Self, Vec<Error>>;
}
impl FromValue for Name {
fn from_value(value: Value) -> Result<Self, Vec<Error>> {
let mut errors = Vec::new();
let value = check_type!(value, String, errors);
if !errors.is_empty() {
return Err(errors);
}
let name = push_error!(
validation::normalize_domain(&value.unwrap()),
errors
);
if errors.is_empty() {
Ok(Name::new(name.unwrap()))
} else {
Err(errors)
}
}
}
impl FromValue for IpAddr {
fn from_value(value: Value) -> Result<Self, Vec<Error>> {
let mut errors = Vec::new();
let address = check_type!(value, String, errors);
let address = if let Some(address) = address {
// TODO: replace with custom validation
push_error!(address.parse::<IpAddr>().map_err(|e| {
Error::from(RDataValidationError::IpAddress { input: address })
.with_cause(&e.to_string())
}), errors)
} else {
None
};
if errors.is_empty() {
Ok(address.unwrap())
} else {
Err(errors)
}
}
}
impl FromValue for u32 {
fn from_value(value: Value) -> Result<Self, Vec<Error>> {
let mut errors = Vec::new();
let number = check_type!(value, Number, errors);
let address = if let Some(number) = number {
push_error!(
number.as_u64()
.ok_or(Error::from(RDataValidationError::Number { min: u32::MIN.into(), max: u32::MAX.into()}))
.and_then(|number| {
u32::try_from(number).map_err(|e| {
Error::from(RDataValidationError::Number { min: u32::MIN.into(), max: u32::MAX.into()})
.with_cause(&e.to_string())
})
}),
errors
)
} else {
None
};
if errors.is_empty() {
Ok(address.unwrap())
} else {
Err(errors)
}
}
}
impl FromValue for u16 {
fn from_value(value: Value) -> Result<Self, Vec<Error>> {
let mut errors = Vec::new();
let number = check_type!(value, Number, errors);
let address = if let Some(number) = number {
push_error!(
number.as_u64()
.ok_or(Error::from(RDataValidationError::Number { min: u16::MIN.into(), max: u16::MAX.into()}))
.and_then(|number| {
u16::try_from(number).map_err(|e| {
Error::from(RDataValidationError::Number { min: u16::MIN.into(), max: u16::MAX.into()})
.with_cause(&e.to_string())
})
}),
errors
)
} else {
None
};
if errors.is_empty() {
Ok(address.unwrap())
} else {
Err(errors)
}
}
}
impl<T: FromValue> FromValue for Vec<T> {
fn from_value(value: serde_json::Value) -> Result<Self, Vec<Error>> {
let mut errors = Vec::new();
let array = check_type!(value, Array, errors);
if !errors.is_empty() {
return Err(errors);
}
let array = array.unwrap();
let mut list = Vec::new();
for (index, item) in array.into_iter().enumerate() {
let res = append_errors!(
T::from_value(item),
errors,
&format!("/{index}")
);
if let Some(item) = res {
list.push(item);
}
}
if errors.is_empty() {
Ok(list)
} else {
Err(errors)
}
}
}
impl FromValue for Text {
fn from_value(value: Value) -> Result<Self, Vec<Error>> {
let mut errors = Vec::new();
let data = check_type!(value, String, errors);
let data = if let Some(data) = data {
append_errors!(validation::parse_txt_data(&data), errors)
} else {
None
};
if errors.is_empty() {
Ok(Text ::new(data.unwrap()))
} else {
Err(errors)
}
}
}