use std::fmt;
use std::cmp;
use std::convert::TryInto;
use std::time;
#[cfg(test)]
use quickcheck::{Arbitrary, Gen};
use crate::types::{DataFormat, Timestamp};
use crate::Error;
use crate::packet;
use crate::Packet;
use crate::Result;
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct Literal {
pub(crate) common: packet::Common,
format: DataFormat,
filename: Option<Vec<u8>>,
date: Option<Timestamp>,
container: packet::Container,
}
assert_send_and_sync!(Literal);
impl fmt::Debug for Literal {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let filename = if let Some(ref filename) = self.filename {
Some(String::from_utf8_lossy(filename))
} else {
None
};
let threshold = 36;
let body = self.body();
let prefix = &body[..cmp::min(threshold, body.len())];
let mut prefix_fmt = String::from_utf8_lossy(prefix).into_owned();
if body.len() > threshold {
prefix_fmt.push_str("...");
}
prefix_fmt.push_str(&format!(" ({} bytes)", body.len())[..]);
f.debug_struct("Literal")
.field("format", &self.format)
.field("filename", &filename)
.field("date", &self.date)
.field("body", &prefix_fmt)
.field("body_digest", &self.container.body_digest())
.finish()
}
}
impl Default for Literal {
fn default() -> Self {
Self::new(Default::default())
}
}
impl Literal {
pub fn new(format: DataFormat) -> Literal {
Literal {
common: Default::default(),
format,
filename: None,
date: None,
container: packet::Container::default_unprocessed(),
}
}
pub fn format(&self) -> DataFormat {
self.format
}
pub fn set_format(&mut self, format: DataFormat) -> DataFormat {
::std::mem::replace(&mut self.format, format)
}
pub fn filename(&self) -> Option<&[u8]> {
self.filename.as_ref().map(|b| b.as_slice())
}
pub fn set_filename<F>(&mut self, filename: F)
-> Result<Option<Vec<u8>>>
where F: AsRef<[u8]>
{
let filename = filename.as_ref();
Ok(::std::mem::replace(&mut self.filename, match filename.len() {
0 => None,
1..=255 => Some(filename.to_vec()),
n => return
Err(Error::InvalidArgument(
format!("filename too long: {} bytes", n)).into()),
}))
}
pub fn date(&self) -> Option<time::SystemTime> {
self.date.map(|d| d.into())
}
pub fn set_date<T>(&mut self, timestamp: T)
-> Result<Option<time::SystemTime>>
where T: Into<Option<time::SystemTime>>
{
let date = if let Some(d) = timestamp.into() {
let t = d.try_into()?;
if u32::from(t) == 0 {
None } else {
Some(t)
}
} else {
None
};
Ok(std::mem::replace(&mut self.date, date).map(|d| d.into()))
}
}
impl_body_forwards!(Literal);
impl From<Literal> for Packet {
fn from(s: Literal) -> Self {
Packet::Literal(s)
}
}
#[cfg(test)]
impl Arbitrary for Literal {
fn arbitrary<G: Gen>(g: &mut G) -> Self {
let mut l = Literal::new(DataFormat::arbitrary(g));
l.set_body(Vec::<u8>::arbitrary(g));
while let Err(_) = l.set_filename(&Vec::<u8>::arbitrary(g)) {
}
l.set_date(Some(Timestamp::arbitrary(g).into())).unwrap();
l
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::parse::Parse;
use crate::serialize::MarshalInto;
quickcheck! {
fn roundtrip(p: Literal) -> bool {
let q = Literal::from_bytes(&p.to_vec().unwrap()).unwrap();
assert_eq!(p, q);
true
}
}
#[test]
fn partial_read_eq() -> Result<()> {
use buffered_reader::BufferedReader;
use crate::parse::PacketParserBuilder;
let mut l0 = Literal::new(Default::default());
l0.set_body(vec![0, 0]);
let l0 = Packet::from(l0);
let l0bin = l0.to_vec()?;
assert_eq!(l0, Packet::from_bytes(&l0bin)?);
for &buffer_unread_content in &[false, true] {
for read_n in 0..3 {
eprintln!("buffer_unread_content: {:?}, read_n: {}",
buffer_unread_content, read_n);
let mut b = PacketParserBuilder::from_bytes(&l0bin)?;
if buffer_unread_content {
b = b.buffer_unread_content();
}
let mut pp = b.build()?.unwrap();
let d = pp.steal(read_n)?;
d.into_iter().for_each(|b| assert_eq!(b, 0));
let l = pp.finish()?;
assert_eq!(&l0, l);
let l = pp.next()?.0;
assert_eq!(l0, l);
}
}
Ok(())
}
}