Skip to main content

quickfix_tokio/
error.rs

1use crate::message::Tag;
2
3/// Session-level reject reasons (tag 373) as defined by the FIX spec.
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub enum SessionRejectReason {
6    InvalidTagNumber,
7    RequiredTagMissing,
8    TagNotDefinedForThisMessageType,
9    UndefinedTag,
10    TagSpecifiedWithoutAValue,
11    ValueIsIncorrect,
12    IncorrectDataFormatForValue,
13    DecryptionProblem,
14    SignatureProblem,
15    CompIDProblem,
16    SendingTimeAccuracyProblem,
17    InvalidMsgType,
18    XMLValidationError,
19    TagAppearsMoreThanOnce,
20    TagSpecifiedOutOfRequiredOrder,
21    RepeatingGroupFieldsOutOfOrder,
22    IncorrectNumInGroupCountForRepeatingGroup,
23    NonDataValueIncludesFieldDelimiter,
24    Other,
25}
26
27impl SessionRejectReason {
28    pub fn code(&self) -> u32 {
29        match self {
30            Self::InvalidTagNumber => 0,
31            Self::RequiredTagMissing => 1,
32            Self::TagNotDefinedForThisMessageType => 2,
33            Self::UndefinedTag => 3,
34            Self::TagSpecifiedWithoutAValue => 4,
35            Self::ValueIsIncorrect => 5,
36            Self::IncorrectDataFormatForValue => 6,
37            Self::DecryptionProblem => 7,
38            Self::SignatureProblem => 8,
39            Self::CompIDProblem => 9,
40            Self::SendingTimeAccuracyProblem => 10,
41            Self::InvalidMsgType => 11,
42            Self::XMLValidationError => 12,
43            Self::TagAppearsMoreThanOnce => 13,
44            Self::TagSpecifiedOutOfRequiredOrder => 14,
45            Self::RepeatingGroupFieldsOutOfOrder => 15,
46            Self::IncorrectNumInGroupCountForRepeatingGroup => 16,
47            Self::NonDataValueIncludesFieldDelimiter => 17,
48            Self::Other => 99,
49        }
50    }
51
52    pub fn text(&self) -> &'static str {
53        match self {
54            Self::InvalidTagNumber => "Invalid tag number",
55            Self::RequiredTagMissing => "Required tag missing",
56            Self::TagNotDefinedForThisMessageType => "Tag not defined for this message type",
57            Self::UndefinedTag => "Undefined tag",
58            Self::TagSpecifiedWithoutAValue => "Tag specified without a value",
59            Self::ValueIsIncorrect => "Value is incorrect (out of range) for this tag",
60            Self::IncorrectDataFormatForValue => "Incorrect data format for value",
61            Self::DecryptionProblem => "Decryption problem",
62            Self::SignatureProblem => "Signature problem",
63            Self::CompIDProblem => "CompID problem",
64            Self::SendingTimeAccuracyProblem => "SendingTime accuracy problem",
65            Self::InvalidMsgType => "Invalid MsgType",
66            Self::XMLValidationError => "XML validation error",
67            Self::TagAppearsMoreThanOnce => "Tag appears more than once",
68            Self::TagSpecifiedOutOfRequiredOrder => "Tag specified out of required order",
69            Self::RepeatingGroupFieldsOutOfOrder => "Repeating group fields out of order",
70            Self::IncorrectNumInGroupCountForRepeatingGroup => {
71                "Incorrect NumInGroup count for repeating group"
72            }
73            Self::NonDataValueIncludesFieldDelimiter => {
74                "Non-data value includes field delimiter (SOH character)"
75            }
76            Self::Other => "Other",
77        }
78    }
79}
80
81/// A message failed validation and should be answered with a session-level Reject (35=3).
82///
83/// Displays as the bare reason text (the offending tag goes in RefTagID(371),
84/// not in Text(58), matching the reference engines).
85#[derive(Debug, Clone, thiserror::Error)]
86#[error("{}", .text.as_deref().unwrap_or(self.reason.text()))]
87pub struct RejectError {
88    pub reason: SessionRejectReason,
89    pub ref_tag: Option<Tag>,
90    /// Overrides the standard reason text in Text(58) when set.
91    pub text: Option<String>,
92    /// True when the offending message should NOT increment NextTargetMsgSeqNum
93    /// (e.g. garbled messages per the spec are ignored, not rejected).
94    pub is_garbled: bool,
95}
96
97impl RejectError {
98    pub fn new(reason: SessionRejectReason) -> Self {
99        Self { reason, ref_tag: None, text: None, is_garbled: false }
100    }
101    pub fn with_tag(reason: SessionRejectReason, tag: Tag) -> Self {
102        Self { reason, ref_tag: Some(tag), text: None, is_garbled: false }
103    }
104    /// SessionRejectReason "Other" (99) with a custom Text(58).
105    pub fn other(text: impl Into<String>, ref_tag: Tag) -> Self {
106        Self {
107            reason: SessionRejectReason::Other,
108            ref_tag: Some(ref_tag),
109            text: Some(text.into()),
110            is_garbled: false,
111        }
112    }
113}
114
115/// Errors converting a field value to/from its wire representation.
116#[derive(Debug, Clone, thiserror::Error)]
117pub enum ConversionError {
118    #[error("field {tag} not found")]
119    FieldNotFound { tag: Tag },
120    #[error("cannot convert value {value:?} for tag {tag}")]
121    InvalidValue { tag: Tag, value: String },
122}
123
124#[derive(Debug, thiserror::Error)]
125pub enum Error {
126    #[error("message parse error: {0}")]
127    Parse(String),
128    #[error(transparent)]
129    Conversion(#[from] ConversionError),
130    #[error(transparent)]
131    Reject(#[from] RejectError),
132    #[error("session {0} not found")]
133    UnknownSession(String),
134    #[error("session {0} is not logged on")]
135    NotLoggedOn(String),
136    #[error("configuration error: {0}")]
137    Config(String),
138    #[error("TLS error: {0}")]
139    Tls(String),
140    #[error("data dictionary error: {0}")]
141    Dictionary(String),
142    #[error("store error: {0}")]
143    Store(String),
144    #[error("do not send")]
145    DoNotSend,
146    #[error(transparent)]
147    Io(#[from] std::io::Error),
148}
149
150pub type Result<T> = std::result::Result<T, Error>;