mirror of
https://github.com/NLnetLabs/domain.git
synced 2026-09-27 12:14:55 +02:00
write some tests for Show
This commit is contained in:
+159
-40
@@ -1,18 +1,17 @@
|
||||
//! Modelled roughly after `std::fmt::Display`
|
||||
|
||||
use core::fmt;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Error;
|
||||
|
||||
impl From<std::fmt::Error> for Error {
|
||||
fn from(value: std::fmt::Error) -> Self {
|
||||
impl From<fmt::Error> for Error {
|
||||
fn from(_: fmt::Error) -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
pub type Result = std::result::Result<(), Error>;
|
||||
|
||||
/// Show a value as zonefile format
|
||||
pub trait Show {
|
||||
fn show(&self, p: &mut Presenter<'_>) -> Result;
|
||||
|
||||
@@ -23,15 +22,15 @@ pub trait Show {
|
||||
|
||||
impl<T: Show + ?Sized> fmt::Display for ZoneFileDisplay<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.inner.show(&mut Presenter {
|
||||
writer: &mut SimplePresentationWriter::new(f)
|
||||
}).map_err(|_| fmt::Error)
|
||||
self.inner
|
||||
.show(&mut Presenter {
|
||||
writer: &mut SimplePresentationWriter::new(f),
|
||||
})
|
||||
.map_err(|_| fmt::Error)
|
||||
}
|
||||
}
|
||||
|
||||
ZoneFileDisplay {
|
||||
inner: self
|
||||
}
|
||||
ZoneFileDisplay { inner: self }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,19 +38,41 @@ impl<T: Show> Show for &T {
|
||||
fn show(&self, p: &mut Presenter<'_>) -> Result {
|
||||
T::show(self, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Determines how a zonefile is formatted
|
||||
pub trait PresentationWriter {
|
||||
/// Push a token to the zonefile
|
||||
fn fmt_token(&mut self, args: std::fmt::Arguments<'_>) -> Result;
|
||||
|
||||
/// Start a block of grouped tokens
|
||||
///
|
||||
/// This might push `'('` to the zonefile, but may be ignored by the
|
||||
/// `PresentationWriter`.
|
||||
fn begin_block(&mut self) -> Result;
|
||||
|
||||
/// End a block of grouped tokens
|
||||
///
|
||||
/// This might push `'('` to the zonefile, but may be ignored by the
|
||||
/// `PresentationWriter`.
|
||||
fn end_block(&mut self) -> Result;
|
||||
|
||||
/// Write a comment
|
||||
///
|
||||
/// This may be ignored.
|
||||
fn fmt_comment(&mut self, args: std::fmt::Arguments<'_>) -> Result;
|
||||
|
||||
/// End the current record and start a new line
|
||||
fn newline(&mut self) -> Result;
|
||||
}
|
||||
|
||||
/// The simplest possible zonefile writer
|
||||
///
|
||||
/// This writer does not do any alignment, comments and squeezes each record
|
||||
/// onto a single line.
|
||||
struct SimplePresentationWriter<'a> {
|
||||
first: bool,
|
||||
writer: &'a mut (dyn std::fmt::Write + 'a),
|
||||
writer: &'a mut (dyn fmt::Write + 'a),
|
||||
}
|
||||
|
||||
impl<'a> SimplePresentationWriter<'a> {
|
||||
@@ -64,7 +85,7 @@ impl<'a> SimplePresentationWriter<'a> {
|
||||
}
|
||||
|
||||
impl PresentationWriter for SimplePresentationWriter<'_> {
|
||||
fn fmt_token(&mut self, args: std::fmt::Arguments<'_>) -> Result {
|
||||
fn fmt_token(&mut self, args: fmt::Arguments<'_>) -> Result {
|
||||
if !self.first {
|
||||
self.writer.write_char(' ')?;
|
||||
}
|
||||
@@ -81,22 +102,29 @@ impl PresentationWriter for SimplePresentationWriter<'_> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fmt_comment(&mut self, _args: std::fmt::Arguments<'_>) -> Result {
|
||||
fn fmt_comment(&mut self, _args: fmt::Arguments<'_>) -> Result {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn newline(&mut self) -> Result {
|
||||
self.writer.write_char('\n');
|
||||
self.writer.write_char('\n')?;
|
||||
self.first = true;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// A more structured wrapper around a [`PresentationWriter`]
|
||||
///
|
||||
/// Writing comments is not allowed with this type because comments can only
|
||||
/// appear when a token is surrounded by parentheses.
|
||||
pub struct Presenter<'a> {
|
||||
writer: &'a mut (dyn PresentationWriter + 'a),
|
||||
}
|
||||
|
||||
impl<'a> Presenter<'a> {
|
||||
/// Start a sequence of grouped tokens
|
||||
///
|
||||
/// The block might be surrounded by `(` and `)` in a multiline format.
|
||||
pub fn block<'b>(&'b mut self) -> Block<'a, 'b> {
|
||||
let result = self.writer.begin_block();
|
||||
Block {
|
||||
@@ -105,7 +133,8 @@ impl<'a> Presenter<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_token(&mut self, token: impl std::fmt::Display) -> Result {
|
||||
/// Push a token
|
||||
pub fn write_token(&mut self, token: impl fmt::Display) -> Result {
|
||||
self.writer.fmt_token(format_args!("{token}"))
|
||||
}
|
||||
}
|
||||
@@ -117,49 +146,139 @@ pub struct Block<'a, 'b> {
|
||||
}
|
||||
|
||||
impl<'a, 'b> Block<'a, 'b> {
|
||||
pub fn write_token(&mut self, token: impl std::fmt::Display) -> &mut Self {
|
||||
self.result = self
|
||||
.result
|
||||
.and_then(|_| self.presenter.write_token(token));
|
||||
/// Push a token
|
||||
pub fn write_token(
|
||||
&mut self,
|
||||
token: impl std::fmt::Display,
|
||||
) -> &mut Self {
|
||||
self.result =
|
||||
self.result.and_then(|_| self.presenter.write_token(token));
|
||||
self
|
||||
}
|
||||
|
||||
/// Push the sequence of tokens generated by the [`Show`] implementation
|
||||
/// of the `token`
|
||||
pub fn write_show(&mut self, token: impl Show) -> &mut Self {
|
||||
self.result = self
|
||||
.result
|
||||
.and_then(|_| token.show(self.presenter));
|
||||
self.result = self.result.and_then(|_| token.show(self.presenter));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn write_comment(&mut self, s: impl std::fmt::Display) -> &mut Self {
|
||||
self.result = self
|
||||
.result
|
||||
.and_then(|_| self.presenter.writer.fmt_comment(format_args!("{s}")));
|
||||
/// Write a comment
|
||||
///
|
||||
/// This may be ignored.
|
||||
pub fn write_comment(&mut self, s: impl fmt::Display) -> &mut Self {
|
||||
self.result = self.result.and_then(|_| {
|
||||
self.presenter.writer.fmt_comment(format_args!("{s}"))
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
/// Finish the block.
|
||||
///
|
||||
/// This _must_ be called before the `Block` is dropped.
|
||||
pub fn finish(&mut self) -> Result {
|
||||
self.result.and_then(|_| self.presenter.writer.end_block())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(all(test, feature = "std"))]
|
||||
mod test {
|
||||
use std::string::ToString as _;
|
||||
use std::vec::Vec;
|
||||
|
||||
use crate::base::iana::{Class, DigestAlg, SecAlg};
|
||||
use crate::base::show::Show;
|
||||
use crate::base::{Record, Name, Ttl};
|
||||
use crate::rdata::A;
|
||||
use crate::base::iana::Class;
|
||||
use crate::base::{Name, Record, Ttl};
|
||||
use crate::rdata::{Cds, Cname, Ds, Mx, Txt, A};
|
||||
|
||||
fn create_record<Data>(data: Data) -> Record<&'static Name<[u8]>, Data> {
|
||||
let name = Name::from_slice(b"\x07example\x03com\x00").unwrap();
|
||||
Record::new(name, Class::IN, Ttl::from_secs(3600), data)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_record() {
|
||||
let name = Name::from_slice(b"\x07example\x03com\x00").unwrap();
|
||||
let record = Record::new(
|
||||
name,
|
||||
Class::IN,
|
||||
Ttl::from_secs(3600),
|
||||
A::new("128.140.76.106".parse().unwrap()),
|
||||
let record = create_record(A::new("128.140.76.106".parse().unwrap()));
|
||||
assert_eq!(
|
||||
"example.com. 3600 IN A 128.140.76.106",
|
||||
record.display_zonefile().to_string()
|
||||
);
|
||||
eprintln!("{}", record.display_zonefile());
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cname_record() {
|
||||
let record = create_record(Cname::new(
|
||||
Name::from_slice(b"\x07example\x03com\x00").unwrap(),
|
||||
));
|
||||
assert_eq!(
|
||||
"example.com. 3600 IN CNAME example.com.",
|
||||
record.display_zonefile().to_string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ds_key_record() {
|
||||
let record = create_record(
|
||||
Ds::new(
|
||||
5414,
|
||||
SecAlg::ED25519,
|
||||
DigestAlg::SHA256,
|
||||
&[0xDE, 0xAD, 0xBE, 0xEF],
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
assert_eq!(
|
||||
"example.com. 3600 IN DS 5414 15 2 DEADBEEF",
|
||||
record.display_zonefile().to_string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cds_record() {
|
||||
let record = create_record(
|
||||
Cds::new(
|
||||
5414,
|
||||
SecAlg::ED25519,
|
||||
DigestAlg::SHA256,
|
||||
&[0xDE, 0xAD, 0xBE, 0xEF],
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
assert_eq!(
|
||||
"example.com. 3600 IN CDS 5414 15 2 DEADBEEF",
|
||||
record.display_zonefile().to_string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mx_record() {
|
||||
let record = create_record(Mx::new(
|
||||
20,
|
||||
Name::from_slice(b"\x07example\x03com\x00").unwrap(),
|
||||
));
|
||||
assert_eq!(
|
||||
"example.com. 3600 IN MX 20 example.com.",
|
||||
record.display_zonefile().to_string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn txt_record() {
|
||||
let record = create_record(Txt::<Vec<u8>>::build_from_slice(
|
||||
b"this is a string that is longer than 255 characters if I just \
|
||||
type a little bit more to pad this test out and then write some \
|
||||
more like a silly monkey with a typewriter accidentally writing \
|
||||
some shakespeare along the way but it feels like I have to type \
|
||||
even longer to hit that limit!\
|
||||
").unwrap());
|
||||
assert_eq!(
|
||||
"example.com. 3600 IN TXT \
|
||||
\"this is a string that is longer than 255 characters if I just \
|
||||
type a little bit more to pad this test out and then write some \
|
||||
more like a silly monkey with a typewriter accidentally writing \
|
||||
some shakespeare along the way but it feels like I have to type \
|
||||
e\" \"ven longer to hit that limit!\"",
|
||||
record.display_zonefile().to_string()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-12
@@ -680,22 +680,11 @@ impl<Octs: AsRef<[u8]>> fmt::Debug for Cds<Octs> {
|
||||
|
||||
impl<Octs: AsRef<[u8]>> Show for Cds<Octs> {
|
||||
fn show(&self, p: &mut Presenter) -> show::Result {
|
||||
struct Data<'a>(&'a [u8]);
|
||||
|
||||
impl std::fmt::Display for Data<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
for ch in self.0 {
|
||||
write!(f, " {:02x}", *ch)?
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
p.block()
|
||||
.write_token(self.key_tag)
|
||||
.write_show(self.algorithm)
|
||||
.write_show(self.digest_type)
|
||||
.write_token(Data(self.digest.as_ref()))
|
||||
.write_token(base16::encode_display(&self.digest))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,7 +234,7 @@ impl<N: fmt::Display> Show for Mx<N> {
|
||||
fn show(&self, p: &mut Presenter) -> show::Result {
|
||||
p.block()
|
||||
.write_token(self.preference)
|
||||
.write_token(&self.exchange)
|
||||
.write_token(format_args!("{}.", self.exchange))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -448,7 +448,7 @@ impl<Octs: AsRef<[u8]>> fmt::Debug for Txt<Octs> {
|
||||
|
||||
//--- Show
|
||||
|
||||
impl<Octs: AsRef<[u8]>> Show for Txt<Octs> {
|
||||
impl<Octs> Show for Txt<Octs> where Octs: AsRef<[u8]> {
|
||||
fn show(&self, p: &mut Presenter) -> show::Result {
|
||||
let mut block = p.block();
|
||||
for slice in self.iter_charstrs() {
|
||||
|
||||
Reference in New Issue
Block a user