blob: 911cecf46355d5ffd78c99313aa54ed804ba3204 [file] [log] [blame]
//!
//! IPP print protocol implementation for Rust. This crate can be used in several ways:
//! * using the low-level request/response API and building the requests manually.
//! * using the higher-level operations API with builders. Currently only a subset of all IPP operations is supported.
//! * using the built-in IPP client.
//! * using any third-party HTTP client and send the serialized request manually.
//!
//! This crate supports both synchronous and asynchronous operations. The following feature flags are supported:
//! * `async` - enables asynchronous APIs.
//! * `async-client` - enables asynchronous IPP client based on `reqwest` crate, implies `async` feature.
//! * `client` - enables blocking IPP client based on `ureq` crate.
//! * `async-client-tls` - enables asynchronous IPP client with TLS, using native-tls backend. Implies `async-client` feature.
//! * `client-tls` - enables blocking IPP client with TLS, using native-tls backend. Implies `client` feature.
//! * `async-client-rustls` - enables asynchronous IPP client with TLS, using rustls backend. Implies `async-client` feature.
//! * `client-rustls` - enables blocking IPP client with TLS, using rustls backend. Implies `client` feature.
//!
//! By default, the following feature is enabled: `async-client-tls`.
//!
//! Implementation notes:
//! * all RFC IPP values are supported including arrays and collections, for both de- and serialization.
//! * this crate is also suitable for building IPP servers, however the example is not provided yet.
//!
//! Usage examples:
//!
//!```rust,no_run
//! // using low-level async API
//! use ipp::prelude::*;
//!
//! #[tokio::main]
//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let uri: Uri = "http://localhost:631/printers/test-printer".parse()?;
//! let req = IppRequestResponse::new(
//! IppVersion::v1_1(),
//! Operation::GetPrinterAttributes,
//! Some(uri.clone())
//! );
//! let client = AsyncIppClient::new(uri);
//! let resp = client.send(req).await?;
//! if resp.header().status_code().is_success() {
//! println!("{:?}", resp.attributes());
//! }
//! Ok(())
//! }
//!```
//!```rust,no_run
//! // using blocking operations API
//! use ipp::prelude::*;
//!
//! fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let uri: Uri = "http://localhost:631/printers/test-printer".parse()?;
//! let operation = IppOperationBuilder::get_printer_attributes(uri.clone()).build();
//! let client = IppClient::new(uri);
//! let resp = client.send(operation)?;
//! if resp.header().status_code().is_success() {
//! println!("{:?}", resp.attributes());
//! }
//! Ok(())
//! }
//!```
#![allow(clippy::result_large_err)]
use bytes::{BufMut, Bytes, BytesMut};
use num_traits::FromPrimitive;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::model::{IppVersion, StatusCode};
pub mod attribute;
#[cfg(any(feature = "client", feature = "async-client"))]
pub mod client;
pub mod error;
pub mod model;
pub mod operation;
pub mod parser;
pub mod payload;
pub mod reader;
pub mod request;
pub mod util;
pub mod value;
pub mod prelude {
//!
//! Common imports
//!
pub use http::Uri;
pub use num_traits::FromPrimitive as _;
pub use crate::{
attribute::{IppAttribute, IppAttributeGroup, IppAttributes},
model::*,
operation::builder::IppOperationBuilder,
payload::IppPayload,
request::IppRequestResponse,
value::IppValue,
};
pub use super::error::IppError;
#[cfg(feature = "async-client")]
pub use super::client::non_blocking::AsyncIppClient;
#[cfg(feature = "client")]
pub use super::client::blocking::IppClient;
pub use super::IppHeader;
}
/// IPP request and response header
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug)]
pub struct IppHeader {
/// IPP protocol version
pub version: IppVersion,
/// Operation tag for requests, status for responses
pub operation_or_status: u16,
/// ID of the request
pub request_id: u32,
}
impl IppHeader {
/// Create IPP header
pub fn new(version: IppVersion, operation_or_status: u16, request_id: u32) -> IppHeader {
IppHeader {
version,
operation_or_status,
request_id,
}
}
/// Write header to a given writer
pub fn to_bytes(&self) -> Bytes {
let mut buffer = BytesMut::new();
buffer.put_u16(self.version.0);
buffer.put_u16(self.operation_or_status);
buffer.put_u32(self.request_id);
buffer.freeze()
}
/// Decode and get IPP status code from the header
pub fn status_code(&self) -> StatusCode {
StatusCode::from_u16(self.operation_or_status).unwrap_or(StatusCode::UnknownStatusCode)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_header_to_bytes() {
let header = IppHeader::new(IppVersion::v2_1(), 0x1234, 0xaa55_aa55);
let buf = header.to_bytes();
assert_eq!(buf, vec![0x02, 0x01, 0x12, 0x34, 0xaa, 0x55, 0xaa, 0x55]);
}
}