use crate::ptp::{
AccessCapability, DateTime as PtpDateTime, DeviceInfo as PtpDeviceInfo,
FilesystemType as PtpFs, ObjectFormatCode, ObjectHandle as PtpObjectHandle,
ObjectInfo as PtpObjectInfo, OperationCode, StorageId as PtpStorageId,
StorageInfo as PtpStorageInfo, StorageType as PtpStorageType,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct ObjectHandle(pub u64);
impl ObjectHandle {
pub const ROOT: Self = ObjectHandle(0x0000_0000);
pub const ALL: Self = ObjectHandle(0xFFFF_FFFF);
#[must_use]
pub(crate) fn to_ptp(self) -> PtpObjectHandle {
PtpObjectHandle(self.0 as u32)
}
}
impl From<PtpObjectHandle> for ObjectHandle {
fn from(h: PtpObjectHandle) -> Self {
ObjectHandle(u64::from(h.0))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct StorageId(pub u64);
impl StorageId {
pub const ALL: Self = StorageId(0xFFFF_FFFF);
#[must_use]
pub(crate) fn to_ptp(self) -> PtpStorageId {
PtpStorageId(self.0 as u32)
}
}
impl From<PtpStorageId> for StorageId {
fn from(s: PtpStorageId) -> Self {
StorageId(u64::from(s.0))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ObjectFormat(pub u16);
impl ObjectFormat {
pub const UNDEFINED: Self = ObjectFormat(0x3000);
pub const ASSOCIATION: Self = ObjectFormat(0x3001);
#[must_use]
pub fn code(self) -> u16 {
self.0
}
#[must_use]
pub fn is_association(self) -> bool {
self == Self::ASSOCIATION
}
#[must_use]
pub fn is_audio(self) -> bool {
ObjectFormatCode::from(self.0).is_audio()
}
#[must_use]
pub fn is_video(self) -> bool {
ObjectFormatCode::from(self.0).is_video()
}
#[must_use]
pub fn is_image(self) -> bool {
ObjectFormatCode::from(self.0).is_image()
}
}
impl Default for ObjectFormat {
fn default() -> Self {
Self::UNDEFINED
}
}
impl From<ObjectFormatCode> for ObjectFormat {
fn from(c: ObjectFormatCode) -> Self {
ObjectFormat(c.into())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DateTime {
pub year: u16,
pub month: u8,
pub day: u8,
pub hour: u8,
pub minute: u8,
pub second: u8,
}
impl From<PtpDateTime> for DateTime {
fn from(d: PtpDateTime) -> Self {
DateTime {
year: d.year,
month: d.month,
day: d.day,
hour: d.hour,
minute: d.minute,
second: d.second,
}
}
}
impl DateTime {
#[must_use]
pub(crate) fn to_ptp(self) -> PtpDateTime {
PtpDateTime {
year: self.year,
month: self.month,
day: self.day,
hour: self.hour,
minute: self.minute,
second: self.second,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum StorageType {
#[default]
Undefined,
FixedRom,
RemovableRom,
FixedRam,
RemovableRam,
Other,
}
impl From<PtpStorageType> for StorageType {
fn from(t: PtpStorageType) -> Self {
match t {
PtpStorageType::Undefined => StorageType::Undefined,
PtpStorageType::FixedRom => StorageType::FixedRom,
PtpStorageType::RemovableRom => StorageType::RemovableRom,
PtpStorageType::FixedRam => StorageType::FixedRam,
PtpStorageType::RemovableRam => StorageType::RemovableRam,
PtpStorageType::Unknown(_) => StorageType::Other,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum FilesystemType {
#[default]
Undefined,
Flat,
Hierarchical,
Dcf,
Other,
}
impl From<PtpFs> for FilesystemType {
fn from(t: PtpFs) -> Self {
match t {
PtpFs::Undefined => FilesystemType::Undefined,
PtpFs::GenericFlat => FilesystemType::Flat,
PtpFs::GenericHierarchical => FilesystemType::Hierarchical,
PtpFs::Dcf => FilesystemType::Dcf,
PtpFs::Unknown(_) => FilesystemType::Other,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct ObjectInfo {
pub handle: ObjectHandle,
pub storage_id: StorageId,
pub parent: ObjectHandle,
pub filename: String,
pub size: u64,
pub format: ObjectFormat,
pub created: Option<DateTime>,
pub modified: Option<DateTime>,
pub image_width: u32,
pub image_height: u32,
pub(crate) folder: bool,
}
impl ObjectInfo {
#[must_use]
pub fn is_folder(&self) -> bool {
self.folder
}
#[must_use]
pub fn is_file(&self) -> bool {
!self.folder
}
pub(crate) fn from_ptp(o: PtpObjectInfo) -> Self {
let folder = o.is_folder();
ObjectInfo {
handle: o.handle.into(),
storage_id: o.storage_id.into(),
parent: o.parent.into(),
filename: o.filename,
size: o.size,
format: o.format.into(),
created: o.created.map(Into::into),
modified: o.modified.map(Into::into),
image_width: o.image_width,
image_height: o.image_height,
folder,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct DeviceInfo {
pub manufacturer: String,
pub model: String,
pub serial_number: String,
pub device_version: String,
}
impl DeviceInfo {
pub(crate) fn from_ptp(d: &PtpDeviceInfo) -> Self {
DeviceInfo {
manufacturer: d.manufacturer.clone(),
model: d.model.clone(),
serial_number: d.serial_number.clone(),
device_version: d.device_version.clone(),
}
}
}
#[derive(Debug, Clone, Default)]
pub struct StorageInfo {
pub id: StorageId,
pub description: String,
pub volume_identifier: String,
pub total_capacity: u64,
pub free_space: u64,
pub is_writable: bool,
pub storage_type: StorageType,
pub filesystem_type: FilesystemType,
}
impl StorageInfo {
pub(crate) fn from_ptp(s: &PtpStorageInfo) -> Self {
StorageInfo {
id: StorageId::default(),
description: s.description.clone(),
volume_identifier: s.volume_identifier.clone(),
total_capacity: s.max_capacity,
free_space: s.free_space_bytes,
is_writable: s.access_capability == AccessCapability::ReadWrite,
storage_type: s.storage_type.into(),
filesystem_type: s.filesystem_type.into(),
}
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Capabilities {
pub can_upload: bool,
pub can_delete: bool,
pub can_rename: bool,
pub can_move: bool,
pub can_copy: bool,
pub can_create_folder: bool,
pub supports_partial_download: bool,
pub supports_thumbnails: bool,
pub supports_events: bool,
}
impl Capabilities {
pub(crate) fn from_ptp_device_info(d: &PtpDeviceInfo) -> Self {
let op = |o: OperationCode| d.supports_operation(o);
Capabilities {
can_upload: op(OperationCode::SendObjectInfo) && op(OperationCode::SendObject),
can_delete: op(OperationCode::DeleteObject),
can_rename: d.supports_rename(),
can_move: op(OperationCode::MoveObject),
can_copy: op(OperationCode::CopyObject),
can_create_folder: op(OperationCode::SendObjectInfo),
supports_partial_download: op(OperationCode::GetPartialObject64)
|| op(OperationCode::GetPartialObject),
supports_thumbnails: op(OperationCode::GetThumb),
supports_events: !d.events_supported.is_empty(),
}
}
}