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 ObjectAdded = 0x4002,
135 ObjectRemoved = 0x4003,
137 StoreAdded = 0x4004,
139 StoreRemoved = 0x4005,
141 DevicePropChanged = 0x4006,
143 ObjectInfoChanged = 0x4007,
145 DeviceInfoChanged = 0x4008,
147 StorageInfoChanged = 0x400C,
149 CaptureComplete = 0x400D,
151 #[num_enum(catch_all)]
153 Unknown(u16),
154}
155
156#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, FromPrimitive, IntoPrimitive)]
160#[repr(u16)]
161pub enum ObjectFormatCode {
162 Undefined = 0x3000,
164 Association = 0x3001,
166 Script = 0x3002,
168 Executable = 0x3003,
170 Text = 0x3004,
172 Html = 0x3005,
174 Dpof = 0x3006,
176 Aiff = 0x3007,
178 Wav = 0x3008,
180 Mp3 = 0x3009,
182 Avi = 0x300A,
184 Mpeg = 0x300B,
186 Asf = 0x300C,
188 Jpeg = 0x3801,
190 Tiff = 0x3804,
192 Gif = 0x3807,
194 Bmp = 0x3808,
196 Pict = 0x380A,
198 Png = 0x380B,
200 WmaAudio = 0xB901,
202 OggAudio = 0xB902,
204 AacAudio = 0xB903,
206 FlacAudio = 0xB906,
208 WmvVideo = 0xB981,
210 Mp4Container = 0xB982,
212 M4aAudio = 0xB984,
214 #[num_enum(catch_all)]
216 Unknown(u16),
217}
218
219impl ObjectFormatCode {
220 #[must_use]
224 pub fn from_extension(ext: &str) -> Self {
225 match ext.to_lowercase().as_str() {
226 "txt" => ObjectFormatCode::Text,
228 "html" | "htm" => ObjectFormatCode::Html,
229 "dpof" => ObjectFormatCode::Dpof,
230
231 "aiff" | "aif" => ObjectFormatCode::Aiff,
233 "wav" => ObjectFormatCode::Wav,
234 "mp3" => ObjectFormatCode::Mp3,
235 "wma" => ObjectFormatCode::WmaAudio,
236 "ogg" | "oga" => ObjectFormatCode::OggAudio,
237 "aac" => ObjectFormatCode::AacAudio,
238 "flac" => ObjectFormatCode::FlacAudio,
239 "m4a" => ObjectFormatCode::M4aAudio,
240
241 "avi" => ObjectFormatCode::Avi,
243 "mpg" | "mpeg" => ObjectFormatCode::Mpeg,
244 "asf" => ObjectFormatCode::Asf,
245 "wmv" => ObjectFormatCode::WmvVideo,
246 "mp4" | "m4v" => ObjectFormatCode::Mp4Container,
247
248 "jpg" | "jpeg" => ObjectFormatCode::Jpeg,
250 "tif" | "tiff" => ObjectFormatCode::Tiff,
251 "gif" => ObjectFormatCode::Gif,
252 "bmp" => ObjectFormatCode::Bmp,
253 "pict" | "pct" => ObjectFormatCode::Pict,
254 "png" => ObjectFormatCode::Png,
255
256 "exe" | "dll" | "bin" => ObjectFormatCode::Executable,
258 "sh" | "bat" | "cmd" | "ps1" => ObjectFormatCode::Script,
259
260 _ => ObjectFormatCode::Undefined,
261 }
262 }
263
264 #[must_use]
266 pub fn is_audio(&self) -> bool {
267 matches!(
268 self,
269 ObjectFormatCode::Aiff
270 | ObjectFormatCode::Wav
271 | ObjectFormatCode::Mp3
272 | ObjectFormatCode::WmaAudio
273 | ObjectFormatCode::OggAudio
274 | ObjectFormatCode::AacAudio
275 | ObjectFormatCode::FlacAudio
276 | ObjectFormatCode::M4aAudio
277 )
278 }
279
280 #[must_use]
282 pub fn is_video(&self) -> bool {
283 matches!(
284 self,
285 ObjectFormatCode::Avi
286 | ObjectFormatCode::Mpeg
287 | ObjectFormatCode::Asf
288 | ObjectFormatCode::WmvVideo
289 | ObjectFormatCode::Mp4Container
290 )
291 }
292
293 #[must_use]
295 pub fn is_image(&self) -> bool {
296 matches!(
297 self,
298 ObjectFormatCode::Jpeg
299 | ObjectFormatCode::Tiff
300 | ObjectFormatCode::Gif
301 | ObjectFormatCode::Bmp
302 | ObjectFormatCode::Pict
303 | ObjectFormatCode::Png
304 )
305 }
306}
307
308#[allow(clippy::derivable_impls)]
310impl Default for ObjectFormatCode {
311 fn default() -> Self {
312 ObjectFormatCode::Undefined
313 }
314}
315
316#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, FromPrimitive, IntoPrimitive)]
320#[repr(u16)]
321pub enum ObjectPropertyCode {
322 StorageId = 0xDC01,
324 ObjectFormat = 0xDC02,
326 ProtectionStatus = 0xDC03,
328 ObjectSize = 0xDC04,
330 ObjectFileName = 0xDC07,
332 DateCreated = 0xDC08,
334 DateModified = 0xDC09,
336 ParentObject = 0xDC0B,
338 Name = 0xDC44,
340 #[num_enum(catch_all)]
342 Unknown(u16),
343}
344
345#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, FromPrimitive, IntoPrimitive)]
349#[repr(u16)]
350pub enum PropertyDataType {
351 Undefined = 0x0000,
353 Int8 = 0x0001,
355 Uint8 = 0x0002,
357 Int16 = 0x0003,
359 Uint16 = 0x0004,
361 Int32 = 0x0005,
363 Uint32 = 0x0006,
365 Int64 = 0x0007,
367 Uint64 = 0x0008,
369 Int128 = 0x0009,
371 Uint128 = 0x000A,
373 #[num_enum(catch_all)]
375 Unknown(u16),
376 String = 0xFFFF,
378}
379
380impl PropertyDataType {
381 #[must_use]
386 pub fn byte_size(&self) -> Option<usize> {
387 match self {
388 PropertyDataType::Int8 | PropertyDataType::Uint8 => Some(1),
389 PropertyDataType::Int16 | PropertyDataType::Uint16 => Some(2),
390 PropertyDataType::Int32 | PropertyDataType::Uint32 => Some(4),
391 PropertyDataType::Int64 | PropertyDataType::Uint64 => Some(8),
392 PropertyDataType::Int128 | PropertyDataType::Uint128 => Some(16),
393 PropertyDataType::String
394 | PropertyDataType::Undefined
395 | PropertyDataType::Unknown(_) => None,
396 }
397 }
398}
399
400#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, FromPrimitive, IntoPrimitive)]
410#[repr(u16)]
411pub enum DevicePropertyCode {
412 Undefined = 0x5000,
414 BatteryLevel = 0x5001,
416 FunctionalMode = 0x5002,
418 ImageSize = 0x5003,
420 CompressionSetting = 0x5004,
422 WhiteBalance = 0x5005,
424 RgbGain = 0x5006,
426 FNumber = 0x5007,
428 FocalLength = 0x5008,
430 FocusDistance = 0x5009,
432 FocusMode = 0x500A,
434 ExposureMeteringMode = 0x500B,
436 FlashMode = 0x500C,
438 ExposureTime = 0x500D,
440 ExposureProgramMode = 0x500E,
442 ExposureIndex = 0x500F,
444 ExposureBiasCompensation = 0x5010,
446 DateTime = 0x5011,
448 CaptureDelay = 0x5012,
450 StillCaptureMode = 0x5013,
452 Contrast = 0x5014,
454 Sharpness = 0x5015,
456 DigitalZoom = 0x5016,
458 EffectMode = 0x5017,
460 BurstNumber = 0x5018,
462 BurstInterval = 0x5019,
464 TimelapseNumber = 0x501A,
466 TimelapseInterval = 0x501B,
468 FocusMeteringMode = 0x501C,
470 UploadUrl = 0x501D,
472 Artist = 0x501E,
474 CopyrightInfo = 0x501F,
476 #[num_enum(catch_all)]
478 Unknown(u16),
479}
480
481#[cfg(test)]
482mod tests {
483 use super::*;
484
485 #[test]
486 fn from_extension_detection() {
487 assert_eq!(
489 ObjectFormatCode::from_extension("mp3"),
490 ObjectFormatCode::Mp3
491 );
492 assert_eq!(
493 ObjectFormatCode::from_extension("flac"),
494 ObjectFormatCode::FlacAudio
495 );
496 assert_eq!(
497 ObjectFormatCode::from_extension("aif"),
498 ObjectFormatCode::Aiff
499 ); assert_eq!(
503 ObjectFormatCode::from_extension("mp4"),
504 ObjectFormatCode::Mp4Container
505 );
506 assert_eq!(
507 ObjectFormatCode::from_extension("avi"),
508 ObjectFormatCode::Avi
509 );
510 assert_eq!(
511 ObjectFormatCode::from_extension("mpg"),
512 ObjectFormatCode::Mpeg
513 ); assert_eq!(
517 ObjectFormatCode::from_extension("jpg"),
518 ObjectFormatCode::Jpeg
519 );
520 assert_eq!(
521 ObjectFormatCode::from_extension("png"),
522 ObjectFormatCode::Png
523 );
524 assert_eq!(
525 ObjectFormatCode::from_extension("tif"),
526 ObjectFormatCode::Tiff
527 ); assert_eq!(
531 ObjectFormatCode::from_extension("txt"),
532 ObjectFormatCode::Text
533 );
534 assert_eq!(
535 ObjectFormatCode::from_extension("htm"),
536 ObjectFormatCode::Html
537 ); assert_eq!(
541 ObjectFormatCode::from_extension("exe"),
542 ObjectFormatCode::Executable
543 );
544 assert_eq!(
545 ObjectFormatCode::from_extension("sh"),
546 ObjectFormatCode::Script
547 );
548
549 assert_eq!(
551 ObjectFormatCode::from_extension("MP3"),
552 ObjectFormatCode::Mp3
553 );
554
555 assert_eq!(
557 ObjectFormatCode::from_extension("xyz"),
558 ObjectFormatCode::Undefined
559 );
560 assert_eq!(
561 ObjectFormatCode::from_extension(""),
562 ObjectFormatCode::Undefined
563 );
564 }
565
566 #[test]
569 fn is_audio() {
570 assert!(ObjectFormatCode::Mp3.is_audio());
571 assert!(ObjectFormatCode::FlacAudio.is_audio());
572 assert!(!ObjectFormatCode::Jpeg.is_audio());
573 assert!(!ObjectFormatCode::Mp4Container.is_audio());
574 }
575
576 #[test]
577 fn is_video() {
578 assert!(ObjectFormatCode::Mp4Container.is_video());
579 assert!(ObjectFormatCode::Avi.is_video());
580 assert!(!ObjectFormatCode::Mp3.is_video());
581 assert!(!ObjectFormatCode::Jpeg.is_video());
582 }
583
584 #[test]
585 fn is_image() {
586 assert!(ObjectFormatCode::Jpeg.is_image());
587 assert!(ObjectFormatCode::Png.is_image());
588 assert!(!ObjectFormatCode::Mp3.is_image());
589 assert!(!ObjectFormatCode::Mp4Container.is_image());
590 }
591
592 #[test]
593 fn format_categories_are_mutually_exclusive() {
594 let all_formats = [
595 ObjectFormatCode::Undefined,
596 ObjectFormatCode::Association,
597 ObjectFormatCode::Script,
598 ObjectFormatCode::Executable,
599 ObjectFormatCode::Text,
600 ObjectFormatCode::Html,
601 ObjectFormatCode::Dpof,
602 ObjectFormatCode::Aiff,
603 ObjectFormatCode::Wav,
604 ObjectFormatCode::Mp3,
605 ObjectFormatCode::Avi,
606 ObjectFormatCode::Mpeg,
607 ObjectFormatCode::Asf,
608 ObjectFormatCode::Jpeg,
609 ObjectFormatCode::Tiff,
610 ObjectFormatCode::Gif,
611 ObjectFormatCode::Bmp,
612 ObjectFormatCode::Pict,
613 ObjectFormatCode::Png,
614 ObjectFormatCode::WmaAudio,
615 ObjectFormatCode::OggAudio,
616 ObjectFormatCode::AacAudio,
617 ObjectFormatCode::FlacAudio,
618 ObjectFormatCode::WmvVideo,
619 ObjectFormatCode::Mp4Container,
620 ObjectFormatCode::M4aAudio,
621 ];
622
623 for format in all_formats {
624 let categories = [format.is_audio(), format.is_video(), format.is_image()];
625 let true_count = categories.iter().filter(|&&b| b).count();
626 assert!(
627 true_count <= 1,
628 "{:?} belongs to multiple categories",
629 format
630 );
631 }
632 }
633
634 #[test]
637 fn property_data_type_byte_size() {
638 assert_eq!(PropertyDataType::Int8.byte_size(), Some(1));
640 assert_eq!(PropertyDataType::Uint8.byte_size(), Some(1));
641 assert_eq!(PropertyDataType::Int16.byte_size(), Some(2));
642 assert_eq!(PropertyDataType::Uint16.byte_size(), Some(2));
643 assert_eq!(PropertyDataType::Int32.byte_size(), Some(4));
644 assert_eq!(PropertyDataType::Uint32.byte_size(), Some(4));
645 assert_eq!(PropertyDataType::Int64.byte_size(), Some(8));
646 assert_eq!(PropertyDataType::Uint64.byte_size(), Some(8));
647 assert_eq!(PropertyDataType::Int128.byte_size(), Some(16));
648 assert_eq!(PropertyDataType::Uint128.byte_size(), Some(16));
649
650 assert_eq!(PropertyDataType::String.byte_size(), None);
652 assert_eq!(PropertyDataType::Undefined.byte_size(), None);
653 assert_eq!(PropertyDataType::Unknown(0x1234).byte_size(), None);
654 }
655}