use std::ptr;
use widestring::U16CStr;
use windows::{
Win32::{
Foundation::PROPERTYKEY,
System::Com::StructuredStorage::PROPVARIANT,
UI::Shell::PropertiesSystem::{PROPDESC_FORMAT_FLAGS, PSGetPropertySystem},
},
core::{Interface, PWSTR, Result},
};
pub use windows::Win32::UI::Shell::PropertiesSystem::IPropertySystem;
use crate::init;
pub trait PropertySystem {
fn new_init() -> Result<IPropertySystem>;
fn new() -> Result<IPropertySystem>;
fn format_for_display<'b>(
&self,
key: &PROPERTYKEY,
propvar: &PROPVARIANT,
pdff: PROPDESC_FORMAT_FLAGS,
buffer: &'b mut [u16],
) -> Result<&'b mut U16CStr>;
fn format_for_display_alloc(
&self,
key: &PROPERTYKEY,
propvar: &PROPVARIANT,
pdff: PROPDESC_FORMAT_FLAGS,
) -> Result<PWSTR>;
}
impl PropertySystem for IPropertySystem {
fn new_init() -> Result<IPropertySystem> {
_ = init();
Self::new()
}
fn new() -> Result<IPropertySystem> {
let mut pv = ptr::null_mut();
unsafe { PSGetPropertySystem(&IPropertySystem::IID, &mut pv)? };
Ok(unsafe { IPropertySystem::from_raw(pv) })
}
fn format_for_display<'b>(
&self,
key: &PROPERTYKEY,
propvar: &PROPVARIANT,
pdff: PROPDESC_FORMAT_FLAGS,
buffer: &'b mut [u16],
) -> Result<&'b mut U16CStr> {
unsafe { self.FormatForDisplay(key, propvar, pdff, buffer) }?;
Ok(U16CStr::from_slice_truncate_mut(buffer).unwrap())
}
fn format_for_display_alloc(
&self,
key: &PROPERTYKEY,
propvar: &PROPVARIANT,
pdff: PROPDESC_FORMAT_FLAGS,
) -> Result<PWSTR> {
unsafe { self.FormatForDisplayAlloc(key, propvar, pdff) }
}
}
#[cfg(test)]
mod tests {
use super::*;
use windows::Win32::Storage::EnhancedStorage::PKEY_Size;
#[test]
fn format_for_display() {
let prop_system = IPropertySystem::new_init().expect("Failed to create IPropertySystem");
let key = PKEY_Size;
let propvar = PROPVARIANT::from(1024_i64);
let pdff = PROPDESC_FORMAT_FLAGS::default();
let mut buffer: [u16; 256] = [0; 256];
let display_value = prop_system
.format_for_display(&key, &propvar, pdff, &mut buffer)
.expect("Failed to format for display");
assert_eq!(display_value, widestring::u16cstr!("1.00 KB"));
}
#[test]
fn format_for_display_alloc_with_pkey_size() {
let prop_system = IPropertySystem::new_init().expect("Failed to create IPropertySystem");
let key = PKEY_Size;
let propvar = PROPVARIANT::from(1024_i64);
let pdff = PROPDESC_FORMAT_FLAGS::default();
let result = prop_system
.format_for_display_alloc(&key, &propvar, pdff)
.expect("Failed to format for display");
let display_value: String =
unsafe { result.to_string() }.expect("Failed to convert PWSTR to String");
assert!(!display_value.is_empty());
eprintln!("Formatted size value: {display_value}");
assert_eq!(display_value, "1.00 KB");
}
#[test]
fn format_for_display_alloc_with_various_sizes() {
let prop_system = IPropertySystem::new_init().expect("Failed to create IPropertySystem");
let key = PKEY_Size;
let test_cases = [
(1024_i64, "1.00 KB"),
(1024 * 1024, "1.00 MB"),
(1024 * 1024 * 1024, "1.00 GB"),
];
for (size, expected) in test_cases {
let propvar = PROPVARIANT::from(size);
let result = prop_system
.format_for_display_alloc(&key, &propvar, PROPDESC_FORMAT_FLAGS::default())
.expect("Failed to format for display");
let display_value: String =
unsafe { result.to_string() }.expect("Failed to convert PWSTR");
assert_eq!(
display_value, expected,
"Size {size} should format to '{expected}'"
);
}
}
}