use alloc::{
boxed::Box,
string::{String, ToString},
};
use core::fmt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::{
error::*,
rr::{RData, RecordData, RecordType},
serialize::{binary::*, txt::ParseError},
};
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
#[non_exhaustive]
pub struct HINFO {
pub cpu: Box<[u8]>,
pub os: Box<[u8]>,
}
impl HINFO {
pub fn new(cpu: String, os: String) -> Self {
Self {
cpu: cpu.into_bytes().into_boxed_slice(),
os: os.into_bytes().into_boxed_slice(),
}
}
pub fn from_bytes(cpu: Box<[u8]>, os: Box<[u8]>) -> Self {
Self { cpu, os }
}
pub(crate) fn from_tokens<'i, I: Iterator<Item = &'i str>>(
mut tokens: I,
) -> Result<Self, ParseError> {
let cpu = tokens
.next()
.ok_or_else(|| ParseError::MissingToken("cpu".to_string()))
.map(ToString::to_string)?;
let os = tokens
.next()
.ok_or_else(|| ParseError::MissingToken("os".to_string()))
.map(ToString::to_string)?;
Ok(Self::new(cpu, os))
}
}
impl BinEncodable for HINFO {
fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
encoder.emit_character_data(&self.cpu)?;
encoder.emit_character_data(&self.os)?;
Ok(())
}
}
impl<'r> BinDecodable<'r> for HINFO {
fn read(decoder: &mut BinDecoder<'r>) -> Result<Self, DecodeError> {
let cpu = decoder.read_character_data()?
.unverified()
.to_vec()
.into_boxed_slice();
let os = decoder.read_character_data()?
.unverified()
.to_vec()
.into_boxed_slice();
Ok(Self { cpu, os })
}
}
impl RecordData for HINFO {
fn try_borrow(data: &RData) -> Option<&Self> {
match data {
RData::HINFO(csync) => Some(csync),
_ => None,
}
}
fn record_type(&self) -> RecordType {
RecordType::HINFO
}
fn into_rdata(self) -> RData {
RData::HINFO(self)
}
}
impl fmt::Display for HINFO {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(
f,
"{cpu} {os}",
cpu = &String::from_utf8_lossy(&self.cpu),
os = &String::from_utf8_lossy(&self.os)
)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::dbg_macro, clippy::print_stdout)]
use alloc::{string::ToString, vec::Vec};
#[cfg(feature = "std")]
use std::println;
use super::*;
#[test]
fn test() {
let rdata = HINFO::new("cpu".to_string(), "os".to_string());
let mut bytes = Vec::new();
let mut encoder: BinEncoder<'_> = BinEncoder::new(&mut bytes);
assert!(rdata.emit(&mut encoder).is_ok());
let bytes = encoder.into_bytes();
#[cfg(feature = "std")]
println!("bytes: {bytes:?}");
let mut decoder: BinDecoder<'_> = BinDecoder::new(bytes);
let read_rdata = HINFO::read(&mut decoder).expect("Decoding error");
assert_eq!(rdata, read_rdata);
}
#[test]
fn test_binary() {
let bin_data = vec![0, 1, 2, 3, 4, 5, 6, 7, 8];
let rdata = HINFO::from_bytes(
b"cpu".to_vec().into_boxed_slice(),
bin_data.into_boxed_slice(),
);
let mut bytes = Vec::new();
let mut encoder: BinEncoder<'_> = BinEncoder::new(&mut bytes);
assert!(rdata.emit(&mut encoder).is_ok());
let bytes = encoder.into_bytes();
#[cfg(feature = "std")]
println!("bytes: {bytes:?}");
let mut decoder: BinDecoder<'_> = BinDecoder::new(bytes);
let read_rdata = HINFO::read(&mut decoder).expect("Decoding error");
assert_eq!(rdata, read_rdata);
}
#[test]
fn test_parsing() {
assert_eq!(
HINFO::from_tokens(vec!["DEC-2060", "TOPS20"].into_iter())
.expect("failed to parse NAPTR"),
HINFO::new("DEC-2060".to_string(), "TOPS20".to_string()),
);
}
#[test]
fn test_parsing_fails() {
assert!(HINFO::from_tokens(vec!["DEC-2060"].into_iter()).is_err());
assert!(HINFO::from_tokens(vec![].into_iter()).is_err());
}
}