use hickory_resolver::IntoName;
use hickory_resolver::Name;
use serde::{Deserialize, Serialize};
use crate::resolver::{Error, ResolverResult};
use crate::RecordType;
#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
pub struct UniQuery {
pub(crate) name: Name,
pub(crate) record_type: RecordType,
}
impl UniQuery {
pub fn new<N: IntoName>(name: N, record_type: RecordType) -> ResolverResult<UniQuery> {
let name = name.into_name().map_err(Error::from)?;
Ok(UniQuery { name, record_type })
}
pub fn name(&self) -> &Name {
&self.name
}
pub fn record_type(&self) -> RecordType {
self.record_type
}
}
impl From<UniQuery> for MultiQuery {
fn from(query: UniQuery) -> MultiQuery {
MultiQuery {
names: vec![query.name],
record_types: vec![query.record_type],
}
}
}
#[derive(Debug, Clone)]
pub struct MultiQuery {
pub(crate) names: Vec<Name>,
pub(crate) record_types: Vec<RecordType>,
}
impl MultiQuery {
pub fn new<N: IntoName, S: IntoIterator<Item = N>, T: IntoIterator<Item = RecordType>>(
names: S,
record_types: T,
) -> ResolverResult<MultiQuery> {
let names: Vec<_> = names
.into_iter()
.map(|name| name.into_name().map_err(Error::from))
.collect();
let names: ResolverResult<Vec<_>> = names.into_iter().collect();
let names = names?;
let record_types = record_types.into_iter().collect();
Ok(MultiQuery { names, record_types })
}
pub fn single<N: IntoName>(name: N, record_type: RecordType) -> ResolverResult<MultiQuery> {
MultiQuery::new(vec![name], vec![record_type])
}
pub fn multi_name<N: IntoName, S: IntoIterator<Item = N>>(
names: S,
record_type: RecordType,
) -> ResolverResult<MultiQuery> {
MultiQuery::new(names, vec![record_type])
}
pub fn multi_record<N: IntoName, T: IntoIterator<Item = RecordType>>(
name: N,
record_types: T,
) -> ResolverResult<MultiQuery> {
MultiQuery::new(vec![name], record_types)
}
pub fn into_uni_queries(self) -> Vec<UniQuery> {
let mut queries = Vec::new();
for name in self.names.iter() {
for record_type in self.record_types.iter() {
queries.push(UniQuery {
name: name.clone(),
record_type: *record_type,
});
}
}
queries
}
pub fn num_names(&self) -> usize {
self.names.len()
}
pub fn num_record_types(&self) -> usize {
self.record_types.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::RecordType;
#[test]
fn uni_query_new_and_accessors() {
let q = UniQuery::new("example.com.", RecordType::A).unwrap();
assert_eq!(q.name().to_utf8(), "example.com.");
assert_eq!(q.record_type(), RecordType::A);
}
#[test]
fn uni_query_invalid_name() {
let long_label = "a".repeat(64);
let result = UniQuery::new(long_label.as_str(), RecordType::A);
assert!(result.is_err());
}
#[test]
fn multi_query_single() {
let mq = MultiQuery::single("example.com.", RecordType::AAAA).unwrap();
assert_eq!(mq.num_names(), 1);
assert_eq!(mq.num_record_types(), 1);
let queries = mq.into_uni_queries();
assert_eq!(queries.len(), 1);
assert_eq!(queries[0].name().to_utf8(), "example.com.");
assert_eq!(queries[0].record_type(), RecordType::AAAA);
}
#[test]
fn multi_query_multi_name() {
let mq = MultiQuery::multi_name(vec!["a.example.com.", "b.example.com."], RecordType::A).unwrap();
assert_eq!(mq.num_names(), 2);
assert_eq!(mq.num_record_types(), 1);
let queries = mq.into_uni_queries();
assert_eq!(queries.len(), 2);
assert_eq!(queries[0].name().to_utf8(), "a.example.com.");
assert_eq!(queries[1].name().to_utf8(), "b.example.com.");
}
#[test]
fn multi_query_multi_record() {
let mq =
MultiQuery::multi_record("example.com.", vec![RecordType::A, RecordType::MX, RecordType::TXT]).unwrap();
assert_eq!(mq.num_names(), 1);
assert_eq!(mq.num_record_types(), 3);
let queries = mq.into_uni_queries();
assert_eq!(queries.len(), 3);
assert_eq!(queries[0].record_type(), RecordType::A);
assert_eq!(queries[1].record_type(), RecordType::MX);
assert_eq!(queries[2].record_type(), RecordType::TXT);
}
#[test]
fn multi_query_cartesian_product() {
let mq = MultiQuery::new(
vec!["a.example.com.", "b.example.com."],
vec![RecordType::A, RecordType::MX, RecordType::TXT],
)
.unwrap();
let queries = mq.into_uni_queries();
assert_eq!(queries.len(), 6);
assert_eq!(queries[0].name().to_utf8(), "a.example.com.");
assert_eq!(queries[0].record_type(), RecordType::A);
assert_eq!(queries[1].name().to_utf8(), "a.example.com.");
assert_eq!(queries[1].record_type(), RecordType::MX);
assert_eq!(queries[2].name().to_utf8(), "a.example.com.");
assert_eq!(queries[2].record_type(), RecordType::TXT);
assert_eq!(queries[3].name().to_utf8(), "b.example.com.");
assert_eq!(queries[3].record_type(), RecordType::A);
assert_eq!(queries[4].name().to_utf8(), "b.example.com.");
assert_eq!(queries[4].record_type(), RecordType::MX);
assert_eq!(queries[5].name().to_utf8(), "b.example.com.");
assert_eq!(queries[5].record_type(), RecordType::TXT);
}
#[test]
fn multi_query_from_uni_query() {
let uq = UniQuery::new("example.com.", RecordType::MX).unwrap();
let mq: MultiQuery = uq.into();
assert_eq!(mq.num_names(), 1);
assert_eq!(mq.num_record_types(), 1);
let queries = mq.into_uni_queries();
assert_eq!(queries.len(), 1);
assert_eq!(queries[0].name().to_utf8(), "example.com.");
assert_eq!(queries[0].record_type(), RecordType::MX);
}
}