1use std::{fmt, str::FromStr};
7
8use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as _};
9
10use crate::Error;
11
12macro_rules! numeric_id {
13 ($name:ident, $kind:literal, $repr:ty) => {
14 #[doc = concat!("Validated ProjectX ", $kind, ".")]
15 #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
16 #[repr(transparent)]
17 pub struct $name($repr);
18
19 impl $name {
20 #[doc = concat!("Creates a ProjectX ", $kind, ".")]
21 pub fn new(value: $repr) -> Result<Self, Error> {
26 if value <= 0 {
27 return Err(Error::InvalidIdentifier {
28 kind: $kind,
29 reason: "value must be positive",
30 });
31 }
32 Ok(Self(value))
33 }
34
35 #[doc = concat!("Returns the raw ProjectX ", $kind, ".")]
36 #[must_use]
37 pub const fn get(self) -> $repr {
38 self.0
39 }
40 }
41
42 impl TryFrom<$repr> for $name {
43 type Error = Error;
44
45 fn try_from(value: $repr) -> Result<Self, Self::Error> {
46 Self::new(value)
47 }
48 }
49
50 impl fmt::Display for $name {
51 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52 self.0.fmt(f)
53 }
54 }
55
56 impl Serialize for $name {
57 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
58 where
59 S: Serializer,
60 {
61 self.0.serialize(serializer)
62 }
63 }
64
65 impl<'de> Deserialize<'de> for $name {
66 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
67 where
68 D: Deserializer<'de>,
69 {
70 let raw = <$repr>::deserialize(deserializer)?;
71 Self::new(raw).map_err(D::Error::custom)
72 }
73 }
74 };
75}
76
77macro_rules! string_id {
78 ($name:ident, $kind:literal) => {
79 #[doc = concat!("Validated ProjectX ", $kind, ".")]
80 #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
81 #[repr(transparent)]
82 pub struct $name(String);
83
84 impl $name {
85 #[doc = concat!("Creates a ProjectX ", $kind, ".")]
86 pub fn new(value: impl Into<String>) -> Result<Self, Error> {
92 let value = value.into();
93 if value.is_empty()
94 || value
95 .chars()
96 .any(|character| character.is_whitespace() || character.is_control())
97 {
98 return Err(Error::InvalidIdentifier {
99 kind: $kind,
100 reason: "value must be non-empty and contain no whitespace or control characters",
101 });
102 }
103 Ok(Self(value))
104 }
105
106 #[doc = concat!("Borrows the raw ProjectX ", $kind, ".")]
107 #[must_use]
108 pub fn as_str(&self) -> &str {
109 &self.0
110 }
111 }
112
113 impl AsRef<str> for $name {
114 fn as_ref(&self) -> &str {
115 self.as_str()
116 }
117 }
118
119 impl FromStr for $name {
120 type Err = Error;
121
122 fn from_str(value: &str) -> Result<Self, Self::Err> {
123 Self::new(value)
124 }
125 }
126
127 impl TryFrom<String> for $name {
128 type Error = Error;
129
130 fn try_from(value: String) -> Result<Self, Self::Error> {
131 Self::new(value)
132 }
133 }
134
135 impl fmt::Display for $name {
136 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137 self.0.fmt(f)
138 }
139 }
140
141 impl Serialize for $name {
142 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
143 where
144 S: Serializer,
145 {
146 serializer.serialize_str(&self.0)
147 }
148 }
149
150 impl<'de> Deserialize<'de> for $name {
151 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
152 where
153 D: Deserializer<'de>,
154 {
155 let raw = String::deserialize(deserializer)?;
156 Self::new(raw).map_err(D::Error::custom)
157 }
158 }
159 };
160}
161
162numeric_id!(AccountId, "account identifier", i32);
163numeric_id!(OrderId, "order identifier", i64);
164numeric_id!(PositionId, "position identifier", i32);
165numeric_id!(TradeId, "trade identifier", i64);
166string_id!(ContractId, "contract identifier");
167string_id!(SymbolId, "symbol identifier");