1use crate::error::{Error, Result};
4use crate::field_type::BpsvFieldType;
5use std::fmt;
6
7#[derive(Debug, Clone, PartialEq)]
9#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
10pub enum BpsvValue {
11 String(String),
13 Hex(String),
15 Decimal(i64),
17 Empty,
19}
20
21impl BpsvValue {
22 pub fn parse(value: &str, field_type: &BpsvFieldType) -> Result<Self> {
43 if value.is_empty() {
44 return Ok(Self::Empty);
45 }
46
47 match field_type {
48 BpsvFieldType::String(_) => {
49 if !field_type.is_valid_value(value) {
50 return Err(Error::InvalidValue {
51 field: "unknown".to_string(),
52 field_type: field_type.to_string(),
53 value: value.to_string(),
54 });
55 }
56 Ok(Self::String(value.to_string()))
57 }
58 BpsvFieldType::Hex(_) => {
59 if value.is_empty() {
61 return Ok(Self::Empty);
62 }
63 if !field_type.is_valid_value(value) {
64 return Err(Error::InvalidHex {
65 value: value.to_string(),
66 });
67 }
68 Ok(Self::Hex(value.to_lowercase()))
69 }
70 BpsvFieldType::Decimal(_) => {
71 if value.is_empty() {
73 return Ok(Self::Empty);
74 }
75 let num = value.parse::<i64>().map_err(|_| Error::InvalidNumber {
76 value: value.to_string(),
77 })?;
78 Ok(Self::Decimal(num))
79 }
80 }
81 }
82
83 pub fn to_bpsv_string(&self) -> String {
85 match self {
86 Self::String(s) => s.clone(),
87 Self::Hex(h) => h.clone(),
88 Self::Decimal(d) => d.to_string(),
89 Self::Empty => String::new(),
90 }
91 }
92
93 pub fn is_empty(&self) -> bool {
95 matches!(self, Self::Empty)
96 }
97
98 pub fn as_string(&self) -> Option<&str> {
100 match self {
101 Self::String(s) => Some(s),
102 _ => None,
103 }
104 }
105
106 pub fn as_hex(&self) -> Option<&str> {
108 match self {
109 Self::Hex(h) => Some(h),
110 _ => None,
111 }
112 }
113
114 pub fn as_decimal(&self) -> Option<i64> {
116 match self {
117 Self::Decimal(d) => Some(*d),
118 _ => None,
119 }
120 }
121
122 pub fn into_string(self) -> Option<String> {
124 match self {
125 Self::String(s) => Some(s),
126 _ => None,
127 }
128 }
129
130 pub fn into_hex(self) -> Option<String> {
132 match self {
133 Self::Hex(h) => Some(h),
134 _ => None,
135 }
136 }
137
138 pub fn into_decimal(self) -> Option<i64> {
140 match self {
141 Self::Decimal(d) => Some(d),
142 _ => None,
143 }
144 }
145
146 pub fn value_type(&self) -> &'static str {
148 match self {
149 Self::String(_) => "String",
150 Self::Hex(_) => "Hex",
151 Self::Decimal(_) => "Decimal",
152 Self::Empty => "Empty",
153 }
154 }
155
156 pub fn is_compatible_with(&self, field_type: &BpsvFieldType) -> bool {
158 match (self, field_type) {
159 (Self::String(_), BpsvFieldType::String(_)) => true,
160 (Self::Hex(_), BpsvFieldType::Hex(_)) => true,
161 (Self::Decimal(_), BpsvFieldType::Decimal(_)) => true,
162 (Self::Empty, _) => true, _ => false,
164 }
165 }
166}
167
168impl fmt::Display for BpsvValue {
169 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170 write!(f, "{}", self.to_bpsv_string())
171 }
172}
173
174impl From<String> for BpsvValue {
175 fn from(s: String) -> Self {
176 if s.is_empty() {
177 Self::Empty
178 } else {
179 Self::String(s)
180 }
181 }
182}
183
184impl From<&str> for BpsvValue {
185 fn from(s: &str) -> Self {
186 if s.is_empty() {
187 Self::Empty
188 } else {
189 Self::String(s.to_string())
190 }
191 }
192}
193
194impl From<i64> for BpsvValue {
195 fn from(i: i64) -> Self {
196 Self::Decimal(i)
197 }
198}
199
200impl From<i32> for BpsvValue {
201 fn from(i: i32) -> Self {
202 Self::Decimal(i64::from(i))
203 }
204}
205
206impl From<u32> for BpsvValue {
207 fn from(i: u32) -> Self {
208 Self::Decimal(i64::from(i))
209 }
210}
211
212impl From<u64> for BpsvValue {
213 fn from(i: u64) -> Self {
214 #[allow(clippy::cast_possible_wrap)]
215 Self::Decimal(i as i64)
216 }
217}
218
219impl TryFrom<BpsvValue> for String {
220 type Error = Error;
221
222 fn try_from(value: BpsvValue) -> Result<Self> {
223 match value {
224 BpsvValue::String(s) => Ok(s),
225 BpsvValue::Empty => Ok(String::new()),
226 _ => Err(Error::InvalidValue {
227 field: "unknown".to_string(),
228 field_type: "String".to_string(),
229 value: value.to_bpsv_string(),
230 }),
231 }
232 }
233}
234
235impl TryFrom<BpsvValue> for i64 {
236 type Error = Error;
237
238 fn try_from(value: BpsvValue) -> Result<Self> {
239 match value {
240 BpsvValue::Decimal(d) => Ok(d),
241 _ => Err(Error::InvalidValue {
242 field: "unknown".to_string(),
243 field_type: "Decimal".to_string(),
244 value: value.to_bpsv_string(),
245 }),
246 }
247 }
248}
249
250#[cfg(test)]
251mod tests {
252 use super::*;
253
254 #[test]
255 fn test_parse_values() {
256 let string_type = BpsvFieldType::String(0);
257 assert_eq!(
258 BpsvValue::parse("hello", &string_type).unwrap(),
259 BpsvValue::String("hello".to_string())
260 );
261
262 let hex_type = BpsvFieldType::Hex(4); assert_eq!(
264 BpsvValue::parse("ABCD1234", &hex_type).unwrap(),
265 BpsvValue::Hex("abcd1234".to_string())
266 );
267
268 let dec_type = BpsvFieldType::Decimal(4);
269 assert_eq!(
270 BpsvValue::parse("1234", &dec_type).unwrap(),
271 BpsvValue::Decimal(1234)
272 );
273
274 assert_eq!(
275 BpsvValue::parse("", &string_type).unwrap(),
276 BpsvValue::Empty
277 );
278 }
279
280 #[test]
281 fn test_invalid_values() {
282 let hex_type = BpsvFieldType::Hex(4);
283 assert!(BpsvValue::parse("xyz", &hex_type).is_err());
284
285 let dec_type = BpsvFieldType::Decimal(4);
286 assert!(BpsvValue::parse("abc", &dec_type).is_err());
287 }
288
289 #[test]
290 fn test_conversions() {
291 let string_val: BpsvValue = "hello".into();
292 assert_eq!(string_val, BpsvValue::String("hello".to_string()));
293
294 let num_val: BpsvValue = 1234i64.into();
295 assert_eq!(num_val, BpsvValue::Decimal(1234));
296
297 let empty_val: BpsvValue = "".into();
298 assert_eq!(empty_val, BpsvValue::Empty);
299 }
300
301 #[test]
302 fn test_accessors() {
303 let string_val = BpsvValue::String("hello".to_string());
304 assert_eq!(string_val.as_string(), Some("hello"));
305 assert_eq!(string_val.as_hex(), None);
306 assert_eq!(string_val.as_decimal(), None);
307
308 let hex_val = BpsvValue::Hex("abcd".to_string());
309 assert_eq!(hex_val.as_hex(), Some("abcd"));
310 assert_eq!(hex_val.as_string(), None);
311
312 let dec_val = BpsvValue::Decimal(1234);
313 assert_eq!(dec_val.as_decimal(), Some(1234));
314 assert_eq!(dec_val.as_string(), None);
315 }
316
317 #[test]
318 fn test_compatibility() {
319 let string_val = BpsvValue::String("hello".to_string());
320 let string_type = BpsvFieldType::String(0);
321 let hex_type = BpsvFieldType::Hex(4);
322
323 assert!(string_val.is_compatible_with(&string_type));
324 assert!(!string_val.is_compatible_with(&hex_type));
325
326 let empty_val = BpsvValue::Empty;
327 assert!(empty_val.is_compatible_with(&string_type));
328 assert!(empty_val.is_compatible_with(&hex_type));
329 }
330
331 #[test]
332 fn test_to_bpsv_string() {
333 assert_eq!(
334 BpsvValue::String("hello".to_string()).to_bpsv_string(),
335 "hello"
336 );
337 assert_eq!(BpsvValue::Hex("abcd".to_string()).to_bpsv_string(), "abcd");
338 assert_eq!(BpsvValue::Decimal(1234).to_bpsv_string(), "1234");
339 assert_eq!(BpsvValue::Empty.to_bpsv_string(), "");
340 }
341}