Skip to main content

ib_shell_item/
path.rs

1use std::{
2    marker::PhantomData,
3    path::{Path, PathBuf},
4};
5
6use widestring::U16CStr;
7use windows::{
8    Win32::UI::Shell::{Common::ITEMIDLIST, IShellItem},
9    core::Result,
10};
11
12use crate::{ShellItem, ShellItemDisplayName};
13
14/// A file system path or Windows Shell item ID list (PIDL).
15#[derive(Clone)]
16pub enum ShellPath<'a> {
17    Path(PathBuf),
18    IdList(*const ITEMIDLIST, PhantomData<&'a ()>),
19}
20
21impl From<PathBuf> for ShellPath<'static> {
22    fn from(value: PathBuf) -> Self {
23        Self::Path(value)
24    }
25}
26
27impl From<&Path> for ShellPath<'static> {
28    fn from(value: &Path) -> Self {
29        Self::Path(value.into())
30    }
31}
32
33impl<'a> ShellPath<'a> {
34    pub fn from_path_or_id_list(path: *const u16, id_list: *const ITEMIDLIST) -> Option<Self> {
35        match (path.is_null(), id_list.is_null()) {
36            (true, true) => None,
37            // Prefer lpFile
38            (false, _) => {
39                let file = unsafe { U16CStr::from_ptr_str(path) };
40
41                Self::Path(file.to_os_string().into()).into()
42            }
43            (true, false) => Self::IdList(id_list, Default::default()).into(),
44        }
45    }
46
47    pub fn to_file_path(&self) -> Result<PathBuf> {
48        Ok(match self {
49            ShellPath::Path(path) => path.clone(),
50            ShellPath::IdList(id_list, _) => {
51                let item = IShellItem::from_id_list(*id_list)?;
52                let name = item.get_display_name(ShellItemDisplayName::FileSystemPath)?;
53                name.to_os_string().into()
54            }
55        })
56    }
57}
58
59impl<'a> std::fmt::Debug for ShellPath<'a> {
60    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61        match self {
62            Self::Path(arg0) => f.debug_tuple("Path").field(arg0).finish(),
63            Self::IdList(arg0, _) => {
64                let mut f = f.debug_tuple("IdList");
65                let f = f.field(arg0);
66                if let Ok(path) = self.to_file_path() {
67                    f.field(&path);
68                }
69                f.finish()
70            }
71        }
72    }
73}