ippper 0.6.0

A library for building simple ipp servers
Documentation
use async_compression::futures::bufread;
use ipp::{
    attribute::{IppAttribute, IppAttributes},
    model::{DelimiterTag, StatusCode},
    payload::IppPayload,
    value::{IppName, IppValue},
};
use std::collections::HashSet;
mod reader_stream;
use crate::error::IppError;
pub(crate) use reader_stream::ReaderStream;

pub fn get_ipp_attribute<'a>(
    r: &'a IppAttributes,
    tag: DelimiterTag,
    name: &str,
) -> Option<&'a IppValue> {
    r.groups_of(tag)
        .find_map(|g| g.attributes().get(name))
        .map(|a| a.value())
}

pub fn take_ipp_attribute(
    r: &mut IppAttributes,
    tag: DelimiterTag,
    name: &str,
) -> Option<IppValue> {
    r.groups_mut()
        .iter_mut()
        .filter(|g| g.tag() == tag)
        .find_map(|g| g.attributes_mut().remove(name))
        .map(|a| a.into_value())
}

pub fn decommpress_payload(
    payload: IppPayload,
    compression: Option<&str>,
) -> anyhow::Result<IppPayload> {
    match compression {
        None => Ok(payload),
        Some("none") => Ok(payload),
        Some("gzip") => {
            let decoder = bufread::GzipDecoder::new(futures::io::BufReader::new(payload));
            Ok(IppPayload::new_async(decoder))
        }
        _ => Err(IppError {
            code: StatusCode::ClientErrorCompressionNotSupported,
            msg: StatusCode::ClientErrorCompressionNotSupported.to_string(),
        }
        .into()),
    }
}

pub fn get_requested_attributes(r: &IppAttributes) -> HashSet<&str> {
    get_ipp_attribute(
        r,
        DelimiterTag::OperationAttributes,
        IppAttribute::REQUESTED_ATTRIBUTES,
    )
    .map(|attr| {
        attr.into_iter()
            .filter_map(|e| e.as_keyword().map(|x| x.as_str()))
            .collect::<HashSet<_>>()
    })
    .unwrap_or_else(|| HashSet::from(["all"]))
}

pub fn take_requesting_user_name(r: &mut IppAttributes) -> IppName {
    take_ipp_attribute(r, DelimiterTag::OperationAttributes, "requesting-user-name")
        .and_then(|attr| match attr {
            IppValue::NameWithoutLanguage(name) => Some(name),
            IppValue::NameWithLanguage { name, .. } => Some(name),
            _ => None,
        })
        .unwrap_or_else(|| IppName::new("anonymous").unwrap())
}