use core::ffi::c_int;
use std::fmt;
use std::mem;
use std::net::Ipv4Addr;
use std::ptr;
use std::slice;
use itertools::Itertools;
use crate::error::{Error, Result};
use crate::record::QueryRecord;
use crate::types::{MAX_ADDRTTLS, QueryType};
use crate::utils::ipv4_from_in_addr;
#[derive(Clone, Copy, Debug)]
pub struct AResults {
naddrttls: usize,
addrttls: [c_ares_sys::ares_addrttl; MAX_ADDRTTLS],
}
#[derive(Clone, Copy)]
pub struct AResult<'a> {
addrttl: &'a c_ares_sys::ares_addrttl,
}
impl AResults {
pub fn parse_from(data: &[u8]) -> Result<AResults> {
let mut addrttls: [c_ares_sys::ares_addrttl; MAX_ADDRTTLS] =
unsafe { mem::MaybeUninit::zeroed().assume_init() };
let mut naddrttls: c_int = MAX_ADDRTTLS as c_int;
let parse_status = unsafe {
c_ares_sys::ares_parse_a_reply(
data.as_ptr(),
data.len() as c_int,
ptr::null_mut(),
addrttls.as_mut_ptr(),
&raw mut naddrttls,
)
};
if parse_status == c_ares_sys::ares_status_t::ARES_SUCCESS as i32 {
Ok(AResults {
naddrttls: naddrttls as usize,
addrttls,
})
} else {
Err(Error::from(parse_status))
}
}
pub fn iter(&self) -> AResultsIter<'_> {
AResultsIter {
addrttls: self.addrttls[0..self.naddrttls].iter(),
}
}
}
impl fmt::Display for AResults {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let results = self.iter().format("}, {");
write!(fmt, "[{{{results}}}]")
}
}
#[derive(Clone, Debug)]
pub struct AResultsIter<'a> {
addrttls: slice::Iter<'a, c_ares_sys::ares_addrttl>,
}
impl<'a> Iterator for AResultsIter<'a> {
type Item = AResult<'a>;
fn next(&mut self) -> Option<Self::Item> {
self.addrttls.next().map(|addrttl| AResult { addrttl })
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.addrttls.size_hint()
}
}
impl ExactSizeIterator for AResultsIter<'_> {}
impl std::iter::FusedIterator for AResultsIter<'_> {}
impl<'a> IntoIterator for &'a AResults {
type Item = AResult<'a>;
type IntoIter = AResultsIter<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl fmt::Debug for AResult<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("AResult")
.field("ipv4", &self.ipv4())
.field("ttl", &self.ttl())
.finish()
}
}
impl AResult<'_> {
pub fn ipv4(self) -> Ipv4Addr {
ipv4_from_in_addr(self.addrttl.ipaddr)
}
pub fn ttl(self) -> i32 {
#[allow(clippy::unnecessary_cast)]
let ttl = self.addrttl.ttl as i32;
ttl
}
}
impl fmt::Display for AResult<'_> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "IPv4: {}, ", self.ipv4())?;
write!(fmt, "TTL: {}", self.ttl())
}
}
impl QueryRecord for AResults {
const QUERY_TYPE: QueryType = QueryType::A;
fn parse(data: &[u8]) -> Result<Self> {
Self::parse_from(data)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_invalid_data() {
let result = AResults::parse_from(&[]);
assert!(result.is_err());
}
#[test]
fn is_send() {
fn assert_send<T: Send>() {}
assert_send::<AResult>();
assert_send::<AResults>();
assert_send::<AResultsIter>();
}
#[test]
fn is_sync() {
fn assert_sync<T: Sync>() {}
assert_sync::<AResult>();
assert_sync::<AResults>();
assert_sync::<AResultsIter>();
}
const TWO_A_RECORDS: &[u8] = &[
0x00, 0x00, 0x81, 0x80, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x07, 0x65, 0x78,
0x61, 0x6d, 0x70, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, 0xc0,
0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x2c, 0x00, 0x04, 0x5d, 0xb8, 0xd8, 0x22,
0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 0x58, 0x00, 0x04, 0x5d, 0xb8, 0xd8,
0x23,
];
#[test]
fn exact_size_iterator() {
let results = AResults::parse_from(TWO_A_RECORDS).unwrap();
let iter = results.iter();
assert_eq!(iter.len(), 2);
}
#[test]
fn fused_iterator() {
let results = AResults::parse_from(TWO_A_RECORDS).unwrap();
let mut iter = results.iter();
assert!(iter.next().is_some());
assert!(iter.next().is_some());
assert!(iter.next().is_none());
assert!(iter.next().is_none());
}
#[test]
fn debug_a_result() {
let results = AResults::parse_from(TWO_A_RECORDS).unwrap();
let result = results.iter().next().unwrap();
let debug = format!("{result:?}");
assert!(debug.contains("AResult"));
assert!(debug.contains("93.184.216.34"));
assert!(debug.contains("300"));
}
#[test]
fn debug_a_results_iter() {
let results = AResults::parse_from(TWO_A_RECORDS).unwrap();
let iter = results.iter();
let debug = format!("{iter:?}");
assert!(debug.contains("AResultsIter"));
}
}