nautilus_infrastructure/sql/models/
enums.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.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
27pub struct CurrencyTypeModel(pub CurrencyType);
28pub struct PriceTypeModel(pub PriceType);
29pub struct BarAggregationModel(pub BarAggregation);
30pub struct AssetClassModel(pub AssetClass);
31pub struct TrailingOffsetTypeModel(pub TrailingOffsetType);
32pub struct AggressorSideModel(pub AggressorSide);
33pub struct AggregationSourceModel(pub AggregationSource);
34
35impl sqlx::Encode<'_, sqlx::Postgres> for CurrencyTypeModel {
36    fn encode_by_ref(
37        &self,
38        buf: &mut <Postgres as Database>::ArgumentBuffer<'_>,
39    ) -> Result<IsNull, BoxDynError> {
40        let currency_type_str = match self.0 {
41            CurrencyType::Crypto => "CRYPTO",
42            CurrencyType::Fiat => "FIAT",
43            CurrencyType::CommodityBacked => "COMMODITY_BACKED",
44        };
45        <&str as sqlx::Encode<sqlx::Postgres>>::encode(currency_type_str, buf)
46    }
47}
48
49impl<'r> sqlx::Decode<'r, sqlx::Postgres> for CurrencyTypeModel {
50    fn decode(value: <Postgres as Database>::ValueRef<'r>) -> Result<Self, BoxDynError> {
51        let currency_type_str: &str = <&str as Decode<sqlx::Postgres>>::decode(value)?;
52        let currency_type = CurrencyType::from_str(currency_type_str).map_err(|_| {
53            sqlx::Error::Decode(format!("Invalid currency type: {}", currency_type_str).into())
54        })?;
55        Ok(CurrencyTypeModel(currency_type))
56    }
57}
58
59impl sqlx::Type<sqlx::Postgres> for CurrencyTypeModel {
60    fn type_info() -> sqlx::postgres::PgTypeInfo {
61        PgTypeInfo::with_name("currency_type")
62    }
63
64    fn compatible(ty: &sqlx::postgres::PgTypeInfo) -> bool {
65        *ty == Self::type_info() || <&str as Type<sqlx::Postgres>>::compatible(ty)
66    }
67}
68
69impl sqlx::Encode<'_, sqlx::Postgres> for AssetClassModel {
70    fn encode_by_ref(
71        &self,
72        buf: &mut <Postgres as Database>::ArgumentBuffer<'_>,
73    ) -> Result<IsNull, BoxDynError> {
74        let asset_type_str = match self.0 {
75            AssetClass::FX => "FX",
76            AssetClass::Equity => "EQUITY",
77            AssetClass::Commodity => "COMMODITY",
78            AssetClass::Debt => "DEBT",
79            AssetClass::Index => "INDEX",
80            AssetClass::Cryptocurrency => "CRYPTOCURRENCY",
81            AssetClass::Alternative => "ALTERNATIVE",
82        };
83        <&str as sqlx::Encode<sqlx::Postgres>>::encode(asset_type_str, buf)
84    }
85}
86
87impl<'r> sqlx::Decode<'r, sqlx::Postgres> for AssetClassModel {
88    fn decode(value: <Postgres as Database>::ValueRef<'r>) -> Result<Self, BoxDynError> {
89        let asset_class_str: &str = <&str as Decode<sqlx::Postgres>>::decode(value)?;
90        let asset_class = AssetClass::from_str(asset_class_str).map_err(|_| {
91            sqlx::Error::Decode(format!("Invalid asset class: {}", asset_class_str).into())
92        })?;
93        Ok(AssetClassModel(asset_class))
94    }
95}
96
97impl sqlx::Type<sqlx::Postgres> for AssetClassModel {
98    fn type_info() -> sqlx::postgres::PgTypeInfo {
99        PgTypeInfo::with_name("asset_class")
100    }
101
102    fn compatible(ty: &sqlx::postgres::PgTypeInfo) -> bool {
103        *ty == Self::type_info() || <&str as Type<sqlx::Postgres>>::compatible(ty)
104    }
105}
106
107impl sqlx::Encode<'_, sqlx::Postgres> for TrailingOffsetTypeModel {
108    fn encode_by_ref(
109        &self,
110        buf: &mut <Postgres as Database>::ArgumentBuffer<'_>,
111    ) -> Result<IsNull, BoxDynError> {
112        let trailing_offset_type_str = match self.0 {
113            TrailingOffsetType::NoTrailingOffset => "NO_TRAILING_OFFSET",
114            TrailingOffsetType::Price => "PRICE",
115            TrailingOffsetType::BasisPoints => "BASIS_POINTS",
116            TrailingOffsetType::Ticks => "TICKS",
117            TrailingOffsetType::PriceTier => "PRICE_TIER",
118        };
119        <&str as sqlx::Encode<sqlx::Postgres>>::encode(trailing_offset_type_str, buf)
120    }
121}
122
123impl<'r> sqlx::Decode<'r, sqlx::Postgres> for TrailingOffsetTypeModel {
124    fn decode(value: <Postgres as Database>::ValueRef<'r>) -> Result<Self, BoxDynError> {
125        let trailing_offset_type_str: &str = <&str as Decode<sqlx::Postgres>>::decode(value)?;
126        let trailing_offset_type =
127            TrailingOffsetType::from_str(trailing_offset_type_str).map_err(|_| {
128                sqlx::Error::Decode(
129                    format!("Invalid trailing offset type: {}", trailing_offset_type_str).into(),
130                )
131            })?;
132        Ok(TrailingOffsetTypeModel(trailing_offset_type))
133    }
134}
135
136impl sqlx::Type<sqlx::Postgres> for TrailingOffsetTypeModel {
137    fn type_info() -> sqlx::postgres::PgTypeInfo {
138        PgTypeInfo::with_name("trailing_offset_type")
139    }
140
141    fn compatible(ty: &sqlx::postgres::PgTypeInfo) -> bool {
142        *ty == Self::type_info() || <&str as Type<sqlx::Postgres>>::compatible(ty)
143    }
144}
145
146impl sqlx::Encode<'_, sqlx::Postgres> for AggressorSideModel {
147    fn encode_by_ref(
148        &self,
149        buf: &mut <Postgres as Database>::ArgumentBuffer<'_>,
150    ) -> Result<IsNull, BoxDynError> {
151        let aggressor_side_str = match self.0 {
152            AggressorSide::NoAggressor => "NO_AGGRESSOR",
153            AggressorSide::Buyer => "BUYER",
154            AggressorSide::Seller => "SELLER",
155        };
156        <&str as sqlx::Encode<sqlx::Postgres>>::encode(aggressor_side_str, buf)
157    }
158}
159
160impl<'r> sqlx::Decode<'r, sqlx::Postgres> for AggressorSideModel {
161    fn decode(value: <Postgres as Database>::ValueRef<'r>) -> Result<Self, BoxDynError> {
162        let aggressor_side_str: &str = <&str as Decode<sqlx::Postgres>>::decode(value)?;
163        let aggressor_side = AggressorSide::from_str(aggressor_side_str).map_err(|_| {
164            sqlx::Error::Decode(format!("Invalid aggressor side: {}", aggressor_side_str).into())
165        })?;
166        Ok(AggressorSideModel(aggressor_side))
167    }
168}
169
170impl sqlx::Type<sqlx::Postgres> for AggressorSideModel {
171    fn type_info() -> sqlx::postgres::PgTypeInfo {
172        PgTypeInfo::with_name("aggressor_side")
173    }
174
175    fn compatible(ty: &sqlx::postgres::PgTypeInfo) -> bool {
176        *ty == Self::type_info() || <&str as Type<sqlx::Postgres>>::compatible(ty)
177    }
178}
179
180impl sqlx::Encode<'_, sqlx::Postgres> for AggregationSourceModel {
181    fn encode_by_ref(
182        &self,
183        buf: &mut <Postgres as Database>::ArgumentBuffer<'_>,
184    ) -> Result<IsNull, BoxDynError> {
185        let aggregation_source_str = match self.0 {
186            AggregationSource::Internal => "INTERNAL",
187            AggregationSource::External => "EXTERNAL",
188        };
189        <&str as sqlx::Encode<sqlx::Postgres>>::encode(aggregation_source_str, buf)
190    }
191}
192
193impl<'r> sqlx::Decode<'r, sqlx::Postgres> for AggregationSourceModel {
194    fn decode(value: <Postgres as Database>::ValueRef<'r>) -> Result<Self, BoxDynError> {
195        let aggregation_source_str: &str = <&str as Decode<sqlx::Postgres>>::decode(value)?;
196        let aggregation_source =
197            AggregationSource::from_str(aggregation_source_str).map_err(|_| {
198                sqlx::Error::Decode(
199                    format!("Invalid aggregation source: {}", aggregation_source_str).into(),
200                )
201            })?;
202        Ok(AggregationSourceModel(aggregation_source))
203    }
204}
205
206impl sqlx::Type<sqlx::Postgres> for AggregationSourceModel {
207    fn type_info() -> sqlx::postgres::PgTypeInfo {
208        PgTypeInfo::with_name("aggregation_source")
209    }
210
211    fn compatible(ty: &sqlx::postgres::PgTypeInfo) -> bool {
212        *ty == Self::type_info() || <&str as Type<sqlx::Postgres>>::compatible(ty)
213    }
214}
215
216impl sqlx::Encode<'_, sqlx::Postgres> for BarAggregationModel {
217    fn encode_by_ref(
218        &self,
219        buf: &mut <Postgres as Database>::ArgumentBuffer<'_>,
220    ) -> Result<IsNull, BoxDynError> {
221        let bar_aggregation_str = match self.0 {
222            BarAggregation::Tick => "TICK",
223            BarAggregation::TickImbalance => "TICK_IMBALANCE",
224            BarAggregation::TickRuns => "TICK_RUNS",
225            BarAggregation::Volume => "VOLUME",
226            BarAggregation::VolumeImbalance => "VOLUME_IMBALANCE",
227            BarAggregation::VolumeRuns => "VOLUME_RUNS",
228            BarAggregation::Value => "VALUE",
229            BarAggregation::ValueImbalance => "VALUE_IMBALANCE",
230            BarAggregation::ValueRuns => "VALUE_RUNS",
231            BarAggregation::Millisecond => "TIME",
232            BarAggregation::Second => "SECOND",
233            BarAggregation::Minute => "MINUTE",
234            BarAggregation::Hour => "HOUR",
235            BarAggregation::Day => "DAY",
236            BarAggregation::Week => "WEEK",
237            BarAggregation::Month => "MONTH",
238        };
239        <&str as sqlx::Encode<sqlx::Postgres>>::encode(bar_aggregation_str, buf)
240    }
241}
242
243impl<'r> sqlx::Decode<'r, sqlx::Postgres> for BarAggregationModel {
244    fn decode(value: <Postgres as Database>::ValueRef<'r>) -> Result<Self, BoxDynError> {
245        let bar_aggregation_str: &str = <&str as Decode<sqlx::Postgres>>::decode(value)?;
246        let bar_aggregation = BarAggregation::from_str(bar_aggregation_str).map_err(|_| {
247            sqlx::Error::Decode(format!("Invalid bar aggregation: {}", bar_aggregation_str).into())
248        })?;
249        Ok(BarAggregationModel(bar_aggregation))
250    }
251}
252
253impl sqlx::Type<sqlx::Postgres> for BarAggregationModel {
254    fn type_info() -> sqlx::postgres::PgTypeInfo {
255        PgTypeInfo::with_name("bar_aggregation")
256    }
257
258    fn compatible(ty: &sqlx::postgres::PgTypeInfo) -> bool {
259        *ty == Self::type_info() || <&str as Type<sqlx::Postgres>>::compatible(ty)
260    }
261}
262
263impl sqlx::Encode<'_, sqlx::Postgres> for PriceTypeModel {
264    fn encode_by_ref(
265        &self,
266        buf: &mut <Postgres as Database>::ArgumentBuffer<'_>,
267    ) -> Result<IsNull, BoxDynError> {
268        let price_type_str = match self.0 {
269            PriceType::Bid => "BID",
270            PriceType::Ask => "ASK",
271            PriceType::Mid => "MID",
272            PriceType::Last => "LAST",
273            PriceType::Mark => "MARK",
274        };
275        <&str as sqlx::Encode<sqlx::Postgres>>::encode(price_type_str, buf)
276    }
277}
278
279impl<'r> sqlx::Decode<'r, sqlx::Postgres> for PriceTypeModel {
280    fn decode(value: <Postgres as Database>::ValueRef<'r>) -> Result<Self, BoxDynError> {
281        let price_type_str: &str = <&str as Decode<sqlx::Postgres>>::decode(value)?;
282        let price_type = PriceType::from_str(price_type_str).map_err(|_| {
283            sqlx::Error::Decode(format!("Invalid price type: {}", price_type_str).into())
284        })?;
285        Ok(PriceTypeModel(price_type))
286    }
287}
288
289impl sqlx::Type<sqlx::Postgres> for PriceTypeModel {
290    fn type_info() -> sqlx::postgres::PgTypeInfo {
291        PgTypeInfo::with_name("price_type")
292    }
293
294    fn compatible(ty: &sqlx::postgres::PgTypeInfo) -> bool {
295        *ty == Self::type_info() || <&str as Type<sqlx::Postgres>>::compatible(ty)
296    }
297}