use num_enum::{FromPrimitive, IntoPrimitive};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, FromPrimitive, IntoPrimitive)]
#[repr(u16)]
pub enum OperationCode {
GetDeviceInfo = 0x1001,
OpenSession = 0x1002,
CloseSession = 0x1003,
GetStorageIds = 0x1004,
GetStorageInfo = 0x1005,
GetNumObjects = 0x1006,
GetObjectHandles = 0x1007,
GetObjectInfo = 0x1008,
GetObject = 0x1009,
GetThumb = 0x100A,
DeleteObject = 0x100B,
SendObjectInfo = 0x100C,
SendObject = 0x100D,
InitiateCapture = 0x100E,
GetDevicePropDesc = 0x1014,
GetDevicePropValue = 0x1015,
SetDevicePropValue = 0x1016,
ResetDevicePropValue = 0x1017,
MoveObject = 0x1019,
CopyObject = 0x101A,
GetPartialObject = 0x101B,
GetObjectPropValue = 0x9803,
SetObjectPropValue = 0x9804,
#[num_enum(catch_all)]
Unknown(u16),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, FromPrimitive, IntoPrimitive)]
#[repr(u16)]
pub enum ResponseCode {
Ok = 0x2001,
GeneralError = 0x2002,
SessionNotOpen = 0x2003,
InvalidTransactionId = 0x2004,
OperationNotSupported = 0x2005,
ParameterNotSupported = 0x2006,
IncompleteTransfer = 0x2007,
InvalidStorageId = 0x2008,
InvalidObjectHandle = 0x2009,
DevicePropNotSupported = 0x200A,
InvalidObjectFormatCode = 0x200B,
StoreFull = 0x200C,
ObjectWriteProtected = 0x200D,
StoreReadOnly = 0x200E,
AccessDenied = 0x200F,
NoThumbnailPresent = 0x2010,
DeviceBusy = 0x2019,
InvalidParentObject = 0x201A,
InvalidDevicePropFormat = 0x201B,
InvalidDevicePropValue = 0x201C,
InvalidParameter = 0x201D,
SessionAlreadyOpen = 0x201E,
TransactionCancelled = 0x201F,
ObjectTooLarge = 0xA809,
#[num_enum(catch_all)]
Unknown(u16),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, FromPrimitive, IntoPrimitive)]
#[repr(u16)]
pub enum EventCode {
ObjectAdded = 0x4002,
ObjectRemoved = 0x4003,
StoreAdded = 0x4004,
StoreRemoved = 0x4005,
DevicePropChanged = 0x4006,
ObjectInfoChanged = 0x4007,
DeviceInfoChanged = 0x4008,
StorageInfoChanged = 0x400C,
CaptureComplete = 0x400D,
#[num_enum(catch_all)]
Unknown(u16),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, FromPrimitive, IntoPrimitive)]
#[repr(u16)]
pub enum ObjectFormatCode {
Undefined = 0x3000,
Association = 0x3001,
Script = 0x3002,
Executable = 0x3003,
Text = 0x3004,
Html = 0x3005,
Dpof = 0x3006,
Aiff = 0x3007,
Wav = 0x3008,
Mp3 = 0x3009,
Avi = 0x300A,
Mpeg = 0x300B,
Asf = 0x300C,
Jpeg = 0x3801,
Tiff = 0x3804,
Gif = 0x3807,
Bmp = 0x3808,
Pict = 0x380A,
Png = 0x380B,
WmaAudio = 0xB901,
OggAudio = 0xB902,
AacAudio = 0xB903,
FlacAudio = 0xB906,
WmvVideo = 0xB981,
Mp4Container = 0xB982,
M4aAudio = 0xB984,
#[num_enum(catch_all)]
Unknown(u16),
}
impl ObjectFormatCode {
#[must_use]
pub fn from_extension(ext: &str) -> Self {
match ext.to_lowercase().as_str() {
"txt" => ObjectFormatCode::Text,
"html" | "htm" => ObjectFormatCode::Html,
"dpof" => ObjectFormatCode::Dpof,
"aiff" | "aif" => ObjectFormatCode::Aiff,
"wav" => ObjectFormatCode::Wav,
"mp3" => ObjectFormatCode::Mp3,
"wma" => ObjectFormatCode::WmaAudio,
"ogg" | "oga" => ObjectFormatCode::OggAudio,
"aac" => ObjectFormatCode::AacAudio,
"flac" => ObjectFormatCode::FlacAudio,
"m4a" => ObjectFormatCode::M4aAudio,
"avi" => ObjectFormatCode::Avi,
"mpg" | "mpeg" => ObjectFormatCode::Mpeg,
"asf" => ObjectFormatCode::Asf,
"wmv" => ObjectFormatCode::WmvVideo,
"mp4" | "m4v" => ObjectFormatCode::Mp4Container,
"jpg" | "jpeg" => ObjectFormatCode::Jpeg,
"tif" | "tiff" => ObjectFormatCode::Tiff,
"gif" => ObjectFormatCode::Gif,
"bmp" => ObjectFormatCode::Bmp,
"pict" | "pct" => ObjectFormatCode::Pict,
"png" => ObjectFormatCode::Png,
"exe" | "dll" | "bin" => ObjectFormatCode::Executable,
"sh" | "bat" | "cmd" | "ps1" => ObjectFormatCode::Script,
_ => ObjectFormatCode::Undefined,
}
}
#[must_use]
pub fn is_audio(&self) -> bool {
matches!(
self,
ObjectFormatCode::Aiff
| ObjectFormatCode::Wav
| ObjectFormatCode::Mp3
| ObjectFormatCode::WmaAudio
| ObjectFormatCode::OggAudio
| ObjectFormatCode::AacAudio
| ObjectFormatCode::FlacAudio
| ObjectFormatCode::M4aAudio
)
}
#[must_use]
pub fn is_video(&self) -> bool {
matches!(
self,
ObjectFormatCode::Avi
| ObjectFormatCode::Mpeg
| ObjectFormatCode::Asf
| ObjectFormatCode::WmvVideo
| ObjectFormatCode::Mp4Container
)
}
#[must_use]
pub fn is_image(&self) -> bool {
matches!(
self,
ObjectFormatCode::Jpeg
| ObjectFormatCode::Tiff
| ObjectFormatCode::Gif
| ObjectFormatCode::Bmp
| ObjectFormatCode::Pict
| ObjectFormatCode::Png
)
}
}
#[allow(clippy::derivable_impls)]
impl Default for ObjectFormatCode {
fn default() -> Self {
ObjectFormatCode::Undefined
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, FromPrimitive, IntoPrimitive)]
#[repr(u16)]
pub enum ObjectPropertyCode {
StorageId = 0xDC01,
ObjectFormat = 0xDC02,
ProtectionStatus = 0xDC03,
ObjectSize = 0xDC04,
ObjectFileName = 0xDC07,
DateCreated = 0xDC08,
DateModified = 0xDC09,
ParentObject = 0xDC0B,
Name = 0xDC44,
#[num_enum(catch_all)]
Unknown(u16),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, FromPrimitive, IntoPrimitive)]
#[repr(u16)]
pub enum PropertyDataType {
Undefined = 0x0000,
Int8 = 0x0001,
Uint8 = 0x0002,
Int16 = 0x0003,
Uint16 = 0x0004,
Int32 = 0x0005,
Uint32 = 0x0006,
Int64 = 0x0007,
Uint64 = 0x0008,
Int128 = 0x0009,
Uint128 = 0x000A,
#[num_enum(catch_all)]
Unknown(u16),
String = 0xFFFF,
}
impl PropertyDataType {
#[must_use]
pub fn byte_size(&self) -> Option<usize> {
match self {
PropertyDataType::Int8 | PropertyDataType::Uint8 => Some(1),
PropertyDataType::Int16 | PropertyDataType::Uint16 => Some(2),
PropertyDataType::Int32 | PropertyDataType::Uint32 => Some(4),
PropertyDataType::Int64 | PropertyDataType::Uint64 => Some(8),
PropertyDataType::Int128 | PropertyDataType::Uint128 => Some(16),
PropertyDataType::String
| PropertyDataType::Undefined
| PropertyDataType::Unknown(_) => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, FromPrimitive, IntoPrimitive)]
#[repr(u16)]
pub enum DevicePropertyCode {
Undefined = 0x5000,
BatteryLevel = 0x5001,
FunctionalMode = 0x5002,
ImageSize = 0x5003,
CompressionSetting = 0x5004,
WhiteBalance = 0x5005,
RgbGain = 0x5006,
FNumber = 0x5007,
FocalLength = 0x5008,
FocusDistance = 0x5009,
FocusMode = 0x500A,
ExposureMeteringMode = 0x500B,
FlashMode = 0x500C,
ExposureTime = 0x500D,
ExposureProgramMode = 0x500E,
ExposureIndex = 0x500F,
ExposureBiasCompensation = 0x5010,
DateTime = 0x5011,
CaptureDelay = 0x5012,
StillCaptureMode = 0x5013,
Contrast = 0x5014,
Sharpness = 0x5015,
DigitalZoom = 0x5016,
EffectMode = 0x5017,
BurstNumber = 0x5018,
BurstInterval = 0x5019,
TimelapseNumber = 0x501A,
TimelapseInterval = 0x501B,
FocusMeteringMode = 0x501C,
UploadUrl = 0x501D,
Artist = 0x501E,
CopyrightInfo = 0x501F,
#[num_enum(catch_all)]
Unknown(u16),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn from_extension_detection() {
assert_eq!(
ObjectFormatCode::from_extension("mp3"),
ObjectFormatCode::Mp3
);
assert_eq!(
ObjectFormatCode::from_extension("flac"),
ObjectFormatCode::FlacAudio
);
assert_eq!(
ObjectFormatCode::from_extension("aif"),
ObjectFormatCode::Aiff
);
assert_eq!(
ObjectFormatCode::from_extension("mp4"),
ObjectFormatCode::Mp4Container
);
assert_eq!(
ObjectFormatCode::from_extension("avi"),
ObjectFormatCode::Avi
);
assert_eq!(
ObjectFormatCode::from_extension("mpg"),
ObjectFormatCode::Mpeg
);
assert_eq!(
ObjectFormatCode::from_extension("jpg"),
ObjectFormatCode::Jpeg
);
assert_eq!(
ObjectFormatCode::from_extension("png"),
ObjectFormatCode::Png
);
assert_eq!(
ObjectFormatCode::from_extension("tif"),
ObjectFormatCode::Tiff
);
assert_eq!(
ObjectFormatCode::from_extension("txt"),
ObjectFormatCode::Text
);
assert_eq!(
ObjectFormatCode::from_extension("htm"),
ObjectFormatCode::Html
);
assert_eq!(
ObjectFormatCode::from_extension("exe"),
ObjectFormatCode::Executable
);
assert_eq!(
ObjectFormatCode::from_extension("sh"),
ObjectFormatCode::Script
);
assert_eq!(
ObjectFormatCode::from_extension("MP3"),
ObjectFormatCode::Mp3
);
assert_eq!(
ObjectFormatCode::from_extension("xyz"),
ObjectFormatCode::Undefined
);
assert_eq!(
ObjectFormatCode::from_extension(""),
ObjectFormatCode::Undefined
);
}
#[test]
fn is_audio() {
assert!(ObjectFormatCode::Mp3.is_audio());
assert!(ObjectFormatCode::FlacAudio.is_audio());
assert!(!ObjectFormatCode::Jpeg.is_audio());
assert!(!ObjectFormatCode::Mp4Container.is_audio());
}
#[test]
fn is_video() {
assert!(ObjectFormatCode::Mp4Container.is_video());
assert!(ObjectFormatCode::Avi.is_video());
assert!(!ObjectFormatCode::Mp3.is_video());
assert!(!ObjectFormatCode::Jpeg.is_video());
}
#[test]
fn is_image() {
assert!(ObjectFormatCode::Jpeg.is_image());
assert!(ObjectFormatCode::Png.is_image());
assert!(!ObjectFormatCode::Mp3.is_image());
assert!(!ObjectFormatCode::Mp4Container.is_image());
}
#[test]
fn format_categories_are_mutually_exclusive() {
let all_formats = [
ObjectFormatCode::Undefined,
ObjectFormatCode::Association,
ObjectFormatCode::Script,
ObjectFormatCode::Executable,
ObjectFormatCode::Text,
ObjectFormatCode::Html,
ObjectFormatCode::Dpof,
ObjectFormatCode::Aiff,
ObjectFormatCode::Wav,
ObjectFormatCode::Mp3,
ObjectFormatCode::Avi,
ObjectFormatCode::Mpeg,
ObjectFormatCode::Asf,
ObjectFormatCode::Jpeg,
ObjectFormatCode::Tiff,
ObjectFormatCode::Gif,
ObjectFormatCode::Bmp,
ObjectFormatCode::Pict,
ObjectFormatCode::Png,
ObjectFormatCode::WmaAudio,
ObjectFormatCode::OggAudio,
ObjectFormatCode::AacAudio,
ObjectFormatCode::FlacAudio,
ObjectFormatCode::WmvVideo,
ObjectFormatCode::Mp4Container,
ObjectFormatCode::M4aAudio,
];
for format in all_formats {
let categories = [format.is_audio(), format.is_video(), format.is_image()];
let true_count = categories.iter().filter(|&&b| b).count();
assert!(
true_count <= 1,
"{:?} belongs to multiple categories",
format
);
}
}
#[test]
fn property_data_type_byte_size() {
assert_eq!(PropertyDataType::Int8.byte_size(), Some(1));
assert_eq!(PropertyDataType::Uint8.byte_size(), Some(1));
assert_eq!(PropertyDataType::Int16.byte_size(), Some(2));
assert_eq!(PropertyDataType::Uint16.byte_size(), Some(2));
assert_eq!(PropertyDataType::Int32.byte_size(), Some(4));
assert_eq!(PropertyDataType::Uint32.byte_size(), Some(4));
assert_eq!(PropertyDataType::Int64.byte_size(), Some(8));
assert_eq!(PropertyDataType::Uint64.byte_size(), Some(8));
assert_eq!(PropertyDataType::Int128.byte_size(), Some(16));
assert_eq!(PropertyDataType::Uint128.byte_size(), Some(16));
assert_eq!(PropertyDataType::String.byte_size(), None);
assert_eq!(PropertyDataType::Undefined.byte_size(), None);
assert_eq!(PropertyDataType::Unknown(0x1234).byte_size(), None);
}
}