1use num_enum::{FromPrimitive, IntoPrimitive};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, FromPrimitive, IntoPrimitive)]
15#[repr(u16)]
16pub enum OperationCode {
17 GetDeviceInfo = 0x1001,
19 OpenSession = 0x1002,
21 CloseSession = 0x1003,
23 GetStorageIds = 0x1004,
25 GetStorageInfo = 0x1005,
27 GetNumObjects = 0x1006,
29 GetObjectHandles = 0x1007,
31 GetObjectInfo = 0x1008,
33 GetObject = 0x1009,
35 GetThumb = 0x100A,
37 DeleteObject = 0x100B,
39 SendObjectInfo = 0x100C,
41 SendObject = 0x100D,
43 InitiateCapture = 0x100E,
45 GetDevicePropDesc = 0x1014,
47 GetDevicePropValue = 0x1015,
49 SetDevicePropValue = 0x1016,
51 ResetDevicePropValue = 0x1017,
53 MoveObject = 0x1019,
55 CopyObject = 0x101A,
57 GetPartialObject = 0x101B,
59 GetObjectPropValue = 0x9803,
61 SetObjectPropValue = 0x9804,
63 #[num_enum(catch_all)]
65 Unknown(u16),
66}
67
68#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, FromPrimitive, IntoPrimitive)]
72#[repr(u16)]
73pub enum ResponseCode {
74 Ok = 0x2001,
76 GeneralError = 0x2002,
78 SessionNotOpen = 0x2003,
80 InvalidTransactionId = 0x2004,
82 OperationNotSupported = 0x2005,
84 ParameterNotSupported = 0x2006,
86 IncompleteTransfer = 0x2007,
88 InvalidStorageId = 0x2008,
90 InvalidObjectHandle = 0x2009,
92 DevicePropNotSupported = 0x200A,
94 InvalidObjectFormatCode = 0x200B,
96 StoreFull = 0x200C,
98 ObjectWriteProtected = 0x200D,
100 StoreReadOnly = 0x200E,
102 AccessDenied = 0x200F,
104 NoThumbnailPresent = 0x2010,
106 DeviceBusy = 0x2019,
108 InvalidParentObject = 0x201A,
110 InvalidDevicePropFormat = 0x201B,
112 InvalidDevicePropValue = 0x201C,
114 InvalidParameter = 0x201D,
116 SessionAlreadyOpen = 0x201E,
118 TransactionCancelled = 0x201F,
120 ObjectTooLarge = 0xA809,
122 #[num_enum(catch_all)]
124 Unknown(u16),
125}
126
127#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, FromPrimitive, IntoPrimitive)]
131#[repr(u16)]
132pub enum EventCode {
133 CancelTransaction = 0x4001,
135 ObjectAdded = 0x4002,
137 ObjectRemoved = 0x4003,
139 StoreAdded = 0x4004,
141 StoreRemoved = 0x4005,
143 DevicePropChanged = 0x4006,
145 ObjectInfoChanged = 0x4007,
147 DeviceInfoChanged = 0x4008,
149 StorageInfoChanged = 0x400C,
151 CaptureComplete = 0x400D,
153 #[num_enum(catch_all)]
155 Unknown(u16),
156}
157
158#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, FromPrimitive, IntoPrimitive)]
162#[repr(u16)]
163pub enum ObjectFormatCode {
164 Undefined = 0x3000,
166 Association = 0x3001,
168 Script = 0x3002,
170 Executable = 0x3003,
172 Text = 0x3004,
174 Html = 0x3005,
176 Dpof = 0x3006,
178 Aiff = 0x3007,
180 Wav = 0x3008,
182 Mp3 = 0x3009,
184 Avi = 0x300A,
186 Mpeg = 0x300B,
188 Asf = 0x300C,
190 Jpeg = 0x3801,
192 Tiff = 0x3804,
194 Gif = 0x3807,
196 Bmp = 0x3808,
198 Pict = 0x380A,
200 Png = 0x380B,
202 WmaAudio = 0xB901,
204 OggAudio = 0xB902,
206 AacAudio = 0xB903,
208 FlacAudio = 0xB906,
210 WmvVideo = 0xB981,
212 Mp4Container = 0xB982,
214 M4aAudio = 0xB984,
216 #[num_enum(catch_all)]
218 Unknown(u16),
219}
220
221impl ObjectFormatCode {
222 #[must_use]
226 pub fn from_extension(ext: &str) -> Self {
227 match ext.to_lowercase().as_str() {
228 "txt" => ObjectFormatCode::Text,
230 "html" | "htm" => ObjectFormatCode::Html,
231 "dpof" => ObjectFormatCode::Dpof,
232
233 "aiff" | "aif" => ObjectFormatCode::Aiff,
235 "wav" => ObjectFormatCode::Wav,
236 "mp3" => ObjectFormatCode::Mp3,
237 "wma" => ObjectFormatCode::WmaAudio,
238 "ogg" | "oga" => ObjectFormatCode::OggAudio,
239 "aac" => ObjectFormatCode::AacAudio,
240 "flac" => ObjectFormatCode::FlacAudio,
241 "m4a" => ObjectFormatCode::M4aAudio,
242
243 "avi" => ObjectFormatCode::Avi,
245 "mpg" | "mpeg" => ObjectFormatCode::Mpeg,
246 "asf" => ObjectFormatCode::Asf,
247 "wmv" => ObjectFormatCode::WmvVideo,
248 "mp4" | "m4v" => ObjectFormatCode::Mp4Container,
249
250 "jpg" | "jpeg" => ObjectFormatCode::Jpeg,
252 "tif" | "tiff" => ObjectFormatCode::Tiff,
253 "gif" => ObjectFormatCode::Gif,
254 "bmp" => ObjectFormatCode::Bmp,
255 "pict" | "pct" => ObjectFormatCode::Pict,
256 "png" => ObjectFormatCode::Png,
257
258 "exe" | "dll" | "bin" => ObjectFormatCode::Executable,
260 "sh" | "bat" | "cmd" | "ps1" => ObjectFormatCode::Script,
261
262 _ => ObjectFormatCode::Undefined,
263 }
264 }
265
266 #[must_use]
268 pub fn is_audio(&self) -> bool {
269 matches!(
270 self,
271 ObjectFormatCode::Aiff
272 | ObjectFormatCode::Wav
273 | ObjectFormatCode::Mp3
274 | ObjectFormatCode::WmaAudio
275 | ObjectFormatCode::OggAudio
276 | ObjectFormatCode::AacAudio
277 | ObjectFormatCode::FlacAudio
278 | ObjectFormatCode::M4aAudio
279 )
280 }
281
282 #[must_use]
284 pub fn is_video(&self) -> bool {
285 matches!(
286 self,
287 ObjectFormatCode::Avi
288 | ObjectFormatCode::Mpeg
289 | ObjectFormatCode::Asf
290 | ObjectFormatCode::WmvVideo
291 | ObjectFormatCode::Mp4Container
292 )
293 }
294
295 #[must_use]
297 pub fn is_image(&self) -> bool {
298 matches!(
299 self,
300 ObjectFormatCode::Jpeg
301 | ObjectFormatCode::Tiff
302 | ObjectFormatCode::Gif
303 | ObjectFormatCode::Bmp
304 | ObjectFormatCode::Pict
305 | ObjectFormatCode::Png
306 )
307 }
308}
309
310#[allow(clippy::derivable_impls)]
312impl Default for ObjectFormatCode {
313 fn default() -> Self {
314 ObjectFormatCode::Undefined
315 }
316}
317
318#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, FromPrimitive, IntoPrimitive)]
322#[repr(u16)]
323pub enum ObjectPropertyCode {
324 StorageId = 0xDC01,
326 ObjectFormat = 0xDC02,
328 ProtectionStatus = 0xDC03,
330 ObjectSize = 0xDC04,
332 ObjectFileName = 0xDC07,
334 DateCreated = 0xDC08,
336 DateModified = 0xDC09,
338 ParentObject = 0xDC0B,
340 Name = 0xDC44,
342 #[num_enum(catch_all)]
344 Unknown(u16),
345}
346
347#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, FromPrimitive, IntoPrimitive)]
351#[repr(u16)]
352pub enum PropertyDataType {
353 Undefined = 0x0000,
355 Int8 = 0x0001,
357 Uint8 = 0x0002,
359 Int16 = 0x0003,
361 Uint16 = 0x0004,
363 Int32 = 0x0005,
365 Uint32 = 0x0006,
367 Int64 = 0x0007,
369 Uint64 = 0x0008,
371 Int128 = 0x0009,
373 Uint128 = 0x000A,
375 #[num_enum(catch_all)]
377 Unknown(u16),
378 String = 0xFFFF,
380}
381
382impl PropertyDataType {
383 #[must_use]
388 pub fn byte_size(&self) -> Option<usize> {
389 match self {
390 PropertyDataType::Int8 | PropertyDataType::Uint8 => Some(1),
391 PropertyDataType::Int16 | PropertyDataType::Uint16 => Some(2),
392 PropertyDataType::Int32 | PropertyDataType::Uint32 => Some(4),
393 PropertyDataType::Int64 | PropertyDataType::Uint64 => Some(8),
394 PropertyDataType::Int128 | PropertyDataType::Uint128 => Some(16),
395 PropertyDataType::String
396 | PropertyDataType::Undefined
397 | PropertyDataType::Unknown(_) => None,
398 }
399 }
400}
401
402#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, FromPrimitive, IntoPrimitive)]
412#[repr(u16)]
413pub enum DevicePropertyCode {
414 Undefined = 0x5000,
416 BatteryLevel = 0x5001,
418 FunctionalMode = 0x5002,
420 ImageSize = 0x5003,
422 CompressionSetting = 0x5004,
424 WhiteBalance = 0x5005,
426 RgbGain = 0x5006,
428 FNumber = 0x5007,
430 FocalLength = 0x5008,
432 FocusDistance = 0x5009,
434 FocusMode = 0x500A,
436 ExposureMeteringMode = 0x500B,
438 FlashMode = 0x500C,
440 ExposureTime = 0x500D,
442 ExposureProgramMode = 0x500E,
444 ExposureIndex = 0x500F,
446 ExposureBiasCompensation = 0x5010,
448 DateTime = 0x5011,
450 CaptureDelay = 0x5012,
452 StillCaptureMode = 0x5013,
454 Contrast = 0x5014,
456 Sharpness = 0x5015,
458 DigitalZoom = 0x5016,
460 EffectMode = 0x5017,
462 BurstNumber = 0x5018,
464 BurstInterval = 0x5019,
466 TimelapseNumber = 0x501A,
468 TimelapseInterval = 0x501B,
470 FocusMeteringMode = 0x501C,
472 UploadUrl = 0x501D,
474 Artist = 0x501E,
476 CopyrightInfo = 0x501F,
478 #[num_enum(catch_all)]
480 Unknown(u16),
481}
482
483#[cfg(test)]
484mod tests {
485 use super::*;
486
487 #[test]
488 fn from_extension_detection() {
489 assert_eq!(
491 ObjectFormatCode::from_extension("mp3"),
492 ObjectFormatCode::Mp3
493 );
494 assert_eq!(
495 ObjectFormatCode::from_extension("flac"),
496 ObjectFormatCode::FlacAudio
497 );
498 assert_eq!(
499 ObjectFormatCode::from_extension("aif"),
500 ObjectFormatCode::Aiff
501 ); assert_eq!(
505 ObjectFormatCode::from_extension("mp4"),
506 ObjectFormatCode::Mp4Container
507 );
508 assert_eq!(
509 ObjectFormatCode::from_extension("avi"),
510 ObjectFormatCode::Avi
511 );
512 assert_eq!(
513 ObjectFormatCode::from_extension("mpg"),
514 ObjectFormatCode::Mpeg
515 ); assert_eq!(
519 ObjectFormatCode::from_extension("jpg"),
520 ObjectFormatCode::Jpeg
521 );
522 assert_eq!(
523 ObjectFormatCode::from_extension("png"),
524 ObjectFormatCode::Png
525 );
526 assert_eq!(
527 ObjectFormatCode::from_extension("tif"),
528 ObjectFormatCode::Tiff
529 ); assert_eq!(
533 ObjectFormatCode::from_extension("txt"),
534 ObjectFormatCode::Text
535 );
536 assert_eq!(
537 ObjectFormatCode::from_extension("htm"),
538 ObjectFormatCode::Html
539 ); assert_eq!(
543 ObjectFormatCode::from_extension("exe"),
544 ObjectFormatCode::Executable
545 );
546 assert_eq!(
547 ObjectFormatCode::from_extension("sh"),
548 ObjectFormatCode::Script
549 );
550
551 assert_eq!(
553 ObjectFormatCode::from_extension("MP3"),
554 ObjectFormatCode::Mp3
555 );
556
557 assert_eq!(
559 ObjectFormatCode::from_extension("xyz"),
560 ObjectFormatCode::Undefined
561 );
562 assert_eq!(
563 ObjectFormatCode::from_extension(""),
564 ObjectFormatCode::Undefined
565 );
566 }
567
568 #[test]
571 fn is_audio() {
572 assert!(ObjectFormatCode::Mp3.is_audio());
573 assert!(ObjectFormatCode::FlacAudio.is_audio());
574 assert!(!ObjectFormatCode::Jpeg.is_audio());
575 assert!(!ObjectFormatCode::Mp4Container.is_audio());
576 }
577
578 #[test]
579 fn is_video() {
580 assert!(ObjectFormatCode::Mp4Container.is_video());
581 assert!(ObjectFormatCode::Avi.is_video());
582 assert!(!ObjectFormatCode::Mp3.is_video());
583 assert!(!ObjectFormatCode::Jpeg.is_video());
584 }
585
586 #[test]
587 fn is_image() {
588 assert!(ObjectFormatCode::Jpeg.is_image());
589 assert!(ObjectFormatCode::Png.is_image());
590 assert!(!ObjectFormatCode::Mp3.is_image());
591 assert!(!ObjectFormatCode::Mp4Container.is_image());
592 }
593
594 #[test]
595 fn format_categories_are_mutually_exclusive() {
596 let all_formats = [
597 ObjectFormatCode::Undefined,
598 ObjectFormatCode::Association,
599 ObjectFormatCode::Script,
600 ObjectFormatCode::Executable,
601 ObjectFormatCode::Text,
602 ObjectFormatCode::Html,
603 ObjectFormatCode::Dpof,
604 ObjectFormatCode::Aiff,
605 ObjectFormatCode::Wav,
606 ObjectFormatCode::Mp3,
607 ObjectFormatCode::Avi,
608 ObjectFormatCode::Mpeg,
609 ObjectFormatCode::Asf,
610 ObjectFormatCode::Jpeg,
611 ObjectFormatCode::Tiff,
612 ObjectFormatCode::Gif,
613 ObjectFormatCode::Bmp,
614 ObjectFormatCode::Pict,
615 ObjectFormatCode::Png,
616 ObjectFormatCode::WmaAudio,
617 ObjectFormatCode::OggAudio,
618 ObjectFormatCode::AacAudio,
619 ObjectFormatCode::FlacAudio,
620 ObjectFormatCode::WmvVideo,
621 ObjectFormatCode::Mp4Container,
622 ObjectFormatCode::M4aAudio,
623 ];
624
625 for format in all_formats {
626 let categories = [format.is_audio(), format.is_video(), format.is_image()];
627 let true_count = categories.iter().filter(|&&b| b).count();
628 assert!(
629 true_count <= 1,
630 "{:?} belongs to multiple categories",
631 format
632 );
633 }
634 }
635
636 #[test]
639 fn property_data_type_byte_size() {
640 assert_eq!(PropertyDataType::Int8.byte_size(), Some(1));
642 assert_eq!(PropertyDataType::Uint8.byte_size(), Some(1));
643 assert_eq!(PropertyDataType::Int16.byte_size(), Some(2));
644 assert_eq!(PropertyDataType::Uint16.byte_size(), Some(2));
645 assert_eq!(PropertyDataType::Int32.byte_size(), Some(4));
646 assert_eq!(PropertyDataType::Uint32.byte_size(), Some(4));
647 assert_eq!(PropertyDataType::Int64.byte_size(), Some(8));
648 assert_eq!(PropertyDataType::Uint64.byte_size(), Some(8));
649 assert_eq!(PropertyDataType::Int128.byte_size(), Some(16));
650 assert_eq!(PropertyDataType::Uint128.byte_size(), Some(16));
651
652 assert_eq!(PropertyDataType::String.byte_size(), None);
654 assert_eq!(PropertyDataType::Undefined.byte_size(), None);
655 assert_eq!(PropertyDataType::Unknown(0x1234).byte_size(), None);
656 }
657}