libmtp_rs/storage/
folders.rs1use std::borrow::Cow;
4use std::ffi::{CStr, CString};
5use std::fmt::{self, Debug};
6
7use libmtp_sys as ffi;
8
9use crate::device::MtpDevice;
10use crate::object::Object;
11use crate::storage::Parent;
12use crate::Result;
13
14pub struct Folder<'a> {
15 inner: *mut ffi::LIBMTP_folder_t,
16 owner: &'a MtpDevice,
17
18 sibling_or_child: bool,
19}
20
21impl Drop for Folder<'_> {
22 fn drop(&mut self) {
23 if !self.sibling_or_child {
26 unsafe {
27 ffi::LIBMTP_destroy_folder_t(self.inner);
28 }
29 }
30 }
31}
32
33impl Object for Folder<'_> {
34 fn id(&self) -> u32 {
35 unsafe { (*self.inner).folder_id }
36 }
37
38 fn device(&self) -> &MtpDevice {
39 self.owner
40 }
41}
42
43impl Object for &Folder<'_> {
44 fn id(&self) -> u32 {
45 unsafe { (*self.inner).folder_id }
46 }
47
48 fn device(&self) -> &MtpDevice {
49 self.owner
50 }
51}
52
53impl Debug for Folder<'_> {
54 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55 f.debug_struct("Folder")
56 .field("parent_id", &self.parent_id())
57 .field("name", &self.name())
58 .finish()
59 }
60}
61
62impl<'a> Folder<'a> {
63 pub fn parent_id(&self) -> u32 {
64 unsafe { (*self.inner).parent_id }
65 }
66
67 pub fn name(&self) -> &str {
68 unsafe {
69 let cstr = CStr::from_ptr((*self.inner).name);
70 cstr.to_str().expect("Invalid UTF-8 on folder name")
71 }
72 }
73
74 pub fn sibling(&self) -> Option<Folder<'a>> {
75 unsafe {
76 if (*self.inner).sibling.is_null() {
77 None
78 } else {
79 Some(Folder {
80 inner: (*self.inner).sibling,
81 owner: self.owner,
82 sibling_or_child: true,
83 })
84 }
85 }
86 }
87
88 pub fn child(&self) -> Option<Folder<'a>> {
89 unsafe {
90 if (*self.inner).child.is_null() {
91 None
92 } else {
93 Some(Folder {
94 inner: (*self.inner).child,
95 owner: self.owner,
96 sibling_or_child: true,
97 })
98 }
99 }
100 }
101
102 pub fn find(&self, folder_id: u32) -> Option<Folder<'a>> {
103 let folder = unsafe { ffi::LIBMTP_Find_Folder(self.inner, folder_id) };
104
105 if folder.is_null() {
106 None
107 } else {
108 Some(Folder {
109 inner: folder,
110 owner: self.owner,
111 sibling_or_child: true,
112 })
113 }
114 }
115
116 pub fn rename(&mut self, new_name: &str) -> Result<()> {
117 let new_name = CString::new(new_name).expect("Nul byte");
118
119 let res =
120 unsafe { ffi::LIBMTP_Set_Folder_Name(self.owner.inner, self.inner, new_name.as_ptr()) };
121
122 if res != 0 {
123 Err(self.owner.latest_error().unwrap_or_default())
124 } else {
125 Ok(())
126 }
127 }
128}
129
130pub(crate) fn get_folder_list(mtpdev: &MtpDevice) -> Option<Folder<'_>> {
131 let folder = unsafe { ffi::LIBMTP_Get_Folder_List(mtpdev.inner) };
132
133 if folder.is_null() {
134 None
135 } else {
136 Some(Folder {
137 inner: folder,
138 owner: mtpdev,
139 sibling_or_child: false,
140 })
141 }
142}
143
144pub(crate) fn get_folder_list_storage(mtpdev: &MtpDevice, storage_id: u32) -> Option<Folder<'_>> {
145 let folder = unsafe { ffi::LIBMTP_Get_Folder_List_For_Storage(mtpdev.inner, storage_id) };
146
147 if folder.is_null() {
148 None
149 } else {
150 Some(Folder {
151 inner: folder,
152 owner: mtpdev,
153 sibling_or_child: false,
154 })
155 }
156}
157
158pub(crate) fn create_folder<'a>(
159 mtpdev: &MtpDevice,
160 name: &'a str,
161 parent: Parent,
162 storage_id: u32,
163) -> Result<(u32, Cow<'a, str>)> {
164 let name_cstr = CString::new(name).expect("Nul byte");
165 let parent = parent.faf_id();
166
167 let name_in_c = unsafe { libc::strdup(name_cstr.as_ptr()) };
168 let folder_id =
169 unsafe { ffi::LIBMTP_Create_Folder(mtpdev.inner, name_in_c, parent, storage_id) };
170
171 let name_from_c = unsafe { CStr::from_ptr(name_in_c) };
172 let name_from_c = name_from_c.to_str().expect("Invalid UTF-8");
173
174 let name = if name_from_c == name {
175 Cow::Borrowed(name)
176 } else {
177 Cow::Owned(name_from_c.to_string())
178 };
179
180 unsafe {
181 libc::free(name_in_c as *mut _);
185 }
186
187 if folder_id == 0 {
188 Err(mtpdev.latest_error().unwrap_or_default())
189 } else {
190 Ok((folder_id, name))
191 }
192}