nautilus_model/identifiers/
client_order_id.rs1use std::{
19 fmt::{Debug, Display},
20 hash::Hash,
21};
22
23use nautilus_core::correctness::{
24 CorrectnessResult, CorrectnessResultExt, FAILED, check_valid_string_ascii,
25};
26use ustr::Ustr;
27
28const EXTERNAL_CLIENT_ORDER_ID: &str = "EXTERNAL";
29
30#[repr(C)]
32#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
33#[cfg_attr(
34 feature = "python",
35 pyo3::pyclass(module = "nautilus_trader.model", from_py_object)
36)]
37#[cfg_attr(
38 feature = "python",
39 pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.model")
40)]
41pub struct ClientOrderId(Ustr);
42
43impl ClientOrderId {
44 pub fn new_checked<T: AsRef<str>>(value: T) -> CorrectnessResult<Self> {
54 let value = value.as_ref();
55 check_valid_string_ascii(value, stringify!(value))?;
56 Ok(Self(Ustr::from(value)))
57 }
58
59 pub fn new<T: AsRef<str>>(value: T) -> Self {
65 Self::new_checked(value).expect_display(FAILED)
66 }
67
68 #[cfg_attr(not(feature = "python"), allow(dead_code))]
70 pub(crate) fn set_inner(&mut self, value: &str) {
71 self.0 = Ustr::from(value);
72 }
73
74 #[must_use]
76 pub fn inner(&self) -> Ustr {
77 self.0
78 }
79
80 #[must_use]
82 pub fn as_str(&self) -> &str {
83 self.0.as_str()
84 }
85
86 #[must_use]
88 pub fn external() -> Self {
89 Self::new(EXTERNAL_CLIENT_ORDER_ID)
90 }
91
92 #[must_use]
94 pub fn is_external(&self) -> bool {
95 self.0 == EXTERNAL_CLIENT_ORDER_ID
96 }
97}
98
99impl Debug for ClientOrderId {
100 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101 write!(f, "\"{}\"", self.0)
102 }
103}
104
105impl Display for ClientOrderId {
106 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107 write!(f, "{}", self.0)
108 }
109}
110
111#[must_use]
112pub fn optional_ustr_to_vec_client_order_ids(value: Option<Ustr>) -> Option<Vec<ClientOrderId>> {
113 value.map(|ids| ids.as_str().split(',').map(ClientOrderId::new).collect())
114}
115
116#[must_use]
117pub fn optional_vec_client_order_ids_to_ustr(value: Option<Vec<ClientOrderId>>) -> Option<Ustr> {
118 value.map(|ids| {
119 let value = ids
120 .iter()
121 .map(ClientOrderId::as_str)
122 .collect::<Vec<_>>()
123 .join(",");
124 Ustr::from(&value)
125 })
126}
127
128#[cfg(test)]
129mod tests {
130 use rstest::rstest;
131 use ustr::Ustr;
132
133 use super::ClientOrderId;
134 use crate::identifiers::{
135 client_order_id::{
136 optional_ustr_to_vec_client_order_ids, optional_vec_client_order_ids_to_ustr,
137 },
138 stubs::*,
139 };
140
141 #[rstest]
142 fn test_string_reprs(client_order_id: ClientOrderId) {
143 assert_eq!(client_order_id.as_str(), "O-19700101-000000-001-001-1");
144 assert_eq!(format!("{client_order_id}"), "O-19700101-000000-001-001-1");
145 }
146
147 #[rstest]
148 fn test_external() {
149 let external = ClientOrderId::external();
150 let local = ClientOrderId::new("LOCAL-1");
151
152 assert_eq!(external.as_str(), "EXTERNAL");
153 assert!(external.is_external());
154 assert!(!local.is_external());
155 }
156
157 #[rstest]
158 #[should_panic(expected = "Condition failed: invalid string for 'value', was empty")]
159 fn test_new_with_empty_string_panics_with_display_format() {
160 let _ = ClientOrderId::new("");
161 }
162
163 #[rstest]
164 fn test_optional_ustr_to_vec_client_order_ids() {
165 assert_eq!(optional_ustr_to_vec_client_order_ids(None), None);
166 assert_eq!(
167 optional_ustr_to_vec_client_order_ids(Some(Ustr::from("id1,id2,id3"))),
168 Some(vec![
169 ClientOrderId::new("id1"),
170 ClientOrderId::new("id2"),
171 ClientOrderId::new("id3"),
172 ])
173 );
174 }
175
176 #[rstest]
177 fn test_optional_vec_client_order_ids_to_ustr() {
178 assert_eq!(optional_vec_client_order_ids_to_ustr(None), None);
179 assert_eq!(
180 optional_vec_client_order_ids_to_ustr(Some(vec![
181 ClientOrderId::new("id1"),
182 ClientOrderId::new("id2"),
183 ClientOrderId::new("id3"),
184 ])),
185 Some(Ustr::from("id1,id2,id3"))
186 );
187 }
188}