nautilus_infrastructure/sql/models/
enums.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Posei Systems Pty Ltd. All rights reserved.
3//  https://poseitrader.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16use std::str::FromStr;
17
18use nautilus_model::enums::{
19    AggregationSource, AggressorSide, AssetClass, BarAggregation, CurrencyType, PriceType,
20    TrailingOffsetType,
21};
22use sqlx::{
23    Database, Decode, Postgres, encode::IsNull, error::BoxDynError, postgres::PgTypeInfo,
24    types::Type,
25};
26
27#[derive(Debug)]
28pub struct CurrencyTypeModel(pub CurrencyType);
29
30#[derive(Debug)]
31pub struct PriceTypeModel(pub PriceType);
32
33#[derive(Debug)]
34pub struct BarAggregationModel(pub BarAggregation);
35
36#[derive(Debug)]
37pub struct AssetClassModel(pub AssetClass);
38
39#[derive(Debug)]
40pub struct TrailingOffsetTypeModel(pub TrailingOffsetType);
41
42#[derive(Debug)]
43pub struct AggressorSideModel(pub AggressorSide);
44
45#[derive(Debug)]
46pub struct AggregationSourceModel(pub AggregationSource);
47
48impl sqlx::Encode<'_, sqlx::Postgres> for CurrencyTypeModel {
49    fn encode_by_ref(
50        &self,
51        buf: &mut <Postgres as Database>::ArgumentBuffer<'_>,
52    ) -> Result<IsNull, BoxDynError> {
53        let currency_type_str = match self.0 {
54            CurrencyType::Crypto => "CRYPTO",
55            CurrencyType::Fiat => "FIAT",
56            CurrencyType::CommodityBacked => "COMMODITY_BACKED",
57        };
58        <&str as sqlx::Encode<sqlx::Postgres>>::encode(currency_type_str, buf)
59    }
60}
61
62impl<'r> sqlx::Decode<'r, sqlx::Postgres> for CurrencyTypeModel {
63    fn decode(value: <Postgres as Database>::ValueRef<'r>) -> Result<Self, BoxDynError> {
64        let currency_type_str: &str = <&str as Decode<sqlx::Postgres>>::decode(value)?;
65        let currency_type = CurrencyType::from_str(currency_type_str).map_err(|_| {
66            sqlx::Error::Decode(format!("Invalid currency type: {currency_type_str}").into())
67        })?;
68        Ok(Self(currency_type))
69    }
70}
71
72impl sqlx::Type<sqlx::Postgres> for CurrencyTypeModel {
73    fn type_info() -> sqlx::postgres::PgTypeInfo {
74        PgTypeInfo::with_name("currency_type")
75    }
76
77    fn compatible(ty: &sqlx::postgres::PgTypeInfo) -> bool {
78        *ty == Self::type_info() || <&str as Type<sqlx::Postgres>>::compatible(ty)
79    }
80}
81
82impl sqlx::Encode<'_, sqlx::Postgres> for AssetClassModel {
83    fn encode_by_ref(
84        &self,
85        buf: &mut <Postgres as Database>::ArgumentBuffer<'_>,
86    ) -> Result<IsNull, BoxDynError> {
87        let asset_type_str = match self.0 {
88            AssetClass::FX => "FX",
89            AssetClass::Equity => "EQUITY",
90            AssetClass::Commodity => "COMMODITY",
91            AssetClass::Debt => "DEBT",
92            AssetClass::Index => "INDEX",
93            AssetClass::Cryptocurrency => "CRYPTOCURRENCY",
94            AssetClass::Alternative => "ALTERNATIVE",
95        };
96        <&str as sqlx::Encode<sqlx::Postgres>>::encode(asset_type_str, buf)
97    }
98}
99
100impl<'r> sqlx::Decode<'r, sqlx::Postgres> for AssetClassModel {
101    fn decode(value: <Postgres as Database>::ValueRef<'r>) -> Result<Self, BoxDynError> {
102        let asset_class_str: &str = <&str as Decode<sqlx::Postgres>>::decode(value)?;
103        let asset_class = AssetClass::from_str(asset_class_str).map_err(|_| {
104            sqlx::Error::Decode(format!("Invalid asset class: {asset_class_str}").into())
105        })?;
106        Ok(Self(asset_class))
107    }
108}
109
110impl sqlx::Type<sqlx::Postgres> for AssetClassModel {
111    fn type_info() -> sqlx::postgres::PgTypeInfo {
112        PgTypeInfo::with_name("asset_class")
113    }
114
115    fn compatible(ty: &sqlx::postgres::PgTypeInfo) -> bool {
116        *ty == Self::type_info() || <&str as Type<sqlx::Postgres>>::compatible(ty)
117    }
118}
119
120impl sqlx::Encode<'_, sqlx::Postgres> for TrailingOffsetTypeModel {
121    fn encode_by_ref(
122        &self,
123        buf: &mut <Postgres as Database>::ArgumentBuffer<'_>,
124    ) -> Result<IsNull, BoxDynError> {
125        let trailing_offset_type_str = match self.0 {
126            TrailingOffsetType::NoTrailingOffset => "NO_TRAILING_OFFSET",
127            TrailingOffsetType::Price => "PRICE",
128            TrailingOffsetType::BasisPoints => "BASIS_POINTS",
129            TrailingOffsetType::Ticks => "TICKS",
130            TrailingOffsetType::PriceTier => "PRICE_TIER",
131        };
132        <&str as sqlx::Encode<sqlx::Postgres>>::encode(trailing_offset_type_str, buf)
133    }
134}
135
136impl<'r> sqlx::Decode<'r, sqlx::Postgres> for TrailingOffsetTypeModel {
137    fn decode(value: <Postgres as Database>::ValueRef<'r>) -> Result<Self, BoxDynError> {
138        let trailing_offset_type_str: &str = <&str as Decode<sqlx::Postgres>>::decode(value)?;
139        let trailing_offset_type =
140            TrailingOffsetType::from_str(trailing_offset_type_str).map_err(|_| {
141                sqlx::Error::Decode(
142                    format!("Invalid trailing offset type: {trailing_offset_type_str}").into(),
143                )
144            })?;
145        Ok(Self(trailing_offset_type))
146    }
147}
148
149impl sqlx::Type<sqlx::Postgres> for TrailingOffsetTypeModel {
150    fn type_info() -> sqlx::postgres::PgTypeInfo {
151        PgTypeInfo::with_name("trailing_offset_type")
152    }
153
154    fn compatible(ty: &sqlx::postgres::PgTypeInfo) -> bool {
155        *ty == Self::type_info() || <&str as Type<sqlx::Postgres>>::compatible(ty)
156    }
157}
158
159impl sqlx::Encode<'_, sqlx::Postgres> for AggressorSideModel {
160    fn encode_by_ref(
161        &self,
162        buf: &mut <Postgres as Database>::ArgumentBuffer<'_>,
163    ) -> Result<IsNull, BoxDynError> {
164        let aggressor_side_str = match self.0 {
165            AggressorSide::NoAggressor => "NO_AGGRESSOR",
166            AggressorSide::Buyer => "BUYER",
167            AggressorSide::Seller => "SELLER",
168        };
169        <&str as sqlx::Encode<sqlx::Postgres>>::encode(aggressor_side_str, buf)
170    }
171}
172
173impl<'r> sqlx::Decode<'r, sqlx::Postgres> for AggressorSideModel {
174    fn decode(value: <Postgres as Database>::ValueRef<'r>) -> Result<Self, BoxDynError> {
175        let aggressor_side_str: &str = <&str as Decode<sqlx::Postgres>>::decode(value)?;
176        let aggressor_side = AggressorSide::from_str(aggressor_side_str).map_err(|_| {
177            sqlx::Error::Decode(format!("Invalid aggressor side: {aggressor_side_str}").into())
178        })?;
179        Ok(Self(aggressor_side))
180    }
181}
182
183impl sqlx::Type<sqlx::Postgres> for AggressorSideModel {
184    fn type_info() -> sqlx::postgres::PgTypeInfo {
185        PgTypeInfo::with_name("aggressor_side")
186    }
187
188    fn compatible(ty: &sqlx::postgres::PgTypeInfo) -> bool {
189        *ty == Self::type_info() || <&str as Type<sqlx::Postgres>>::compatible(ty)
190    }
191}
192
193impl sqlx::Encode<'_, sqlx::Postgres> for AggregationSourceModel {
194    fn encode_by_ref(
195        &self,
196        buf: &mut <Postgres as Database>::ArgumentBuffer<'_>,
197    ) -> Result<IsNull, BoxDynError> {
198        let aggregation_source_str = match self.0 {
199            AggregationSource::Internal => "INTERNAL",
200            AggregationSource::External => "EXTERNAL",
201        };
202        <&str as sqlx::Encode<sqlx::Postgres>>::encode(aggregation_source_str, buf)
203    }
204}
205
206impl<'r> sqlx::Decode<'r, sqlx::Postgres> for AggregationSourceModel {
207    fn decode(value: <Postgres as Database>::ValueRef<'r>) -> Result<Self, BoxDynError> {
208        let aggregation_source_str: &str = <&str as Decode<sqlx::Postgres>>::decode(value)?;
209        let aggregation_source =
210            AggregationSource::from_str(aggregation_source_str).map_err(|_| {
211                sqlx::Error::Decode(
212                    format!("Invalid aggregation source: {aggregation_source_str}").into(),
213                )
214            })?;
215        Ok(Self(aggregation_source))
216    }
217}
218
219impl sqlx::Type<sqlx::Postgres> for AggregationSourceModel {
220    fn type_info() -> sqlx::postgres::PgTypeInfo {
221        PgTypeInfo::with_name("aggregation_source")
222    }
223
224    fn compatible(ty: &sqlx::postgres::PgTypeInfo) -> bool {
225        *ty == Self::type_info() || <&str as Type<sqlx::Postgres>>::compatible(ty)
226    }
227}
228
229impl sqlx::Encode<'_, sqlx::Postgres> for BarAggregationModel {
230    fn encode_by_ref(
231        &self,
232        buf: &mut <Postgres as Database>::ArgumentBuffer<'_>,
233    ) -> Result<IsNull, BoxDynError> {
234        let bar_aggregation_str = match self.0 {
235            BarAggregation::Tick => "TICK",
236            BarAggregation::TickImbalance => "TICK_IMBALANCE",
237            BarAggregation::TickRuns => "TICK_RUNS",
238            BarAggregation::Volume => "VOLUME",
239            BarAggregation::VolumeImbalance => "VOLUME_IMBALANCE",
240            BarAggregation::VolumeRuns => "VOLUME_RUNS",
241            BarAggregation::Value => "VALUE",
242            BarAggregation::ValueImbalance => "VALUE_IMBALANCE",
243            BarAggregation::ValueRuns => "VALUE_RUNS",
244            BarAggregation::Millisecond => "TIME",
245            BarAggregation::Second => "SECOND",
246            BarAggregation::Minute => "MINUTE",
247            BarAggregation::Hour => "HOUR",
248            BarAggregation::Day => "DAY",
249            BarAggregation::Week => "WEEK",
250            BarAggregation::Month => "MONTH",
251        };
252        <&str as sqlx::Encode<sqlx::Postgres>>::encode(bar_aggregation_str, buf)
253    }
254}
255
256impl<'r> sqlx::Decode<'r, sqlx::Postgres> for BarAggregationModel {
257    fn decode(value: <Postgres as Database>::ValueRef<'r>) -> Result<Self, BoxDynError> {
258        let bar_aggregation_str: &str = <&str as Decode<sqlx::Postgres>>::decode(value)?;
259        let bar_aggregation = BarAggregation::from_str(bar_aggregation_str).map_err(|_| {
260            sqlx::Error::Decode(format!("Invalid bar aggregation: {bar_aggregation_str}").into())
261        })?;
262        Ok(Self(bar_aggregation))
263    }
264}
265
266impl sqlx::Type<sqlx::Postgres> for BarAggregationModel {
267    fn type_info() -> sqlx::postgres::PgTypeInfo {
268        PgTypeInfo::with_name("bar_aggregation")
269    }
270
271    fn compatible(ty: &sqlx::postgres::PgTypeInfo) -> bool {
272        *ty == Self::type_info() || <&str as Type<sqlx::Postgres>>::compatible(ty)
273    }
274}
275
276impl sqlx::Encode<'_, sqlx::Postgres> for PriceTypeModel {
277    fn encode_by_ref(
278        &self,
279        buf: &mut <Postgres as Database>::ArgumentBuffer<'_>,
280    ) -> Result<IsNull, BoxDynError> {
281        let price_type_str = match self.0 {
282            PriceType::Bid => "BID",
283            PriceType::Ask => "ASK",
284            PriceType::Mid => "MID",
285            PriceType::Last => "LAST",
286            PriceType::Mark => "MARK",
287        };
288        <&str as sqlx::Encode<sqlx::Postgres>>::encode(price_type_str, buf)
289    }
290}
291
292impl<'r> sqlx::Decode<'r, sqlx::Postgres> for PriceTypeModel {
293    fn decode(value: <Postgres as Database>::ValueRef<'r>) -> Result<Self, BoxDynError> {
294        let price_type_str: &str = <&str as Decode<sqlx::Postgres>>::decode(value)?;
295        let price_type = PriceType::from_str(price_type_str).map_err(|_| {
296            sqlx::Error::Decode(format!("Invalid price type: {price_type_str}").into())
297        })?;
298        Ok(Self(price_type))
299    }
300}
301
302impl sqlx::Type<sqlx::Postgres> for PriceTypeModel {
303    fn type_info() -> sqlx::postgres::PgTypeInfo {
304        PgTypeInfo::with_name("price_type")
305    }
306
307    fn compatible(ty: &sqlx::postgres::PgTypeInfo) -> bool {
308        *ty == Self::type_info() || <&str as Type<sqlx::Postgres>>::compatible(ty)
309    }
310}