use std::io::{self, Cursor, Write};
use bytes::Bytes;
use enum_as_inner::EnumAsInner;
use futures::Stream;
use log::debug;
use tempfile::NamedTempFile;
use crate::{
attribute::*,
ipp::{DelimiterTag, IppVersion, Operation},
parser::IppParseResult,
value::*,
IppHeader, IppJobSource, IppWriter, StatusCode,
};
#[derive(EnumAsInner)]
pub enum PayloadKind {
JobSource(IppJobSource),
ReceivedData(NamedTempFile),
}
pub struct IppRequestResponse {
header: IppHeader,
attributes: IppAttributes,
payload: Option<PayloadKind>,
}
impl IppRequestResponse {
pub fn new(version: IppVersion, operation: Operation, uri: Option<&str>) -> IppRequestResponse {
let hdr = IppHeader::new(version, operation as u16, 1);
let mut retval = IppRequestResponse {
header: hdr,
attributes: IppAttributes::new(),
payload: None,
};
retval.attributes_mut().add(
DelimiterTag::OperationAttributes,
IppAttribute::new(ATTRIBUTES_CHARSET, IppValue::Charset("utf-8".to_string())),
);
retval.attributes_mut().add(
DelimiterTag::OperationAttributes,
IppAttribute::new(ATTRIBUTES_NATURAL_LANGUAGE, IppValue::NaturalLanguage("en".to_string())),
);
if let Some(uri) = uri {
retval.attributes_mut().add(
DelimiterTag::OperationAttributes,
IppAttribute::new(PRINTER_URI, IppValue::Uri(uri.replace("http", "ipp").to_string())),
);
}
retval
}
pub fn new_response(version: IppVersion, status: StatusCode, id: u32) -> IppRequestResponse {
let hdr = IppHeader::new(version, status as u16, id);
let mut retval = IppRequestResponse {
header: hdr,
attributes: IppAttributes::new(),
payload: None,
};
retval.attributes_mut().add(
DelimiterTag::OperationAttributes,
IppAttribute::new(ATTRIBUTES_CHARSET, IppValue::Charset("utf-8".to_string())),
);
retval.attributes_mut().add(
DelimiterTag::OperationAttributes,
IppAttribute::new(ATTRIBUTES_NATURAL_LANGUAGE, IppValue::NaturalLanguage("en".to_string())),
);
retval
}
pub fn from_parse_result(result: IppParseResult) -> IppRequestResponse {
IppRequestResponse {
header: result.header,
attributes: result.attributes,
payload: result.payload,
}
}
pub fn header(&self) -> &IppHeader {
&self.header
}
pub fn header_mut(&mut self) -> &mut IppHeader {
&mut self.header
}
pub fn attributes(&self) -> &IppAttributes {
&self.attributes
}
pub fn attributes_mut(&mut self) -> &mut IppAttributes {
&mut self.attributes
}
pub fn payload(&self) -> &Option<PayloadKind> {
&self.payload
}
pub fn payload_mut(&mut self) -> &mut Option<PayloadKind> {
&mut self.payload
}
pub fn add_payload(&mut self, payload: IppJobSource) {
self.payload = Some(PayloadKind::JobSource(payload))
}
pub fn write(&mut self, writer: &mut Write) -> io::Result<usize> {
let mut retval = self.header.write(writer)?;
retval += self.attributes.write(writer)?;
debug!("Wrote {} bytes IPP stream", retval);
Ok(retval)
}
pub fn into_stream(self) -> Box<dyn Stream<Item = Bytes, Error = io::Error> + Send + 'static> {
let mut cursor = Cursor::new(Vec::with_capacity(1024));
let _ = self
.header
.write(&mut cursor)
.and_then(|_| self.attributes.write(&mut cursor));
let headers = futures::stream::once(Ok(cursor.into_inner().into()));
match self.payload {
Some(PayloadKind::JobSource(payload)) => Box::new(headers.chain(payload)),
_ => Box::new(headers),
}
}
}