Add new_raw IppRequest constructor, DeleteAttr IppValueTag, and standard attribute helper#10
Add new_raw IppRequest constructor, DeleteAttr IppValueTag, and standard attribute helper#10bakayu wants to merge 1 commit intoOpenPrinting:mainfrom
new_raw IppRequest constructor, DeleteAttr IppValueTag, and standard attribute helper#10Conversation
|
I recommend you to rebase commits and squash last one. |
ae3c6fc to
5d0972b
Compare
|
Hey little occupied for now!, will glance through this in the weekend |
|
Hi @Gmin2 |
|
nice works, looks good with the first pass! |
There was a problem hiding this comment.
Pull request overview
This PR expands the low-level IPP request builder API to better support administrative / CUPS-specific operations without requiring downstream crates to use unsafe FFI directly.
Changes:
- Added
IppRequest::new_raw(op_code: i32)to construct requests from raw/legacy operation codes. - Added
IppValueTag::DeleteAttrmapping forIPP_TAG_DELETEATTR. - Added
IppRequest::add_standard_attrs()helper and a basic unit test for it.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| /// Create a new IPP request from a raw operation code. | ||
| /// This is useful for deprecated operations. | ||
| pub fn new_raw(op_code: i32) -> Result<Self> { | ||
| let ipp = unsafe { bindings::ippNewRequest(op_code) }; | ||
|
|
||
| if ipp.is_null() { | ||
| return Err(Error::UnsupportedFeature( | ||
| "Failed to create IPP request".to_string(), | ||
| )); | ||
| } | ||
|
|
||
| Ok(IppRequest { | ||
| ipp, | ||
| _phantom: PhantomData, | ||
| }) | ||
| } |
There was a problem hiding this comment.
new_raw duplicates the logic in IppRequest::new (same call + null check + struct construction). To keep the constructors consistent over time, consider refactoring to a shared private helper (or have new call into new_raw(operation.into() as i32) if the types line up).
There was a problem hiding this comment.
This works in theory, would it be better to change new to this or keep both the constructors as they are currently? @Gmin2
impl IppRequest {
pub fn new(operation: IppOperation) -> Result<Self> {
Self::new_raw(operation as i32)
}
}There was a problem hiding this comment.
I think it will be better to remove the new_raw method, I dont see any utility of it !
There was a problem hiding this comment.
Right, there is no long term utility, I intended to add this as a temporary method to create IPP requests for Ipp operations that are not supported yet. For example server side queue management, I explained the usecase with examples in the PR description. Once we have more API coverage with safe wrappers, this would loose its purpose.
let me know what you think.
| let result = request.add_standard_attrs(); | ||
| assert!(result.is_ok()); |
There was a problem hiding this comment.
The test only asserts that add_standard_attrs() returns Ok(()) but doesn’t verify that the three required attributes were actually added (or that their values are correct). Since IppRequest::as_ptr() is available, the test can use the raw IPP helpers (e.g., ippFindAttribute) to assert the presence/tag/value of attributes-charset, attributes-natural-language, and requesting-user-name.
| let result = request.add_standard_attrs(); | |
| assert!(result.is_ok()); | |
| request.add_standard_attrs().expect("Failed to add standard attributes"); | |
| let ipp = request.as_ptr(); | |
| assert!(!ipp.is_null(), "Underlying IPP pointer should not be null"); | |
| unsafe { | |
| // Verify that the three required standard attributes are present | |
| let charset_name = CString::new("attributes-charset").unwrap(); | |
| let natural_name = CString::new("attributes-natural-language").unwrap(); | |
| let user_name = CString::new("requesting-user-name").unwrap(); | |
| let charset_attr = bindings::ippFindAttribute( | |
| ipp, | |
| charset_name.as_ptr(), | |
| bindings::ipp_tag_t_IPP_TAG_ZERO, | |
| ); | |
| let natural_attr = bindings::ippFindAttribute( | |
| ipp, | |
| natural_name.as_ptr(), | |
| bindings::ipp_tag_t_IPP_TAG_ZERO, | |
| ); | |
| let user_attr = bindings::ippFindAttribute( | |
| ipp, | |
| user_name.as_ptr(), | |
| bindings::ipp_tag_t_IPP_TAG_ZERO, | |
| ); | |
| assert!( | |
| !charset_attr.is_null(), | |
| "attributes-charset attribute should be present" | |
| ); | |
| assert!( | |
| !natural_attr.is_null(), | |
| "attributes-natural-language attribute should be present" | |
| ); | |
| assert!( | |
| !user_attr.is_null(), | |
| "requesting-user-name attribute should be present" | |
| ); | |
| // Verify that the attributes have the expected IPP value tags | |
| assert_eq!( | |
| bindings::ippGetValueTag(charset_attr), | |
| bindings::ipp_tag_t_IPP_TAG_CHARSET, | |
| "attributes-charset should have CHARSET value tag" | |
| ); | |
| assert_eq!( | |
| bindings::ippGetValueTag(natural_attr), | |
| bindings::ipp_tag_t_IPP_TAG_LANGUAGE, | |
| "attributes-natural-language should have LANGUAGE value tag" | |
| ); | |
| assert_eq!( | |
| bindings::ippGetValueTag(user_attr), | |
| bindings::ipp_tag_t_IPP_TAG_NAME, | |
| "requesting-user-name should have NAME value tag" | |
| ); | |
| } |
There was a problem hiding this comment.
@Gmin2 I believe we currently lack safe wrappers to check if certain IppValueTags were injected currently, what approach should we take here?
There was a problem hiding this comment.
Its important that yu have a comment explaining why this is safe, whenever using unsafe code !
There was a problem hiding this comment.
I don't understand which part you are referring to...
| Uri, | ||
| Charset, | ||
| Language, | ||
| MimeType, | ||
| DeleteAttr, | ||
| } |
There was a problem hiding this comment.
IppValueTag::DeleteAttr is an out-of-band IPP value tag (used to delete/unset an attribute) and generally should be sent without a string/integer payload. The current safe API only exposes add_string/add_integer/add_boolean/add_strings, so callers still have no safe way to construct a delete-attr operation without passing a dummy value or dropping to raw bindings. Consider adding a dedicated helper (e.g., add_out_of_band(...) or delete_attr(name, group)) that calls the appropriate CUPS IPP function for out-of-band values, and document that DeleteAttr is only valid through that helper.
There was a problem hiding this comment.
DeleteAttr can be added via add_integer, here is an example for a helper fn which I used in my rust port of lpadmin:
fn add_delete_attr(
request: &mut IppRequest,
option: &str,
) -> Result<(), Box<dyn std::error::Error>> {
request.add_integer(IppTag::Printer, IppValueTag::DeleteAttr, option, 0)?;
Ok(())
}…ndard attributes Signed-off-by: Ayush <mail@ayuch.dev> feat: add `DeleteAttr` IppValueTag Signed-off-by: Ayush <mail@ayuch.dev> refacator: use `config::get_user();` instead of reading `USER` env var Signed-off-by: Ayush <mail@ayuch.dev> test: add test for `new_raw` IppRequest constructor Signed-off-by: Ayush <mail@ayuch.dev>
Summary
This PR extends the
IppRequestandIppValueTagAPIs to support administrative operations (likeCUPS-Add-Modify-PrinterandCUPS-Get-Devices) without forcing downstream applications to use unsafe blocks or raw FFI.Changes:
Added
IppRequest::new_raw(op_code: i32):CUPS_GET_DEVICES0x400B) or not yet part of theIppOperationenum.Added
IppValueTag::DeleteAttr:IPP_TAG_DELETEATTR(0x13).CUPS-Add-Modify-Printeroperations where specific attributes need to be unset/deleted.Added
IppRequest::add_standard_attrs():attributes-charset,attributes-natural-language,requesting-user-name).Use Case / Context
These additions were discovered while porting lpinfo and lpadmin to Rust. Previously, implementing lpinfo -v (Device Discovery) and lpadmin (Queue Management) required significant usage of unsafe blocks in the application code to access these missing tags and operations. These changes allow those tools to be written safely using the library's abstractions.
Previous oxidized version of lpinfo: https://github.com/bakayu/lpinfo-rs/blob/master/src/main.rs
New oxidized version of lpinfo (no usage of unsafe blocks): https://github.com/bakayu/lpinfo-rs/blob/feat-use-safe-wrappers/src/main.rs
Previous oxidized version of lpadmin: https://github.com/bakayu/lpadmin-rs/blob/master/src/ipp_helpers.rs
New oxidized version of lpadmin (no usage of unsafe blocks): https://github.com/bakayu/lpadmin-rs/blob/feat-use-safe-wrappers/src/ipp_helpers.rs