1pub mod capabilities;
6pub mod raw;
7
8use capabilities::DeviceCapability;
9use libmtp_sys as ffi;
10use num_derive::ToPrimitive;
11use num_traits::{FromPrimitive, ToPrimitive};
12use std::ffi::CString;
13use std::fmt::{self, Debug};
14
15use crate::error::Error;
16use crate::object::filetypes::Filetype;
17use crate::object::properties::Property;
18use crate::object::{AsObjectId, DummyObject};
19use crate::storage::files::File;
20use crate::storage::StoragePool;
21use crate::values::AllowedValues;
22use crate::Result;
23
24#[derive(Debug, Clone, Copy, ToPrimitive)]
26pub enum StorageSort {
27 NotSorted = 0,
29 ByFreeSpace,
31 ByMaximumSpace,
33}
34
35#[derive(Debug, Clone, Copy)]
43pub enum UpdateResult {
44 Success,
46 OnlyIds,
48}
49
50#[derive(Debug, Copy, Clone)]
62pub enum BatteryLevel {
63 OnBattery(u8),
65 OnExternalPower,
67}
68
69pub struct MtpDevice {
82 pub(crate) inner: *mut ffi::LIBMTP_mtpdevice_t,
83}
84
85impl Drop for MtpDevice {
86 fn drop(&mut self) {
87 unsafe {
88 ffi::LIBMTP_Release_Device(self.inner);
89 }
90 }
91}
92
93impl Debug for MtpDevice {
94 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95 let max_bat_level = unsafe { (*self.inner).maximum_battery_level };
96
97 f.debug_struct("MTPDevice")
98 .field("maximum_battery_level", &max_bat_level)
99 .field("default_music_folder", &self.default_music_folder())
100 .field("default_playlist_folder", &self.default_playlist_folder())
101 .field("default_picture_folder", &self.default_picture_folder())
102 .field("default_video_folder", &self.default_video_folder())
103 .field("default_organizer_folder", &self.default_organizer_folder())
104 .field("default_zencast_folder", &self.default_zencast_folder())
105 .field("default_album_folder", &self.default_album_folder())
106 .field("default_text_folder", &self.default_text_folder())
107 .finish()
108 }
109}
110
111impl MtpDevice {
112 pub(crate) fn latest_error(&self) -> Option<Error> {
113 unsafe {
114 let list = ffi::LIBMTP_Get_Errorstack(self.inner);
115 let err = Error::from_latest_error(list)?;
116 ffi::LIBMTP_Clear_Errorstack(self.inner);
117 Some(err)
118 }
119 }
120}
121
122impl MtpDevice {
123 pub fn default_music_folder(&self) -> u32 {
127 unsafe { (*self.inner).default_music_folder }
128 }
129
130 pub fn default_playlist_folder(&self) -> u32 {
134 unsafe { (*self.inner).default_playlist_folder }
135 }
136
137 pub fn default_picture_folder(&self) -> u32 {
141 unsafe { (*self.inner).default_picture_folder }
142 }
143
144 pub fn default_video_folder(&self) -> u32 {
148 unsafe { (*self.inner).default_video_folder }
149 }
150
151 pub fn default_organizer_folder(&self) -> u32 {
155 unsafe { (*self.inner).default_organizer_folder }
156 }
157
158 pub fn default_zencast_folder(&self) -> u32 {
162 unsafe { (*self.inner).default_zencast_folder }
163 }
164
165 pub fn default_album_folder(&self) -> u32 {
169 unsafe { (*self.inner).default_album_folder }
170 }
171
172 pub fn default_text_folder(&self) -> u32 {
176 unsafe { (*self.inner).default_text_folder }
177 }
178
179 pub fn get_friendly_name(&self) -> Result<String> {
181 unsafe {
182 let friendly_name = ffi::LIBMTP_Get_Friendlyname(self.inner);
183
184 if friendly_name.is_null() {
185 Err(self.latest_error().unwrap_or_default())
186 } else {
187 let u8vec = cstr_to_u8vec!(friendly_name);
188 libc::free(friendly_name as *mut _);
189 Ok(String::from_utf8(u8vec)?)
190 }
191 }
192 }
193
194 pub fn set_friendly_name(&self, name: &str) -> Result<()> {
196 let name = CString::new(name).expect("Nul byte");
197
198 unsafe {
199 let res = ffi::LIBMTP_Set_Friendlyname(self.inner, name.as_ptr());
200
201 if res != 0 {
202 Err(self.latest_error().unwrap_or_default())
203 } else {
204 Ok(())
205 }
206 }
207 }
208
209 pub fn get_sync_partner(&self) -> Result<String> {
211 unsafe {
212 let partner = ffi::LIBMTP_Get_Syncpartner(self.inner);
213 let u8vec = cstr_to_u8vec!(partner);
214 libc::free(partner as *mut _);
215 Ok(String::from_utf8(u8vec)?)
216 }
217 }
218
219 pub fn set_sync_partner(&self, partner: &str) -> Result<()> {
221 let partner = CString::new(partner).expect("Nul byte");
222
223 unsafe {
224 let res = ffi::LIBMTP_Set_Syncpartner(self.inner, partner.as_ptr());
225
226 if res != 0 {
227 Err(self.latest_error().unwrap_or_default())
228 } else {
229 Ok(())
230 }
231 }
232 }
233
234 pub fn manufacturer_name(&self) -> Result<String> {
236 unsafe {
237 let manufacturer = ffi::LIBMTP_Get_Manufacturername(self.inner);
238
239 if manufacturer.is_null() {
240 Err(self.latest_error().unwrap_or_default())
241 } else {
242 let u8vec = cstr_to_u8vec!(manufacturer);
243 libc::free(manufacturer as *mut _);
244 Ok(String::from_utf8(u8vec)?)
245 }
246 }
247 }
248
249 pub fn model_name(&self) -> Result<String> {
251 unsafe {
252 let model = ffi::LIBMTP_Get_Modelname(self.inner);
253
254 if model.is_null() {
255 Err(self.latest_error().unwrap_or_default())
256 } else {
257 let u8vec = cstr_to_u8vec!(model);
258 libc::free(model as *mut _);
259 Ok(String::from_utf8(u8vec)?)
260 }
261 }
262 }
263
264 pub fn serial_number(&self) -> Result<String> {
266 unsafe {
267 let serial = ffi::LIBMTP_Get_Serialnumber(self.inner);
268
269 if serial.is_null() {
270 Err(self.latest_error().unwrap_or_default())
271 } else {
272 let u8vec = cstr_to_u8vec!(serial);
273 libc::free(serial as *mut _);
274 Ok(String::from_utf8(u8vec)?)
275 }
276 }
277 }
278
279 pub fn device_certificate(&self) -> Result<String> {
281 unsafe {
282 let mut devcert = std::ptr::null_mut();
283 let res = ffi::LIBMTP_Get_Device_Certificate(self.inner, &mut devcert);
284
285 if res != 0 || devcert.is_null() {
286 Err(self.latest_error().unwrap_or_default())
287 } else {
288 let u8vec = cstr_to_u8vec!(devcert);
289 libc::free(devcert as *mut _);
290 Ok(String::from_utf8(u8vec)?)
291 }
292 }
293 }
294
295 pub fn battery_level(&self) -> Result<(BatteryLevel, u8)> {
297 unsafe {
298 let mut max_level = 0;
299 let mut cur_level = 0;
300
301 let res = ffi::LIBMTP_Get_Batterylevel(self.inner, &mut max_level, &mut cur_level);
302
303 if res != 0 {
304 Err(self.latest_error().unwrap_or_default())
305 } else {
306 let cur_level = if cur_level == 0 {
307 BatteryLevel::OnExternalPower
308 } else {
309 BatteryLevel::OnBattery(cur_level)
310 };
311
312 Ok((cur_level, max_level))
313 }
314 }
315 }
316
317 pub fn secure_time(&self) -> Result<String> {
319 unsafe {
320 let mut secure_time = std::ptr::null_mut();
321 let res = ffi::LIBMTP_Get_Secure_Time(self.inner, &mut secure_time);
322
323 if res != 0 || secure_time.is_null() {
324 Err(self.latest_error().unwrap_or_default())
325 } else {
326 let u8vec = cstr_to_u8vec!(secure_time);
327 libc::free(secure_time as *mut _);
328 Ok(String::from_utf8(u8vec)?)
329 }
330 }
331 }
332
333 pub fn supported_filetypes(&self) -> Result<Vec<Filetype>> {
336 unsafe {
337 let mut filetypes = std::ptr::null_mut();
338 let mut len = 0;
339
340 let res = ffi::LIBMTP_Get_Supported_Filetypes(self.inner, &mut filetypes, &mut len);
341
342 if res != 0 || filetypes.is_null() {
343 Err(self.latest_error().unwrap_or_default())
344 } else {
345 let mut filetypes_vec = Vec::with_capacity(len as usize);
346 for i in 0..(len as isize) {
347 let ftype = Filetype::from_u16(*filetypes.offset(i)).unwrap();
348 filetypes_vec.push(ftype);
349 }
350
351 libc::free(filetypes as *mut _);
352 Ok(filetypes_vec)
353 }
354 }
355 }
356
357 pub fn check_capability(&self, capability: DeviceCapability) -> bool {
359 unsafe {
360 let cap_code = capability.to_u32().unwrap();
361 let res = ffi::LIBMTP_Check_Capability(self.inner, cap_code);
362 res != 0
363 }
364 }
365
366 pub fn reset_device(&self) -> Result<()> {
369 unsafe {
370 let res = ffi::LIBMTP_Reset_Device(self.inner);
371
372 if res != 0 {
373 Err(self.latest_error().unwrap_or_default())
374 } else {
375 Ok(())
376 }
377 }
378 }
379
380 pub fn update_storage(&mut self, sort_by: StorageSort) -> Result<UpdateResult> {
384 unsafe {
385 let res = ffi::LIBMTP_Get_Storage(self.inner, sort_by.to_i32().unwrap());
386 match res {
387 0 => Ok(UpdateResult::Success),
388 1 => Ok(UpdateResult::OnlyIds),
389 _ => Err(self.latest_error().unwrap_or_default()),
390 }
391 }
392 }
393
394 pub fn storage_pool(&self) -> StoragePool<'_> {
397 unsafe {
398 let storage = (*self.inner).storage;
399 StoragePool::from_raw(&self, storage)
400 }
401 }
402
403 pub fn dump_device_info(&self) {
406 unsafe {
407 ffi::LIBMTP_Dump_Device_Info(self.inner);
408 }
409 }
410
411 pub fn is_property_supported(&self, property: Property, filetype: Filetype) -> Result<bool> {
413 let property = property.to_u32().unwrap();
414 let filetype = filetype.to_u32().unwrap();
415
416 unsafe {
417 let res = ffi::LIBMTP_Is_Property_Supported(self.inner, property, filetype);
418 match res {
419 0 => Ok(false),
420 r if r > 0 => Ok(true),
421 _ => Err(self.latest_error().unwrap_or_default()),
422 }
423 }
424 }
425
426 pub fn allowed_property_values(
428 &self,
429 property: Property,
430 filetype: Filetype,
431 ) -> Result<AllowedValues> {
432 let property = property.to_u32().unwrap();
433 let filetype = filetype.to_u32().unwrap();
434
435 unsafe {
436 let allowed_values_ptr = std::ptr::null_mut();
437
438 let res = ffi::LIBMTP_Get_Allowed_Property_Values(
439 self.inner,
440 property,
441 filetype,
442 allowed_values_ptr,
443 );
444
445 if res != 0 || allowed_values_ptr.is_null() {
446 Err(self.latest_error().unwrap_or_default())
447 } else {
448 let allowed_values =
449 AllowedValues::from_raw(allowed_values_ptr).ok_or_else(|| Error::Unknown)?;
450 ffi::LIBMTP_destroy_allowed_values_t(allowed_values_ptr);
451 Ok(allowed_values)
452 }
453 }
454 }
455
456 pub fn dummy_object(&self, id: impl AsObjectId) -> DummyObject<'_> {
466 DummyObject {
467 id: id.as_id(),
468 mtpdev: self,
469 }
470 }
471
472 pub fn search_file(&self, id: impl AsObjectId) -> Result<File<'_>> {
477 let file = unsafe { ffi::LIBMTP_Get_Filemetadata(self.inner, id.as_id()) };
478
479 if file.is_null() {
480 Err(self.latest_error().unwrap_or_default())
481 } else {
482 Ok(File {
483 inner: file,
484 owner: self,
485 })
486 }
487 }
488
489 }