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 GetPartialObject64 = 0x95C1,
66 #[num_enum(catch_all)]
68 Unknown(u16),
69}
70
71#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, FromPrimitive, IntoPrimitive)]
75#[repr(u16)]
76pub enum ResponseCode {
77 Ok = 0x2001,
79 GeneralError = 0x2002,
81 SessionNotOpen = 0x2003,
83 InvalidTransactionId = 0x2004,
85 OperationNotSupported = 0x2005,
87 ParameterNotSupported = 0x2006,
89 IncompleteTransfer = 0x2007,
91 InvalidStorageId = 0x2008,
93 InvalidObjectHandle = 0x2009,
95 DevicePropNotSupported = 0x200A,
97 InvalidObjectFormatCode = 0x200B,
99 StoreFull = 0x200C,
101 ObjectWriteProtected = 0x200D,
103 StoreReadOnly = 0x200E,
105 AccessDenied = 0x200F,
107 NoThumbnailPresent = 0x2010,
109 DeviceBusy = 0x2019,
111 InvalidParentObject = 0x201A,
113 InvalidDevicePropFormat = 0x201B,
115 InvalidDevicePropValue = 0x201C,
117 InvalidParameter = 0x201D,
119 SessionAlreadyOpen = 0x201E,
121 TransactionCancelled = 0x201F,
123 ObjectTooLarge = 0xA809,
125 #[num_enum(catch_all)]
127 Unknown(u16),
128}
129
130#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, FromPrimitive, IntoPrimitive)]
134#[repr(u16)]
135pub enum EventCode {
136 CancelTransaction = 0x4001,
138 ObjectAdded = 0x4002,
140 ObjectRemoved = 0x4003,
142 StoreAdded = 0x4004,
144 StoreRemoved = 0x4005,
146 DevicePropChanged = 0x4006,
148 ObjectInfoChanged = 0x4007,
150 DeviceInfoChanged = 0x4008,
152 StorageInfoChanged = 0x400C,
154 CaptureComplete = 0x400D,
156 #[num_enum(catch_all)]
158 Unknown(u16),
159}
160
161#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, FromPrimitive, IntoPrimitive)]
165#[repr(u16)]
166pub enum ObjectFormatCode {
167 Undefined = 0x3000,
169 Association = 0x3001,
171 Script = 0x3002,
173 Executable = 0x3003,
175 Text = 0x3004,
177 Html = 0x3005,
179 Dpof = 0x3006,
181 Aiff = 0x3007,
183 Wav = 0x3008,
185 Mp3 = 0x3009,
187 Avi = 0x300A,
189 Mpeg = 0x300B,
191 Asf = 0x300C,
193 Jpeg = 0x3801,
195 Tiff = 0x3804,
197 Gif = 0x3807,
199 Bmp = 0x3808,
201 Pict = 0x380A,
203 Png = 0x380B,
205 WmaAudio = 0xB901,
207 OggAudio = 0xB902,
209 AacAudio = 0xB903,
211 FlacAudio = 0xB906,
213 WmvVideo = 0xB981,
215 Mp4Container = 0xB982,
217 M4aAudio = 0xB984,
219 #[num_enum(catch_all)]
221 Unknown(u16),
222}
223
224impl ObjectFormatCode {
225 #[must_use]
229 pub fn from_extension(ext: &str) -> Self {
230 match ext.to_lowercase().as_str() {
231 "txt" => ObjectFormatCode::Text,
233 "html" | "htm" => ObjectFormatCode::Html,
234 "dpof" => ObjectFormatCode::Dpof,
235
236 "aiff" | "aif" => ObjectFormatCode::Aiff,
238 "wav" => ObjectFormatCode::Wav,
239 "mp3" => ObjectFormatCode::Mp3,
240 "wma" => ObjectFormatCode::WmaAudio,
241 "ogg" | "oga" => ObjectFormatCode::OggAudio,
242 "aac" => ObjectFormatCode::AacAudio,
243 "flac" => ObjectFormatCode::FlacAudio,
244 "m4a" => ObjectFormatCode::M4aAudio,
245
246 "avi" => ObjectFormatCode::Avi,
248 "mpg" | "mpeg" => ObjectFormatCode::Mpeg,
249 "asf" => ObjectFormatCode::Asf,
250 "wmv" => ObjectFormatCode::WmvVideo,
251 "mp4" | "m4v" => ObjectFormatCode::Mp4Container,
252
253 "jpg" | "jpeg" => ObjectFormatCode::Jpeg,
255 "tif" | "tiff" => ObjectFormatCode::Tiff,
256 "gif" => ObjectFormatCode::Gif,
257 "bmp" => ObjectFormatCode::Bmp,
258 "pict" | "pct" => ObjectFormatCode::Pict,
259 "png" => ObjectFormatCode::Png,
260
261 "exe" | "dll" | "bin" => ObjectFormatCode::Executable,
263 "sh" | "bat" | "cmd" | "ps1" => ObjectFormatCode::Script,
264
265 _ => ObjectFormatCode::Undefined,
266 }
267 }
268
269 #[must_use]
271 pub fn is_audio(&self) -> bool {
272 matches!(
273 self,
274 ObjectFormatCode::Aiff
275 | ObjectFormatCode::Wav
276 | ObjectFormatCode::Mp3
277 | ObjectFormatCode::WmaAudio
278 | ObjectFormatCode::OggAudio
279 | ObjectFormatCode::AacAudio
280 | ObjectFormatCode::FlacAudio
281 | ObjectFormatCode::M4aAudio
282 )
283 }
284
285 #[must_use]
287 pub fn is_video(&self) -> bool {
288 matches!(
289 self,
290 ObjectFormatCode::Avi
291 | ObjectFormatCode::Mpeg
292 | ObjectFormatCode::Asf
293 | ObjectFormatCode::WmvVideo
294 | ObjectFormatCode::Mp4Container
295 )
296 }
297
298 #[must_use]
300 pub fn is_image(&self) -> bool {
301 matches!(
302 self,
303 ObjectFormatCode::Jpeg
304 | ObjectFormatCode::Tiff
305 | ObjectFormatCode::Gif
306 | ObjectFormatCode::Bmp
307 | ObjectFormatCode::Pict
308 | ObjectFormatCode::Png
309 )
310 }
311}
312
313#[allow(clippy::derivable_impls)]
315impl Default for ObjectFormatCode {
316 fn default() -> Self {
317 ObjectFormatCode::Undefined
318 }
319}
320
321#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, FromPrimitive, IntoPrimitive)]
325#[repr(u16)]
326pub enum ObjectPropertyCode {
327 StorageId = 0xDC01,
329 ObjectFormat = 0xDC02,
331 ProtectionStatus = 0xDC03,
333 ObjectSize = 0xDC04,
335 ObjectFileName = 0xDC07,
337 DateCreated = 0xDC08,
339 DateModified = 0xDC09,
341 ParentObject = 0xDC0B,
343 Name = 0xDC44,
345 #[num_enum(catch_all)]
347 Unknown(u16),
348}
349
350#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, FromPrimitive, IntoPrimitive)]
354#[repr(u16)]
355pub enum PropertyDataType {
356 Undefined = 0x0000,
358 Int8 = 0x0001,
360 Uint8 = 0x0002,
362 Int16 = 0x0003,
364 Uint16 = 0x0004,
366 Int32 = 0x0005,
368 Uint32 = 0x0006,
370 Int64 = 0x0007,
372 Uint64 = 0x0008,
374 Int128 = 0x0009,
376 Uint128 = 0x000A,
378 #[num_enum(catch_all)]
380 Unknown(u16),
381 String = 0xFFFF,
383}
384
385impl PropertyDataType {
386 #[must_use]
391 pub fn byte_size(&self) -> Option<usize> {
392 match self {
393 PropertyDataType::Int8 | PropertyDataType::Uint8 => Some(1),
394 PropertyDataType::Int16 | PropertyDataType::Uint16 => Some(2),
395 PropertyDataType::Int32 | PropertyDataType::Uint32 => Some(4),
396 PropertyDataType::Int64 | PropertyDataType::Uint64 => Some(8),
397 PropertyDataType::Int128 | PropertyDataType::Uint128 => Some(16),
398 PropertyDataType::String
399 | PropertyDataType::Undefined
400 | PropertyDataType::Unknown(_) => None,
401 }
402 }
403}
404
405#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, FromPrimitive, IntoPrimitive)]
415#[repr(u16)]
416pub enum DevicePropertyCode {
417 Undefined = 0x5000,
419 BatteryLevel = 0x5001,
421 FunctionalMode = 0x5002,
423 ImageSize = 0x5003,
425 CompressionSetting = 0x5004,
427 WhiteBalance = 0x5005,
429 RgbGain = 0x5006,
431 FNumber = 0x5007,
433 FocalLength = 0x5008,
435 FocusDistance = 0x5009,
437 FocusMode = 0x500A,
439 ExposureMeteringMode = 0x500B,
441 FlashMode = 0x500C,
443 ExposureTime = 0x500D,
445 ExposureProgramMode = 0x500E,
447 ExposureIndex = 0x500F,
449 ExposureBiasCompensation = 0x5010,
451 DateTime = 0x5011,
453 CaptureDelay = 0x5012,
455 StillCaptureMode = 0x5013,
457 Contrast = 0x5014,
459 Sharpness = 0x5015,
461 DigitalZoom = 0x5016,
463 EffectMode = 0x5017,
465 BurstNumber = 0x5018,
467 BurstInterval = 0x5019,
469 TimelapseNumber = 0x501A,
471 TimelapseInterval = 0x501B,
473 FocusMeteringMode = 0x501C,
475 UploadUrl = 0x501D,
477 Artist = 0x501E,
479 CopyrightInfo = 0x501F,
481 #[num_enum(catch_all)]
483 Unknown(u16),
484}
485
486#[cfg(test)]
487mod tests {
488 use super::*;
489
490 #[test]
491 fn from_extension_detection() {
492 assert_eq!(
494 ObjectFormatCode::from_extension("mp3"),
495 ObjectFormatCode::Mp3
496 );
497 assert_eq!(
498 ObjectFormatCode::from_extension("flac"),
499 ObjectFormatCode::FlacAudio
500 );
501 assert_eq!(
502 ObjectFormatCode::from_extension("aif"),
503 ObjectFormatCode::Aiff
504 ); assert_eq!(
508 ObjectFormatCode::from_extension("mp4"),
509 ObjectFormatCode::Mp4Container
510 );
511 assert_eq!(
512 ObjectFormatCode::from_extension("avi"),
513 ObjectFormatCode::Avi
514 );
515 assert_eq!(
516 ObjectFormatCode::from_extension("mpg"),
517 ObjectFormatCode::Mpeg
518 ); assert_eq!(
522 ObjectFormatCode::from_extension("jpg"),
523 ObjectFormatCode::Jpeg
524 );
525 assert_eq!(
526 ObjectFormatCode::from_extension("png"),
527 ObjectFormatCode::Png
528 );
529 assert_eq!(
530 ObjectFormatCode::from_extension("tif"),
531 ObjectFormatCode::Tiff
532 ); assert_eq!(
536 ObjectFormatCode::from_extension("txt"),
537 ObjectFormatCode::Text
538 );
539 assert_eq!(
540 ObjectFormatCode::from_extension("htm"),
541 ObjectFormatCode::Html
542 ); assert_eq!(
546 ObjectFormatCode::from_extension("exe"),
547 ObjectFormatCode::Executable
548 );
549 assert_eq!(
550 ObjectFormatCode::from_extension("sh"),
551 ObjectFormatCode::Script
552 );
553
554 assert_eq!(
556 ObjectFormatCode::from_extension("MP3"),
557 ObjectFormatCode::Mp3
558 );
559
560 assert_eq!(
562 ObjectFormatCode::from_extension("xyz"),
563 ObjectFormatCode::Undefined
564 );
565 assert_eq!(
566 ObjectFormatCode::from_extension(""),
567 ObjectFormatCode::Undefined
568 );
569 }
570
571 #[test]
574 fn is_audio() {
575 assert!(ObjectFormatCode::Mp3.is_audio());
576 assert!(ObjectFormatCode::FlacAudio.is_audio());
577 assert!(!ObjectFormatCode::Jpeg.is_audio());
578 assert!(!ObjectFormatCode::Mp4Container.is_audio());
579 }
580
581 #[test]
582 fn is_video() {
583 assert!(ObjectFormatCode::Mp4Container.is_video());
584 assert!(ObjectFormatCode::Avi.is_video());
585 assert!(!ObjectFormatCode::Mp3.is_video());
586 assert!(!ObjectFormatCode::Jpeg.is_video());
587 }
588
589 #[test]
590 fn is_image() {
591 assert!(ObjectFormatCode::Jpeg.is_image());
592 assert!(ObjectFormatCode::Png.is_image());
593 assert!(!ObjectFormatCode::Mp3.is_image());
594 assert!(!ObjectFormatCode::Mp4Container.is_image());
595 }
596
597 #[test]
598 fn format_categories_are_mutually_exclusive() {
599 let all_formats = [
600 ObjectFormatCode::Undefined,
601 ObjectFormatCode::Association,
602 ObjectFormatCode::Script,
603 ObjectFormatCode::Executable,
604 ObjectFormatCode::Text,
605 ObjectFormatCode::Html,
606 ObjectFormatCode::Dpof,
607 ObjectFormatCode::Aiff,
608 ObjectFormatCode::Wav,
609 ObjectFormatCode::Mp3,
610 ObjectFormatCode::Avi,
611 ObjectFormatCode::Mpeg,
612 ObjectFormatCode::Asf,
613 ObjectFormatCode::Jpeg,
614 ObjectFormatCode::Tiff,
615 ObjectFormatCode::Gif,
616 ObjectFormatCode::Bmp,
617 ObjectFormatCode::Pict,
618 ObjectFormatCode::Png,
619 ObjectFormatCode::WmaAudio,
620 ObjectFormatCode::OggAudio,
621 ObjectFormatCode::AacAudio,
622 ObjectFormatCode::FlacAudio,
623 ObjectFormatCode::WmvVideo,
624 ObjectFormatCode::Mp4Container,
625 ObjectFormatCode::M4aAudio,
626 ];
627
628 for format in all_formats {
629 let categories = [format.is_audio(), format.is_video(), format.is_image()];
630 let true_count = categories.iter().filter(|&&b| b).count();
631 assert!(
632 true_count <= 1,
633 "{:?} belongs to multiple categories",
634 format
635 );
636 }
637 }
638
639 #[test]
642 fn property_data_type_byte_size() {
643 assert_eq!(PropertyDataType::Int8.byte_size(), Some(1));
645 assert_eq!(PropertyDataType::Uint8.byte_size(), Some(1));
646 assert_eq!(PropertyDataType::Int16.byte_size(), Some(2));
647 assert_eq!(PropertyDataType::Uint16.byte_size(), Some(2));
648 assert_eq!(PropertyDataType::Int32.byte_size(), Some(4));
649 assert_eq!(PropertyDataType::Uint32.byte_size(), Some(4));
650 assert_eq!(PropertyDataType::Int64.byte_size(), Some(8));
651 assert_eq!(PropertyDataType::Uint64.byte_size(), Some(8));
652 assert_eq!(PropertyDataType::Int128.byte_size(), Some(16));
653 assert_eq!(PropertyDataType::Uint128.byte_size(), Some(16));
654
655 assert_eq!(PropertyDataType::String.byte_size(), None);
657 assert_eq!(PropertyDataType::Undefined.byte_size(), None);
658 assert_eq!(PropertyDataType::Unknown(0x1234).byte_size(), None);
659 }
660}