1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
//!
//! 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 support
//! * `client-tls` - enables blocking IPP client with TLS support
//!
//! By default, all features are enabled.
//!
//!
//! 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.
//! * some operations (e.g. CUPS-specific) require authorization which can be supplied in the printer URI.
//!
//! 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]);
}
}