Skip to main content

exiftool_rs/
value.rs

1use std::fmt;
2
3/// Represents a metadata tag value, which can be of various types.
4#[derive(Debug, Clone, PartialEq)]
5pub enum Value {
6    /// ASCII/UTF-8 string
7    String(String),
8    /// Unsigned 8-bit integer
9    U8(u8),
10    /// Unsigned 16-bit integer
11    U16(u16),
12    /// Unsigned 32-bit integer
13    U32(u32),
14    /// Signed 16-bit integer
15    I16(i16),
16    /// Signed 32-bit integer
17    I32(i32),
18    /// Unsigned rational (numerator/denominator)
19    URational(u32, u32),
20    /// Signed rational (numerator/denominator)
21    IRational(i32, i32),
22    /// 32-bit float
23    F32(f32),
24    /// 64-bit float
25    F64(f64),
26    /// Raw binary data
27    Binary(Vec<u8>),
28    /// A list of values (e.g., GPS coordinates, color space arrays)
29    List(Vec<Value>),
30    /// Undefined/opaque bytes with a semantic type hint
31    Undefined(Vec<u8>),
32}
33
34impl Value {
35    /// Convert to string representation (PrintConv equivalent).
36    pub fn to_display_string(&self) -> String {
37        match self {
38            Value::String(s) => s.clone(),
39            Value::U8(v) => v.to_string(),
40            Value::U16(v) => v.to_string(),
41            Value::U32(v) => v.to_string(),
42            Value::I16(v) => v.to_string(),
43            Value::I32(v) => v.to_string(),
44            Value::URational(n, d) => {
45                if *d == 0 {
46                    if *n == 0 { "undef".to_string() } else { "inf".to_string() }
47                } else if *n % *d == 0 {
48                    (*n / *d).to_string()
49                } else {
50                    format!("{}/{}", n, d)
51                }
52            }
53            Value::IRational(n, d) => {
54                if *d == 0 {
55                    if *n >= 0 { "inf".to_string() } else { "-inf".to_string() }
56                } else if *n % *d == 0 {
57                    (*n / *d).to_string()
58                } else {
59                    format!("{}/{}", n, d)
60                }
61            }
62            Value::F32(v) => format!("{}", v),
63            Value::F64(v) => format!("{}", v),
64            Value::Binary(data) => format!("(Binary data {} bytes)", data.len()),
65            Value::List(items) => {
66                items
67                    .iter()
68                    .map(|v| v.to_display_string())
69                    .collect::<Vec<_>>()
70                    .join(", ")
71            }
72            Value::Undefined(data) => format!("(Undefined {} bytes)", data.len()),
73        }
74    }
75
76    /// Try to interpret the value as a float.
77    pub fn as_f64(&self) -> Option<f64> {
78        match self {
79            Value::U8(v) => Some(*v as f64),
80            Value::U16(v) => Some(*v as f64),
81            Value::U32(v) => Some(*v as f64),
82            Value::I16(v) => Some(*v as f64),
83            Value::I32(v) => Some(*v as f64),
84            Value::F32(v) => Some(*v as f64),
85            Value::F64(v) => Some(*v),
86            Value::URational(n, d) if *d != 0 => Some(*n as f64 / *d as f64),
87            Value::IRational(n, d) if *d != 0 => Some(*n as f64 / *d as f64),
88            _ => None,
89        }
90    }
91
92    /// Try to interpret the value as a string.
93    pub fn as_str(&self) -> Option<&str> {
94        match self {
95            Value::String(s) => Some(s),
96            _ => None,
97        }
98    }
99
100    /// Try to interpret the value as an unsigned integer.
101    pub fn as_u64(&self) -> Option<u64> {
102        match self {
103            Value::U8(v) => Some(*v as u64),
104            Value::U16(v) => Some(*v as u64),
105            Value::U32(v) => Some(*v as u64),
106            _ => None,
107        }
108    }
109}
110
111impl fmt::Display for Value {
112    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113        write!(f, "{}", self.to_display_string())
114    }
115}