api.rs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652
  1. use std::{
  2. cmp::Ordering,
  3. fmt::Display,
  4. ops::{Deref, DerefMut},
  5. };
  6. use derive_more::derive::From;
  7. use itertools::Itertools as _;
  8. use serde::{de::Error, Deserialize, Serialize};
  9. use utoipa::ToSchema;
  10. use crate::{
  11. payload::AggregatedPriceFeedData,
  12. time::{DurationUs, FixedRate, TimestampUs},
  13. ChannelId, Price, PriceFeedId, PriceFeedProperty, Rate,
  14. };
  15. #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, ToSchema)]
  16. #[serde(rename_all = "camelCase")]
  17. pub struct LatestPriceRequestRepr {
  18. // Either price feed ids or symbols must be specified.
  19. #[schema(example = json!([1]))]
  20. pub price_feed_ids: Option<Vec<PriceFeedId>>,
  21. #[schema(example = schema_default_symbols)]
  22. pub symbols: Option<Vec<String>>,
  23. pub properties: Vec<PriceFeedProperty>,
  24. // "chains" was renamed to "formats". "chains" is still supported for compatibility.
  25. #[serde(alias = "chains")]
  26. pub formats: Vec<Format>,
  27. #[serde(default)]
  28. pub json_binary_encoding: JsonBinaryEncoding,
  29. /// If `true`, the stream update will contain a JSON object containing
  30. /// all data of the update.
  31. #[serde(default = "default_parsed")]
  32. pub parsed: bool,
  33. pub channel: Channel,
  34. }
  35. #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, ToSchema)]
  36. #[serde(rename_all = "camelCase")]
  37. pub struct LatestPriceRequest(LatestPriceRequestRepr);
  38. impl<'de> Deserialize<'de> for LatestPriceRequest {
  39. fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
  40. where
  41. D: serde::Deserializer<'de>,
  42. {
  43. let value = LatestPriceRequestRepr::deserialize(deserializer)?;
  44. Self::new(value).map_err(Error::custom)
  45. }
  46. }
  47. impl LatestPriceRequest {
  48. pub fn new(value: LatestPriceRequestRepr) -> Result<Self, &'static str> {
  49. validate_price_feed_ids_or_symbols(&value.price_feed_ids, &value.symbols)?;
  50. validate_optional_nonempty_vec_has_unique_elements(
  51. &value.price_feed_ids,
  52. "no price feed ids specified",
  53. "duplicate price feed ids specified",
  54. )?;
  55. validate_optional_nonempty_vec_has_unique_elements(
  56. &value.symbols,
  57. "no symbols specified",
  58. "duplicate symbols specified",
  59. )?;
  60. validate_formats(&value.formats)?;
  61. validate_properties(&value.properties)?;
  62. Ok(Self(value))
  63. }
  64. }
  65. impl Deref for LatestPriceRequest {
  66. type Target = LatestPriceRequestRepr;
  67. fn deref(&self) -> &Self::Target {
  68. &self.0
  69. }
  70. }
  71. impl DerefMut for LatestPriceRequest {
  72. fn deref_mut(&mut self) -> &mut Self::Target {
  73. &mut self.0
  74. }
  75. }
  76. #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, ToSchema)]
  77. #[serde(rename_all = "camelCase")]
  78. pub struct PriceRequestRepr {
  79. pub timestamp: TimestampUs,
  80. // Either price feed ids or symbols must be specified.
  81. pub price_feed_ids: Option<Vec<PriceFeedId>>,
  82. #[schema(default)]
  83. pub symbols: Option<Vec<String>>,
  84. pub properties: Vec<PriceFeedProperty>,
  85. pub formats: Vec<Format>,
  86. #[serde(default)]
  87. pub json_binary_encoding: JsonBinaryEncoding,
  88. /// If `true`, the stream update will contain a JSON object containing
  89. /// all data of the update.
  90. #[serde(default = "default_parsed")]
  91. pub parsed: bool,
  92. pub channel: Channel,
  93. }
  94. #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, ToSchema)]
  95. #[serde(rename_all = "camelCase")]
  96. pub struct PriceRequest(PriceRequestRepr);
  97. impl<'de> Deserialize<'de> for PriceRequest {
  98. fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
  99. where
  100. D: serde::Deserializer<'de>,
  101. {
  102. let value = PriceRequestRepr::deserialize(deserializer)?;
  103. Self::new(value).map_err(Error::custom)
  104. }
  105. }
  106. impl PriceRequest {
  107. pub fn new(value: PriceRequestRepr) -> Result<Self, &'static str> {
  108. validate_price_feed_ids_or_symbols(&value.price_feed_ids, &value.symbols)?;
  109. validate_optional_nonempty_vec_has_unique_elements(
  110. &value.price_feed_ids,
  111. "no price feed ids specified",
  112. "duplicate price feed ids specified",
  113. )?;
  114. validate_optional_nonempty_vec_has_unique_elements(
  115. &value.symbols,
  116. "no symbols specified",
  117. "duplicate symbols specified",
  118. )?;
  119. validate_formats(&value.formats)?;
  120. validate_properties(&value.properties)?;
  121. Ok(Self(value))
  122. }
  123. }
  124. impl Deref for PriceRequest {
  125. type Target = PriceRequestRepr;
  126. fn deref(&self) -> &Self::Target {
  127. &self.0
  128. }
  129. }
  130. impl DerefMut for PriceRequest {
  131. fn deref_mut(&mut self) -> &mut Self::Target {
  132. &mut self.0
  133. }
  134. }
  135. #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, ToSchema)]
  136. #[serde(rename_all = "camelCase")]
  137. pub struct ReducePriceRequest {
  138. pub payload: JsonUpdate,
  139. pub price_feed_ids: Vec<PriceFeedId>,
  140. }
  141. pub type LatestPriceResponse = JsonUpdate;
  142. pub type ReducePriceResponse = JsonUpdate;
  143. pub type PriceResponse = JsonUpdate;
  144. pub fn default_parsed() -> bool {
  145. true
  146. }
  147. pub fn schema_default_symbols() -> Option<Vec<String>> {
  148. None
  149. }
  150. pub fn schema_default_price_feed_ids() -> Option<Vec<PriceFeedId>> {
  151. Some(vec![PriceFeedId(1)])
  152. }
  153. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize, ToSchema)]
  154. #[serde(rename_all = "camelCase")]
  155. pub enum DeliveryFormat {
  156. /// Deliver stream updates as JSON text messages.
  157. #[default]
  158. Json,
  159. /// Deliver stream updates as binary messages.
  160. Binary,
  161. }
  162. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, ToSchema)]
  163. #[serde(rename_all = "camelCase")]
  164. pub enum Format {
  165. Evm,
  166. Solana,
  167. LeEcdsa,
  168. LeUnsigned,
  169. }
  170. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize, ToSchema)]
  171. #[serde(rename_all = "camelCase")]
  172. pub enum JsonBinaryEncoding {
  173. #[default]
  174. Base64,
  175. Hex,
  176. }
  177. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, From, ToSchema)]
  178. #[schema(example = "fixed_rate@200ms")]
  179. pub enum Channel {
  180. FixedRate(FixedRate),
  181. #[schema(rename = "real_time")]
  182. RealTime,
  183. }
  184. impl PartialOrd for Channel {
  185. fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
  186. let rate_left = match self {
  187. Channel::FixedRate(rate) => rate.duration().as_micros(),
  188. Channel::RealTime => FixedRate::MIN.duration().as_micros(),
  189. };
  190. let rate_right = match other {
  191. Channel::FixedRate(rate) => rate.duration().as_micros(),
  192. Channel::RealTime => FixedRate::MIN.duration().as_micros(),
  193. };
  194. Some(rate_left.cmp(&rate_right))
  195. }
  196. }
  197. impl Serialize for Channel {
  198. fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
  199. where
  200. S: serde::Serializer,
  201. {
  202. match self {
  203. Channel::FixedRate(fixed_rate) => serializer.serialize_str(&format!(
  204. "fixed_rate@{}ms",
  205. fixed_rate.duration().as_millis()
  206. )),
  207. Channel::RealTime => serializer.serialize_str("real_time"),
  208. }
  209. }
  210. }
  211. impl Display for Channel {
  212. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  213. match self {
  214. Channel::FixedRate(fixed_rate) => {
  215. write!(f, "fixed_rate@{}ms", fixed_rate.duration().as_millis())
  216. }
  217. Channel::RealTime => write!(f, "real_time"),
  218. }
  219. }
  220. }
  221. impl Channel {
  222. pub fn id(&self) -> ChannelId {
  223. match self {
  224. Channel::FixedRate(fixed_rate) => match fixed_rate.duration().as_millis() {
  225. 50 => ChannelId::FIXED_RATE_50,
  226. 200 => ChannelId::FIXED_RATE_200,
  227. 1000 => ChannelId::FIXED_RATE_1000,
  228. _ => panic!("unknown channel: {self:?}"),
  229. },
  230. Channel::RealTime => ChannelId::REAL_TIME,
  231. }
  232. }
  233. }
  234. #[test]
  235. fn id_supports_all_fixed_rates() {
  236. for rate in FixedRate::ALL {
  237. Channel::FixedRate(rate).id();
  238. }
  239. }
  240. fn parse_channel(value: &str) -> Option<Channel> {
  241. if value == "real_time" {
  242. Some(Channel::RealTime)
  243. } else if let Some(rest) = value.strip_prefix("fixed_rate@") {
  244. let ms_value = rest.strip_suffix("ms")?;
  245. Some(Channel::FixedRate(FixedRate::from_millis(
  246. ms_value.parse().ok()?,
  247. )?))
  248. } else {
  249. None
  250. }
  251. }
  252. impl<'de> Deserialize<'de> for Channel {
  253. fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
  254. where
  255. D: serde::Deserializer<'de>,
  256. {
  257. let value = <String>::deserialize(deserializer)?;
  258. parse_channel(&value).ok_or_else(|| Error::custom("unknown channel"))
  259. }
  260. }
  261. #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, ToSchema)]
  262. #[serde(rename_all = "camelCase")]
  263. pub struct SubscriptionParamsRepr {
  264. // Either price feed ids or symbols must be specified.
  265. pub price_feed_ids: Option<Vec<PriceFeedId>>,
  266. #[schema(default)]
  267. pub symbols: Option<Vec<String>>,
  268. pub properties: Vec<PriceFeedProperty>,
  269. // "chains" was renamed to "formats". "chains" is still supported for compatibility.
  270. #[serde(alias = "chains")]
  271. pub formats: Vec<Format>,
  272. #[serde(default)]
  273. pub delivery_format: DeliveryFormat,
  274. #[serde(default)]
  275. pub json_binary_encoding: JsonBinaryEncoding,
  276. /// If `true`, the stream update will contain a `parsed` JSON field containing
  277. /// all data of the update.
  278. #[serde(default = "default_parsed")]
  279. pub parsed: bool,
  280. pub channel: Channel,
  281. // "ignoreInvalidFeedIds" was renamed to "ignoreInvalidFeeds". "ignoreInvalidFeedIds" is still supported for compatibility.
  282. #[serde(default, alias = "ignoreInvalidFeedIds")]
  283. pub ignore_invalid_feeds: bool,
  284. }
  285. #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, ToSchema)]
  286. #[serde(rename_all = "camelCase")]
  287. pub struct SubscriptionParams(SubscriptionParamsRepr);
  288. impl<'de> Deserialize<'de> for SubscriptionParams {
  289. fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
  290. where
  291. D: serde::Deserializer<'de>,
  292. {
  293. let value = SubscriptionParamsRepr::deserialize(deserializer)?;
  294. Self::new(value).map_err(Error::custom)
  295. }
  296. }
  297. impl SubscriptionParams {
  298. pub fn new(value: SubscriptionParamsRepr) -> Result<Self, &'static str> {
  299. validate_price_feed_ids_or_symbols(&value.price_feed_ids, &value.symbols)?;
  300. validate_optional_nonempty_vec_has_unique_elements(
  301. &value.price_feed_ids,
  302. "no price feed ids specified",
  303. "duplicate price feed ids specified",
  304. )?;
  305. validate_optional_nonempty_vec_has_unique_elements(
  306. &value.symbols,
  307. "no symbols specified",
  308. "duplicate symbols specified",
  309. )?;
  310. validate_formats(&value.formats)?;
  311. validate_properties(&value.properties)?;
  312. Ok(Self(value))
  313. }
  314. }
  315. impl Deref for SubscriptionParams {
  316. type Target = SubscriptionParamsRepr;
  317. fn deref(&self) -> &Self::Target {
  318. &self.0
  319. }
  320. }
  321. impl DerefMut for SubscriptionParams {
  322. fn deref_mut(&mut self) -> &mut Self::Target {
  323. &mut self.0
  324. }
  325. }
  326. #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, ToSchema)]
  327. #[serde(rename_all = "camelCase")]
  328. pub struct JsonBinaryData {
  329. pub encoding: JsonBinaryEncoding,
  330. pub data: String,
  331. }
  332. #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, ToSchema)]
  333. #[serde(rename_all = "camelCase")]
  334. pub struct JsonUpdate {
  335. /// Present unless `parsed = false` is specified in subscription params.
  336. #[serde(skip_serializing_if = "Option::is_none")]
  337. pub parsed: Option<ParsedPayload>,
  338. /// Only present if `Evm` is present in `formats` in subscription params.
  339. #[serde(skip_serializing_if = "Option::is_none")]
  340. pub evm: Option<JsonBinaryData>,
  341. /// Only present if `Solana` is present in `formats` in subscription params.
  342. #[serde(skip_serializing_if = "Option::is_none")]
  343. pub solana: Option<JsonBinaryData>,
  344. /// Only present if `LeEcdsa` is present in `formats` in subscription params.
  345. #[serde(skip_serializing_if = "Option::is_none")]
  346. pub le_ecdsa: Option<JsonBinaryData>,
  347. /// Only present if `LeUnsigned` is present in `formats` in subscription params.
  348. #[serde(skip_serializing_if = "Option::is_none")]
  349. pub le_unsigned: Option<JsonBinaryData>,
  350. }
  351. #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, ToSchema)]
  352. #[serde(rename_all = "camelCase")]
  353. pub struct ParsedPayload {
  354. #[serde(with = "crate::serde_str::timestamp")]
  355. pub timestamp_us: TimestampUs,
  356. pub price_feeds: Vec<ParsedFeedPayload>,
  357. }
  358. #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, ToSchema)]
  359. #[serde(rename_all = "camelCase")]
  360. pub struct ParsedFeedPayload {
  361. pub price_feed_id: PriceFeedId,
  362. #[serde(skip_serializing_if = "Option::is_none")]
  363. #[serde(with = "crate::serde_str::option_price")]
  364. #[serde(default)]
  365. pub price: Option<Price>,
  366. #[serde(skip_serializing_if = "Option::is_none")]
  367. #[serde(with = "crate::serde_str::option_price")]
  368. #[serde(default)]
  369. pub best_bid_price: Option<Price>,
  370. #[serde(skip_serializing_if = "Option::is_none")]
  371. #[serde(with = "crate::serde_str::option_price")]
  372. #[serde(default)]
  373. pub best_ask_price: Option<Price>,
  374. #[serde(skip_serializing_if = "Option::is_none")]
  375. #[serde(default)]
  376. pub publisher_count: Option<u16>,
  377. #[serde(skip_serializing_if = "Option::is_none")]
  378. #[serde(default)]
  379. pub exponent: Option<i16>,
  380. #[serde(skip_serializing_if = "Option::is_none")]
  381. #[serde(default)]
  382. pub confidence: Option<Price>,
  383. #[serde(skip_serializing_if = "Option::is_none")]
  384. #[serde(default)]
  385. pub funding_rate: Option<Rate>,
  386. #[serde(skip_serializing_if = "Option::is_none")]
  387. #[serde(default)]
  388. pub funding_timestamp: Option<TimestampUs>,
  389. // More fields may be added later.
  390. #[serde(skip_serializing_if = "Option::is_none")]
  391. #[serde(default)]
  392. pub funding_rate_interval: Option<DurationUs>,
  393. }
  394. impl ParsedFeedPayload {
  395. pub fn new(
  396. price_feed_id: PriceFeedId,
  397. data: &AggregatedPriceFeedData,
  398. properties: &[PriceFeedProperty],
  399. ) -> Self {
  400. let mut output = Self {
  401. price_feed_id,
  402. price: None,
  403. best_bid_price: None,
  404. best_ask_price: None,
  405. publisher_count: None,
  406. exponent: None,
  407. confidence: None,
  408. funding_rate: None,
  409. funding_timestamp: None,
  410. funding_rate_interval: None,
  411. };
  412. for &property in properties {
  413. match property {
  414. PriceFeedProperty::Price => {
  415. output.price = data.price;
  416. }
  417. PriceFeedProperty::BestBidPrice => {
  418. output.best_bid_price = data.best_bid_price;
  419. }
  420. PriceFeedProperty::BestAskPrice => {
  421. output.best_ask_price = data.best_ask_price;
  422. }
  423. PriceFeedProperty::PublisherCount => {
  424. output.publisher_count = Some(data.publisher_count);
  425. }
  426. PriceFeedProperty::Exponent => {
  427. output.exponent = Some(data.exponent);
  428. }
  429. PriceFeedProperty::Confidence => {
  430. output.confidence = data.confidence;
  431. }
  432. PriceFeedProperty::FundingRate => {
  433. output.funding_rate = data.funding_rate;
  434. }
  435. PriceFeedProperty::FundingTimestamp => {
  436. output.funding_timestamp = data.funding_timestamp;
  437. }
  438. PriceFeedProperty::FundingRateInterval => {
  439. output.funding_rate_interval = data.funding_rate_interval;
  440. }
  441. }
  442. }
  443. output
  444. }
  445. pub fn new_full(
  446. price_feed_id: PriceFeedId,
  447. exponent: Option<i16>,
  448. data: &AggregatedPriceFeedData,
  449. ) -> Self {
  450. Self {
  451. price_feed_id,
  452. price: data.price,
  453. best_bid_price: data.best_bid_price,
  454. best_ask_price: data.best_ask_price,
  455. publisher_count: Some(data.publisher_count),
  456. exponent,
  457. confidence: data.confidence,
  458. funding_rate: data.funding_rate,
  459. funding_timestamp: data.funding_timestamp,
  460. funding_rate_interval: data.funding_rate_interval,
  461. }
  462. }
  463. }
  464. /// A request sent from the client to the server.
  465. #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, ToSchema)]
  466. #[serde(tag = "type")]
  467. #[serde(rename_all = "camelCase")]
  468. pub enum WsRequest {
  469. Subscribe(SubscribeRequest),
  470. Unsubscribe(UnsubscribeRequest),
  471. }
  472. #[derive(
  473. Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, ToSchema,
  474. )]
  475. pub struct SubscriptionId(pub u64);
  476. #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, ToSchema)]
  477. #[serde(rename_all = "camelCase")]
  478. pub struct SubscribeRequest {
  479. pub subscription_id: SubscriptionId,
  480. #[serde(flatten)]
  481. pub params: SubscriptionParams,
  482. }
  483. #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, ToSchema)]
  484. #[serde(rename_all = "camelCase")]
  485. pub struct UnsubscribeRequest {
  486. pub subscription_id: SubscriptionId,
  487. }
  488. /// A JSON response sent from the server to the client.
  489. #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, From, ToSchema)]
  490. #[serde(tag = "type")]
  491. #[serde(rename_all = "camelCase")]
  492. pub enum WsResponse {
  493. Error(ErrorResponse),
  494. Subscribed(SubscribedResponse),
  495. SubscribedWithInvalidFeedIdsIgnored(SubscribedWithInvalidFeedIdsIgnoredResponse),
  496. Unsubscribed(UnsubscribedResponse),
  497. SubscriptionError(SubscriptionErrorResponse),
  498. StreamUpdated(StreamUpdatedResponse),
  499. }
  500. /// Sent from the server after a successul subscription.
  501. #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, ToSchema)]
  502. #[serde(rename_all = "camelCase")]
  503. pub struct SubscribedResponse {
  504. pub subscription_id: SubscriptionId,
  505. }
  506. #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, ToSchema)]
  507. #[serde(rename_all = "camelCase")]
  508. pub struct InvalidFeedSubscriptionDetails {
  509. pub unknown_ids: Vec<PriceFeedId>,
  510. pub unknown_symbols: Vec<String>,
  511. pub unsupported_channels: Vec<PriceFeedId>,
  512. pub unstable: Vec<PriceFeedId>,
  513. }
  514. #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, ToSchema)]
  515. #[serde(rename_all = "camelCase")]
  516. pub struct SubscribedWithInvalidFeedIdsIgnoredResponse {
  517. pub subscription_id: SubscriptionId,
  518. pub subscribed_feed_ids: Vec<PriceFeedId>,
  519. pub ignored_invalid_feed_ids: InvalidFeedSubscriptionDetails,
  520. }
  521. #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, ToSchema)]
  522. #[serde(rename_all = "camelCase")]
  523. pub struct UnsubscribedResponse {
  524. pub subscription_id: SubscriptionId,
  525. }
  526. /// Sent from the server if the requested subscription or unsubscription request
  527. /// could not be fulfilled.
  528. #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, ToSchema)]
  529. #[serde(rename_all = "camelCase")]
  530. pub struct SubscriptionErrorResponse {
  531. pub subscription_id: SubscriptionId,
  532. pub error: String,
  533. }
  534. /// Sent from the server if an internal error occured while serving data for an existing subscription,
  535. /// or a client request sent a bad request.
  536. #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, ToSchema)]
  537. #[serde(rename_all = "camelCase")]
  538. pub struct ErrorResponse {
  539. pub error: String,
  540. }
  541. /// Sent from the server when new data is available for an existing subscription
  542. /// (only if `delivery_format == Json`).
  543. #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, ToSchema)]
  544. #[serde(rename_all = "camelCase")]
  545. pub struct StreamUpdatedResponse {
  546. pub subscription_id: SubscriptionId,
  547. #[serde(flatten)]
  548. pub payload: JsonUpdate,
  549. }
  550. // Common validation functions
  551. fn validate_price_feed_ids_or_symbols(
  552. price_feed_ids: &Option<Vec<PriceFeedId>>,
  553. symbols: &Option<Vec<String>>,
  554. ) -> Result<(), &'static str> {
  555. if price_feed_ids.is_none() && symbols.is_none() {
  556. return Err("either price feed ids or symbols must be specified");
  557. }
  558. if price_feed_ids.is_some() && symbols.is_some() {
  559. return Err("either price feed ids or symbols must be specified, not both");
  560. }
  561. Ok(())
  562. }
  563. fn validate_optional_nonempty_vec_has_unique_elements<T>(
  564. vec: &Option<Vec<T>>,
  565. empty_msg: &'static str,
  566. duplicate_msg: &'static str,
  567. ) -> Result<(), &'static str>
  568. where
  569. T: Eq + std::hash::Hash,
  570. {
  571. if let Some(ref items) = vec {
  572. if items.is_empty() {
  573. return Err(empty_msg);
  574. }
  575. if !items.iter().all_unique() {
  576. return Err(duplicate_msg);
  577. }
  578. }
  579. Ok(())
  580. }
  581. fn validate_properties(properties: &[PriceFeedProperty]) -> Result<(), &'static str> {
  582. if properties.is_empty() {
  583. return Err("no properties specified");
  584. }
  585. if !properties.iter().all_unique() {
  586. return Err("duplicate properties specified");
  587. }
  588. Ok(())
  589. }
  590. fn validate_formats(formats: &[Format]) -> Result<(), &'static str> {
  591. if !formats.iter().all_unique() {
  592. return Err("duplicate formats or chains specified");
  593. }
  594. Ok(())
  595. }