1use thiserror::Error;
2
3#[derive(Error, Debug, Clone, Copy, PartialEq, Eq)]
5pub enum MalformedFix {
6 #[error("Invalid FIX message framing")]
7 InvalidMessage,
8 #[error("Invalid FIX format")]
9 InvalidFormat,
10 #[error("Missing separator")]
11 MissingSeparator,
12 #[error("BodyLength mismatch")]
13 BodyLengthMismatch,
14 #[error("Checksum mismatch")]
15 ChecksumMismatch,
16 #[error("Non-ASCII byte in FIX message")]
17 NonAsciiByte,
18}
19
20#[derive(Error, Debug)]
21pub enum FixError {
22 #[error("{0}")]
24 Malformed(#[from] MalformedFix),
25
26 #[error("Invalid value for tag {tag}{ctx}")]
28 InvalidValue { tag: u32, ctx: String },
29
30 #[error("Invalid tag {0}")]
31 InvalidTag(u32),
32
33 #[error("Invalid enum value for tag {tag} ({ty})")]
34 InvalidEnumValue {
35 tag: u32,
36 ty: &'static str,
38 },
39 #[error("Missing required field {name} (tag {tag})")]
40 MissingField { name: &'static str, tag: u32 },
41 #[error("Missing required component {0}")]
42 MissingComponent(&'static str),
43 #[error("UTF-8 parsing error")]
44 Utf8Error(#[from] std::str::Utf8Error),
45 #[error("DateTime parsing error")]
46 DateTimeError(#[from] chrono::ParseError),
47}
48
49impl FixError {
50 #[inline]
51 pub fn invalid_value(tag: u32) -> Self {
52 Self::InvalidValue {
53 tag,
54 ctx: String::new(),
55 }
56 }
57
58 #[inline]
59 pub fn invalid_value_ctx(tag: u32, bytes: &[u8]) -> Self {
60 const MAX: usize = 48;
61 let mut s = String::from_utf8_lossy(bytes).into_owned();
62 if s.len() > MAX {
63 s.truncate(MAX);
64 s.push('…');
65 }
66 Self::InvalidValue {
67 tag,
68 ctx: format!(" (value: {s})"),
69 }
70 }
71
72 #[inline]
73 pub fn invalid_enum_value(tag: u32, ty: &'static str) -> Self {
74 Self::InvalidEnumValue { tag, ty }
75 }
76}