use http::Uri;
use num_traits::FromPrimitive;
use crate::{
attribute::IppAttribute,
error::IppError,
model::{DelimiterTag, PrinterState},
prelude::IppRequestResponse,
};
pub fn canonicalize_uri(uri: &Uri) -> Uri {
let mut builder = Uri::builder().scheme("ipp").path_and_query(uri.path());
if let Some(authority) = uri.authority() {
if let Some(port) = authority.port_u16() {
builder = builder.authority(format!("{}:{}", authority.host(), port).as_str());
} else {
builder = builder.authority(authority.host());
}
}
builder.build().unwrap_or_else(|_| uri.to_owned())
}
const ERROR_STATES: &[&str] = &[
"media-jam",
"toner-empty",
"spool-area-full",
"cover-open",
"door-open",
"input-tray-missing",
"output-tray-missing",
"marker-supply-empty",
"paused",
"shutdown",
];
pub fn is_printer_ready(response: &IppRequestResponse) -> Result<bool, IppError> {
let status = response.header().status_code();
if !status.is_success() {
return Err(IppError::StatusError(status));
}
let state = response
.attributes()
.groups_of(DelimiterTag::PrinterAttributes)
.next()
.and_then(|g| g.attributes().get(IppAttribute::PRINTER_STATE))
.and_then(|attr| attr.value().as_enum())
.and_then(|v| PrinterState::from_i32(*v));
if let Some(PrinterState::Stopped) = state {
return Ok(false);
}
if let Some(reasons) = response
.attributes()
.groups_of(DelimiterTag::PrinterAttributes)
.next()
.and_then(|g| g.attributes().get(IppAttribute::PRINTER_STATE_REASONS))
{
let keywords = reasons
.value()
.into_iter()
.filter_map(|e| e.as_keyword())
.map(ToOwned::to_owned)
.collect::<Vec<_>>();
if keywords.iter().any(|k| ERROR_STATES.contains(&&k[..])) {
return Ok(false);
}
}
Ok(true)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_canonicalize_uri() {
assert_eq!(
canonicalize_uri(&"http://user:pass@example.com:631/path?query=val".parse().unwrap()),
"ipp://example.com:631/path"
);
assert_eq!(
canonicalize_uri(&"http://example.com/path?query=val".parse().unwrap()),
"ipp://example.com/path"
);
}
}