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#[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 (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}