1use crate::error::DecodeError;
16use bytes::Bytes;
17use rust_decimal::Decimal;
18use serde::{Deserialize, Serialize};
19use std::fmt;
20use std::str::FromStr;
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
28#[repr(transparent)]
29#[serde(transparent)]
30pub struct FieldTag(u32);
31
32impl FieldTag {
33 #[inline]
38 #[must_use]
39 pub const fn new(tag: u32) -> Self {
40 Self(tag)
41 }
42
43 #[inline]
45 #[must_use]
46 pub const fn value(self) -> u32 {
47 self.0
48 }
49
50 #[inline]
52 #[must_use]
53 pub const fn is_standard(self) -> bool {
54 self.0 >= 1 && self.0 <= 5000
55 }
56
57 #[inline]
59 #[must_use]
60 pub const fn is_user_defined(self) -> bool {
61 self.0 > 5000
62 }
63}
64
65impl From<u32> for FieldTag {
66 fn from(tag: u32) -> Self {
67 Self(tag)
68 }
69}
70
71impl From<FieldTag> for u32 {
72 fn from(tag: FieldTag) -> Self {
73 tag.0
74 }
75}
76
77impl fmt::Display for FieldTag {
78 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79 write!(f, "{}", self.0)
80 }
81}
82
83#[derive(Debug, Clone, Copy)]
88pub struct FieldRef<'a> {
89 pub tag: u32,
91 pub value: &'a [u8],
93}
94
95impl<'a> FieldRef<'a> {
96 #[inline]
102 #[must_use]
103 pub const fn new(tag: u32, value: &'a [u8]) -> Self {
104 Self { tag, value }
105 }
106
107 #[inline]
109 #[must_use]
110 pub const fn tag(&self) -> FieldTag {
111 FieldTag(self.tag)
112 }
113
114 pub fn as_str(&self) -> Result<&'a str, DecodeError> {
119 std::str::from_utf8(self.value).map_err(DecodeError::from)
120 }
121
122 pub fn to_string(&self) -> Result<String, DecodeError> {
127 self.as_str().map(String::from)
128 }
129
130 pub fn parse<T: FromStr>(&self) -> Result<T, DecodeError> {
135 let s = self.as_str()?;
136 s.parse().map_err(|_| DecodeError::InvalidFieldValue {
137 tag: self.tag,
138 reason: format!("failed to parse '{}' as {}", s, std::any::type_name::<T>()),
139 })
140 }
141
142 pub fn as_u64(&self) -> Result<u64, DecodeError> {
147 self.parse()
148 }
149
150 pub fn as_i64(&self) -> Result<i64, DecodeError> {
155 self.parse()
156 }
157
158 pub fn as_decimal(&self) -> Result<Decimal, DecodeError> {
163 self.parse()
164 }
165
166 pub fn as_bool(&self) -> Result<bool, DecodeError> {
171 match self.value {
172 b"Y" => Ok(true),
173 b"N" => Ok(false),
174 _ => Err(DecodeError::InvalidFieldValue {
175 tag: self.tag,
176 reason: "expected 'Y' or 'N'".to_string(),
177 }),
178 }
179 }
180
181 pub fn as_char(&self) -> Result<char, DecodeError> {
186 if self.value.len() == 1 && self.value[0].is_ascii() {
187 Ok(self.value[0] as char)
188 } else {
189 Err(DecodeError::InvalidFieldValue {
190 tag: self.tag,
191 reason: "expected single ASCII character".to_string(),
192 })
193 }
194 }
195
196 #[inline]
198 #[must_use]
199 pub const fn as_bytes(&self) -> &'a [u8] {
200 self.value
201 }
202
203 #[inline]
205 #[must_use]
206 pub const fn len(&self) -> usize {
207 self.value.len()
208 }
209
210 #[inline]
212 #[must_use]
213 pub const fn is_empty(&self) -> bool {
214 self.value.is_empty()
215 }
216}
217
218#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
220pub enum FieldValue {
221 String(String),
223 Int(i64),
225 UInt(u64),
227 Decimal(Decimal),
229 Bool(bool),
231 Char(char),
233 Data(Bytes),
235}
236
237impl FieldValue {
238 #[must_use]
240 pub fn as_str(&self) -> Option<&str> {
241 match self {
242 Self::String(s) => Some(s),
243 _ => None,
244 }
245 }
246
247 #[must_use]
249 pub const fn as_i64(&self) -> Option<i64> {
250 match self {
251 Self::Int(v) => Some(*v),
252 _ => None,
253 }
254 }
255
256 #[must_use]
258 pub const fn as_u64(&self) -> Option<u64> {
259 match self {
260 Self::UInt(v) => Some(*v),
261 _ => None,
262 }
263 }
264
265 #[must_use]
267 pub const fn as_decimal(&self) -> Option<Decimal> {
268 match self {
269 Self::Decimal(v) => Some(*v),
270 _ => None,
271 }
272 }
273
274 #[must_use]
276 pub const fn as_bool(&self) -> Option<bool> {
277 match self {
278 Self::Bool(v) => Some(*v),
279 _ => None,
280 }
281 }
282
283 #[must_use]
285 pub const fn as_char(&self) -> Option<char> {
286 match self {
287 Self::Char(v) => Some(*v),
288 _ => None,
289 }
290 }
291}
292
293impl fmt::Display for FieldValue {
294 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
295 match self {
296 Self::String(s) => write!(f, "{}", s),
297 Self::Int(v) => write!(f, "{}", v),
298 Self::UInt(v) => write!(f, "{}", v),
299 Self::Decimal(v) => write!(f, "{}", v),
300 Self::Bool(v) => write!(f, "{}", if *v { "Y" } else { "N" }),
301 Self::Char(c) => write!(f, "{}", c),
302 Self::Data(d) => write!(f, "<{} bytes>", d.len()),
303 }
304 }
305}
306
307pub trait FixField: Sized {
312 const TAG: u32;
314
315 type Value;
317
318 fn decode(bytes: &[u8]) -> Result<Self::Value, DecodeError>;
326
327 fn encode(value: &Self::Value, buf: &mut Vec<u8>);
333}
334
335#[cfg(test)]
336mod tests {
337 use super::*;
338
339 #[test]
340 fn test_field_tag() {
341 let tag = FieldTag::new(35);
342 assert_eq!(tag.value(), 35);
343 assert!(tag.is_standard());
344 assert!(!tag.is_user_defined());
345
346 let user_tag = FieldTag::new(5001);
347 assert!(!user_tag.is_standard());
348 assert!(user_tag.is_user_defined());
349 }
350
351 #[test]
352 fn test_field_ref_as_str() {
353 let field = FieldRef::new(11, b"ORDER123");
354 assert_eq!(field.as_str().unwrap(), "ORDER123");
355 }
356
357 #[test]
358 fn test_field_ref_as_u64() {
359 let field = FieldRef::new(34, b"12345");
360 assert_eq!(field.as_u64().unwrap(), 12345);
361 }
362
363 #[test]
364 fn test_field_ref_as_bool() {
365 let yes = FieldRef::new(141, b"Y");
366 let no = FieldRef::new(141, b"N");
367 assert!(yes.as_bool().unwrap());
368 assert!(!no.as_bool().unwrap());
369 }
370
371 #[test]
372 fn test_field_ref_as_char() {
373 let field = FieldRef::new(54, b"1");
374 assert_eq!(field.as_char().unwrap(), '1');
375 }
376
377 #[test]
378 fn test_field_ref_invalid_utf8() {
379 let field = FieldRef::new(1, &[0xFF, 0xFE]);
380 assert!(field.as_str().is_err());
381 }
382
383 #[test]
384 fn test_field_value_display() {
385 assert_eq!(FieldValue::String("test".to_string()).to_string(), "test");
386 assert_eq!(FieldValue::Int(42).to_string(), "42");
387 assert_eq!(FieldValue::Bool(true).to_string(), "Y");
388 assert_eq!(FieldValue::Bool(false).to_string(), "N");
389 }
390}