1use crate::value::Value;
2
3#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5pub struct TagGroup {
6 pub family0: String,
8 pub family1: String,
10 pub family2: String,
12}
13
14#[derive(Debug, Clone)]
16pub struct Tag {
17 pub id: TagId,
19 pub name: String,
21 pub description: String,
23 pub group: TagGroup,
25 pub raw_value: Value,
27 pub print_value: String,
29 pub priority: i32,
31}
32
33impl Tag {
34 pub fn display_value(&self, numeric: bool) -> String {
38 if numeric {
39 self.raw_value.to_display_string()
40 } else {
41 self.print_value.clone()
42 }
43 }
44}
45
46#[derive(Debug, Clone, PartialEq, Eq, Hash)]
48pub enum TagId {
49 Numeric(u16),
51 Text(String),
53}
54
55impl std::fmt::Display for TagId {
56 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57 match self {
58 TagId::Numeric(id) => write!(f, "0x{:04x}", id),
59 TagId::Text(s) => write!(f, "{}", s),
60 }
61 }
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67
68 fn make_tag(raw: Value, print: &str) -> Tag {
69 Tag {
70 id: TagId::Numeric(0x0001),
71 name: "TestTag".to_string(),
72 description: "Test Tag".to_string(),
73 group: TagGroup {
74 family0: "EXIF".to_string(),
75 family1: "IFD0".to_string(),
76 family2: "Image".to_string(),
77 },
78 raw_value: raw,
79 print_value: print.to_string(),
80 priority: 0,
81 }
82 }
83
84 #[test]
87 fn tag_id_numeric_display_low() {
88 assert_eq!(format!("{}", TagId::Numeric(0x0001)), "0x0001");
89 }
90
91 #[test]
92 fn tag_id_numeric_display_hex() {
93 assert_eq!(format!("{}", TagId::Numeric(0x00FF)), "0x00ff");
94 }
95
96 #[test]
97 fn tag_id_numeric_display_zero() {
98 assert_eq!(format!("{}", TagId::Numeric(0)), "0x0000");
99 }
100
101 #[test]
102 fn tag_id_numeric_display_max() {
103 assert_eq!(format!("{}", TagId::Numeric(0xFFFF)), "0xffff");
104 }
105
106 #[test]
107 fn tag_id_text_display() {
108 assert_eq!(format!("{}", TagId::Text("dc:title".into())), "dc:title");
109 }
110
111 #[test]
112 fn tag_id_text_display_empty() {
113 assert_eq!(format!("{}", TagId::Text(String::new())), "");
114 }
115
116 #[test]
119 fn display_value_numeric_true_returns_raw() {
120 let tag = make_tag(Value::URational(1, 100), "0.01 s");
121 assert_eq!(tag.display_value(true), "1/100");
122 }
123
124 #[test]
125 fn display_value_numeric_false_returns_print() {
126 let tag = make_tag(Value::URational(1, 100), "0.01 s");
127 assert_eq!(tag.display_value(false), "0.01 s");
128 }
129
130 #[test]
131 fn display_value_string_raw() {
132 let tag = make_tag(Value::String("Canon EOS R5".into()), "Canon EOS R5");
133 assert_eq!(tag.display_value(true), "Canon EOS R5");
134 assert_eq!(tag.display_value(false), "Canon EOS R5");
135 }
136
137 #[test]
140 fn tag_id_equality() {
141 assert_eq!(TagId::Numeric(42), TagId::Numeric(42));
142 assert_ne!(TagId::Numeric(1), TagId::Numeric(2));
143 assert_eq!(TagId::Text("foo".into()), TagId::Text("foo".into()));
144 assert_ne!(TagId::Numeric(1), TagId::Text("1".into()));
145 }
146
147 #[test]
150 fn tag_group_equality() {
151 let g1 = TagGroup {
152 family0: "EXIF".into(),
153 family1: "IFD0".into(),
154 family2: "Image".into(),
155 };
156 let g2 = g1.clone();
157 assert_eq!(g1, g2);
158 }
159}