use super::{File, Folder};
use std::collections::HashMap;
use mtp_spec::communication::response;
use mtp_spec::device::session::MtpSession;
use mtp_spec::device::{Device, PtpIo};
use mtp_spec::error::MtpError;
use mtp_spec::object::properties::ObjectSize;
use mtp_spec::object::{
Association, FolderType, ObjectFormatCode, ObjectHandle, ObjectInfo, ProtectionStatus,
PtpString,
};
use std::future::Future;
use std::sync::Weak;
use tokio::sync::RwLock;
pub trait SessionFsExt<D>
where
D: Device,
{
fn mkdir<N>(
&self,
parent: Option<&Folder<D>>,
name: N,
) -> impl Future<Output = Result<Folder<D>, MtpError<<D as PtpIo>::TransportError>>> + Send
where
N: AsRef<str> + Send;
fn create<N>(
&self,
parent: Option<&Folder<D>>,
name: N,
format: ObjectFormatCode,
data: Vec<u8>,
) -> impl Future<Output = Result<File<D>, MtpError<<D as PtpIo>::TransportError>>> + Send
where
N: AsRef<str> + Send;
}
impl<D> SessionFsExt<D> for MtpSession<D>
where
D: Device,
{
async fn mkdir<N>(
&self,
parent: Option<&Folder<D>>,
name: N,
) -> Result<Folder<D>, MtpError<<D as PtpIo>::TransportError>>
where
N: AsRef<str> + Send,
{
let storage = parent.map(|p| p.storage_id);
let parent_object = parent.map(|p| p.id);
let fs_context = parent.map_or_else(Weak::new, |p| p.fs.clone());
let name_ptp = PtpString::try_from(name.as_ref())?;
let response = self
.send_object_info(ObjectInfo {
storage_id: storage.unwrap_or_default(),
object_format: ObjectFormatCode::Association,
protection_status: ProtectionStatus::NoProtection,
parent_object,
association: Some(Association::GenericFolder {
ty: FolderType::Generic,
}),
filename: name_ptp,
..Default::default()
})
.await?;
self.send_object(Vec::new()).await?;
let response::SendObjectInfo {
storage_id,
parent: _,
reserved_handle,
} = response.data;
let object_info = self.get_object_info(reserved_handle).await?.data.data;
Ok(Folder {
fs: fs_context,
storage_id,
id: reserved_handle,
parent_id: parent.map_or(ObjectHandle::NONE, |p| p.id),
name: object_info.filename.to_string(),
format: ObjectFormatCode::Association,
protection_status: ProtectionStatus::NoProtection,
date_created: object_info.date_created,
date_modified: object_info.date_modified,
children: RwLock::new(HashMap::new()),
})
}
async fn create<N>(
&self,
parent: Option<&Folder<D>>,
name: N,
format: ObjectFormatCode,
data: Vec<u8>,
) -> Result<File<D>, MtpError<<D as PtpIo>::TransportError>>
where
N: AsRef<str> + Send,
{
if format == ObjectFormatCode::Association {
return Err(MtpError::UnsupportedOperation);
}
let storage = parent.map(|p| p.storage_id);
let parent_object = parent.map(|p| p.id);
let fs_context = parent.map(|p| p.fs.clone()).unwrap_or_default();
let name_ptp = PtpString::try_from(name.as_ref())?;
let response = self
.send_object_info(ObjectInfo {
storage_id: storage.unwrap_or_default(),
parent_object,
object_format: format,
compressed_size: data.len() as u32,
association: None,
filename: name_ptp,
protection_status: ProtectionStatus::default(),
thumbnail: None,
image_details: None,
sequence_number: 0,
date_created: None,
date_modified: None,
keywords: PtpString::default(),
})
.await?;
let response::SendObjectInfo {
storage_id,
parent: _,
reserved_handle,
} = response.data;
self.send_object(data).await?;
let object_info = self.get_object_info(reserved_handle).await?.data.data;
let size_response = self
.get_object_prop_value::<ObjectSize>(reserved_handle)
.await?;
Ok(File {
fs: fs_context,
storage_id,
id: reserved_handle,
parent_id: parent.map_or(ObjectHandle::NONE, |p| p.id),
name: object_info.filename.to_string(),
size: size_response.data.data,
format: object_info.object_format,
protection_status: object_info.protection_status,
date_modified: object_info.date_modified,
date_created: object_info.date_created,
})
}
}