1pub mod api;
5pub mod binary_update;
7mod dynamic_value;
8mod exchange_enums;
9mod feed_kind;
10pub mod hermes;
11pub mod jrpc;
13pub mod message;
15pub mod payload;
17mod price;
18pub mod publisher;
20mod rate;
21mod serde_price_as_i64;
22mod serde_str;
23mod symbol_state;
24pub mod time;
26
27use {
28 protobuf::MessageFull,
29 serde::{Deserialize, Serialize},
30};
31
32use {
33 derive_more::{From, Into},
34 strum::FromRepr,
35};
36
37pub use crate::{
38 dynamic_value::DynamicValue,
39 exchange_enums::{ExchangeAssetClass, ExchangeAssetSector, ExchangeAssetSubclass},
40 feed_kind::FeedKind,
41 price::{Price, PriceError},
42 rate::{Rate, RateError},
43 symbol_state::SymbolState,
44};
45
46#[derive(
47 Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, From, Into,
48)]
49pub struct AssetId(pub u32);
50
51#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
52#[derive(
53 Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, From, Into,
54)]
55#[cfg_attr(feature = "utoipa", schema(value_type = u32))]
56pub struct ExchangeId(pub u32);
57
58#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
59#[derive(
60 Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, From, Into,
61)]
62#[cfg_attr(feature = "utoipa", schema(value_type = u16))]
63pub struct PublisherId(pub u16);
64
65#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
66#[derive(
67 Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, From, Into,
68)]
69#[cfg_attr(feature = "utoipa", schema(value_type = u32))]
70pub struct PriceFeedId(pub u32);
71
72#[derive(
73 Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, From, Into,
74)]
75pub struct ChannelId(pub u8);
76
77#[derive(
80 Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, From, Into,
81)]
82pub struct PublisherDatapoint(pub i64);
83
84impl From<Price> for PublisherDatapoint {
85 fn from(price: Price) -> Self {
86 Self(price.mantissa_i64())
87 }
88}
89
90impl From<Rate> for PublisherDatapoint {
91 fn from(rate: Rate) -> Self {
92 Self(rate.mantissa())
93 }
94}
95
96impl ChannelId {
97 pub const REAL_TIME: ChannelId = ChannelId(1);
98 pub const FIXED_RATE_50: ChannelId = ChannelId(2);
99 pub const FIXED_RATE_200: ChannelId = ChannelId(3);
100 pub const FIXED_RATE_1000: ChannelId = ChannelId(4);
101}
102
103#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
104#[derive(
105 Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, FromRepr, strum::EnumIter,
106)]
107#[serde(rename_all = "camelCase")]
108#[repr(u8)]
109pub enum PriceFeedProperty {
110 Price,
111 BestBidPrice,
112 BestAskPrice,
113 PublisherCount,
114 Exponent,
115 Confidence,
116 FundingRate,
117 FundingTimestamp,
118 FundingRateInterval,
119 MarketSession,
120 EmaPrice,
121 EmaConfidence,
122 FeedUpdateTimestamp,
123 }
125
126#[derive(Debug, Clone, Serialize, Deserialize, strum::IntoStaticStr, strum::EnumIter)]
127#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
128#[serde(rename_all = "kebab-case")]
129#[strum(serialize_all = "kebab-case")]
130pub enum AssetClass {
131 Crypto,
132 Fx,
133 Equity,
134 Metal,
135 Rates,
136 Nav,
137 Commodity,
138 FundingRate,
139 Eco,
140 Kalshi,
141 InterestRate,
142}
143
144impl AssetClass {
145 pub fn as_str(&self) -> &'static str {
146 self.into()
147 }
148}
149
150#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
151#[derive(Debug, Clone, Copy, Serialize, Deserialize, strum::IntoStaticStr)]
152#[serde(rename_all = "kebab-case")]
153#[strum(serialize_all = "kebab-case")]
154pub enum InstrumentType {
155 Spot,
156 Rate,
157 Future,
158 Index,
159 Nav,
160}
161
162enum ExponentFactor {
164 Mul(i64),
166 Div(i64),
168}
169
170impl ExponentFactor {
171 fn get(exponent: i16) -> Option<Self> {
172 if exponent >= 0 {
173 let exponent: u32 = exponent.try_into().ok()?;
174 Some(ExponentFactor::Div(10_i64.checked_pow(exponent)?))
175 } else {
176 let minus_exponent: u32 = exponent.checked_neg()?.try_into().ok()?;
177 Some(ExponentFactor::Mul(10_i64.checked_pow(minus_exponent)?))
178 }
179 }
180}
181
182pub fn parse_proto_json<M: MessageFull>(
183 json: &str,
184) -> Result<M, protobuf_json_mapping::ParseError> {
185 protobuf_json_mapping::parse_from_str_with_options::<M>(
186 json,
187 &protobuf_json_mapping::ParseOptions {
188 ignore_unknown_fields: true,
189 ..Default::default()
190 },
191 )
192}
193
194#[test]
195fn magics_in_big_endian() {
196 use crate::{
197 binary_update::BINARY_UPDATE_FORMAT_MAGIC,
198 message::format_magics_le::{
199 EVM_FORMAT_MAGIC, JSON_FORMAT_MAGIC, LE_ECDSA_FORMAT_MAGIC, LE_UNSIGNED_FORMAT_MAGIC,
200 SOLANA_FORMAT_MAGIC,
201 },
202 payload::PAYLOAD_FORMAT_MAGIC,
203 };
204
205 assert_eq!(u32::swap_bytes(BINARY_UPDATE_FORMAT_MAGIC), 1937213467);
209 assert_eq!(u32::swap_bytes(PAYLOAD_FORMAT_MAGIC), 1976813459);
210
211 assert_eq!(u32::swap_bytes(SOLANA_FORMAT_MAGIC), 3103857282);
212 assert_eq!(u32::swap_bytes(JSON_FORMAT_MAGIC), 2584795844);
213 assert_eq!(u32::swap_bytes(EVM_FORMAT_MAGIC), 706910618);
214 assert_eq!(u32::swap_bytes(LE_ECDSA_FORMAT_MAGIC), 3837609805);
215 assert_eq!(u32::swap_bytes(LE_UNSIGNED_FORMAT_MAGIC), 206398297);
216
217 for magic in [
218 BINARY_UPDATE_FORMAT_MAGIC,
219 PAYLOAD_FORMAT_MAGIC,
220 SOLANA_FORMAT_MAGIC,
221 JSON_FORMAT_MAGIC,
222 EVM_FORMAT_MAGIC,
223 LE_ECDSA_FORMAT_MAGIC,
224 LE_UNSIGNED_FORMAT_MAGIC,
225 ] {
226 assert_ne!(u32::swap_bytes(magic), magic);
228 }
229}
230
231#[test]
232fn parse_proto_json_works() {
233 use protobuf::descriptor::descriptor_proto::ExtensionRange;
234
235 let ts1 = parse_proto_json::<ExtensionRange>(r#"{"start":1,"end":2}"#).unwrap();
236 assert_eq!(ts1.start, Some(1));
237 assert_eq!(ts1.end, Some(2));
238
239 let ts2 = parse_proto_json::<ExtensionRange>(r#"{"start":3,"extra":5}"#).unwrap();
240 assert_eq!(ts2.start, Some(3));
241 assert_eq!(ts2.end, None);
242}