nautilus_model/identifiers/
trade_id.rs1use std::{
19 ffi::CStr,
20 fmt::{Debug, Display},
21 hash::Hash,
22};
23
24use nautilus_core::StackStr;
25use serde::{Deserialize, Deserializer, Serialize, Serializer};
26
27#[repr(C)]
36#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
37#[cfg_attr(
38 feature = "python",
39 pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.model")
40)]
41pub struct TradeId(StackStr);
42
43impl TradeId {
44 pub fn new_checked<T: AsRef<str>>(value: T) -> anyhow::Result<Self> {
58 Ok(Self(StackStr::new_checked(value.as_ref())?))
59 }
60
61 pub fn new<T: AsRef<str>>(value: T) -> Self {
71 Self(StackStr::new(value.as_ref()))
72 }
73
74 pub fn from_bytes(bytes: &[u8]) -> anyhow::Result<Self> {
81 Ok(Self(StackStr::from_bytes(bytes)?))
82 }
83
84 #[inline]
86 #[must_use]
87 pub fn as_str(&self) -> &str {
88 self.0.as_str()
89 }
90
91 #[inline]
93 #[must_use]
94 pub fn as_cstr(&self) -> &CStr {
95 self.0.as_cstr()
96 }
97}
98
99impl Debug for TradeId {
100 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101 write!(f, "{}('{}')", stringify!(TradeId), self)
102 }
103}
104
105impl Display for TradeId {
106 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107 write!(f, "{}", self.0)
108 }
109}
110
111impl Serialize for TradeId {
112 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
113 where
114 S: Serializer,
115 {
116 self.0.serialize(serializer)
117 }
118}
119
120impl<'de> Deserialize<'de> for TradeId {
121 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
122 where
123 D: Deserializer<'de>,
124 {
125 let inner = StackStr::deserialize(deserializer)?;
126 Ok(Self(inner))
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use rstest::rstest;
133
134 use crate::identifiers::{TradeId, stubs::*};
135
136 #[rstest]
137 fn test_trade_id_new_valid() {
138 let trade_id = TradeId::new("TRADE12345");
139 assert_eq!(trade_id.to_string(), "TRADE12345");
140 }
141
142 #[rstest]
143 #[should_panic(expected = "exceeds maximum length")]
144 fn test_trade_id_new_invalid_length() {
145 let _ = TradeId::new("A".repeat(37).as_str());
146 }
147
148 #[rstest]
149 #[case(b"1234567890", "1234567890")]
150 #[case(b"ABCDEFGHIJKLMNOPQRSTUVWXYZ1234", "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234")] #[case(b"1234567890\0", "1234567890")]
152 #[case(b"ABCDEFGHIJKLMNOPQRSTUVWXYZ1234\0", "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234")] fn test_trade_id_from_valid_bytes(#[case] input: &[u8], #[case] expected: &str) {
154 let trade_id = TradeId::from_bytes(input).unwrap();
155 assert_eq!(trade_id.to_string(), expected);
156 }
157
158 #[rstest]
159 #[should_panic(expected = "String is empty")]
160 fn test_trade_id_from_bytes_empty() {
161 TradeId::from_bytes(&[] as &[u8]).unwrap();
162 }
163
164 #[rstest]
165 #[should_panic(expected = "String is empty")]
166 fn test_trade_id_single_null_byte() {
167 TradeId::from_bytes(&[0u8] as &[u8]).unwrap();
168 }
169
170 #[rstest]
171 #[case(b"ABCDEFGHIJKLMNOPQRSTUVWXYZ12345678901")] #[case(b"ABCDEFGHIJKLMNOPQRSTUVWXYZ12345678901\0")] #[should_panic(expected = "exceeds maximum length")]
174 fn test_trade_id_exceeds_max_length(#[case] input: &[u8]) {
175 TradeId::from_bytes(input).unwrap();
176 }
177
178 #[rstest]
179 fn test_trade_id_with_null_terminator_at_max_length() {
180 let input = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890\0" as &[u8];
181 let trade_id = TradeId::from_bytes(input).unwrap();
182 assert_eq!(trade_id.to_string(), "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"); }
184
185 #[rstest]
186 fn test_trade_id_as_cstr() {
187 let trade_id = TradeId::new("TRADE12345");
188 assert_eq!(trade_id.as_cstr().to_str().unwrap(), "TRADE12345");
189 }
190
191 #[rstest]
192 fn test_trade_id_as_str() {
193 let trade_id = TradeId::new("TRADE12345");
194 assert_eq!(trade_id.as_str(), "TRADE12345");
195 }
196
197 #[rstest]
198 fn test_trade_id_equality() {
199 let trade_id1 = TradeId::new("TRADE12345");
200 let trade_id2 = TradeId::new("TRADE12345");
201 assert_eq!(trade_id1, trade_id2);
202 }
203
204 #[rstest]
205 fn test_string_reprs(trade_id: TradeId) {
206 assert_eq!(trade_id.to_string(), "1234567890");
207 assert_eq!(format!("{trade_id}"), "1234567890");
208 assert_eq!(format!("{trade_id:?}"), "TradeId('1234567890')");
209 }
210
211 #[rstest]
212 fn test_trade_id_ordering() {
213 let trade_id1 = TradeId::new("TRADE12345");
214 let trade_id2 = TradeId::new("TRADE12346");
215 assert!(trade_id1 < trade_id2);
216 }
217
218 #[rstest]
219 fn test_trade_id_serialization() {
220 let trade_id = TradeId::new("TRADE12345");
221 let json = serde_json::to_string(&trade_id).unwrap();
222 assert_eq!(json, "\"TRADE12345\"");
223
224 let deserialized: TradeId = serde_json::from_str(&json).unwrap();
225 assert_eq!(trade_id, deserialized);
226 }
227}