Skip to content

Commit e18e462

Browse files
committed
feat(pyth-lazer-protocol): add deserializers with validation for LatestPriceRequest and PriceRequest
1 parent 6f9d6da commit e18e462

File tree

1 file changed

+137
-2
lines changed
  • lazer/sdk/rust/protocol/src

1 file changed

+137
-2
lines changed

lazer/sdk/rust/protocol/src/api.rs

Lines changed: 137 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ use crate::{
1616

1717
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
1818
#[serde(rename_all = "camelCase")]
19-
pub struct LatestPriceRequest {
19+
pub struct LatestPriceRequestRepr {
20+
// Either price feed ids or symbols must be specified.
2021
pub price_feed_ids: Option<Vec<PriceFeedId>>,
2122
pub symbols: Option<Vec<String>>,
2223
pub properties: Vec<PriceFeedProperty>,
@@ -32,9 +33,76 @@ pub struct LatestPriceRequest {
3233
pub channel: Channel,
3334
}
3435

36+
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
37+
#[serde(rename_all = "camelCase")]
38+
pub struct LatestPriceRequest(LatestPriceRequestRepr);
39+
40+
impl<'de> Deserialize<'de> for LatestPriceRequest {
41+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
42+
where
43+
D: serde::Deserializer<'de>,
44+
{
45+
let value = LatestPriceRequestRepr::deserialize(deserializer)?;
46+
Self::new(value).map_err(Error::custom)
47+
}
48+
}
49+
50+
impl LatestPriceRequest {
51+
pub fn new(value: LatestPriceRequestRepr) -> Result<Self, &'static str> {
52+
if value.price_feed_ids.is_none() && value.symbols.is_none() {
53+
return Err("either price feed ids or symbols must be specified");
54+
}
55+
if value.price_feed_ids.is_some() && value.symbols.is_some() {
56+
return Err("either price feed ids or symbols must be specified, not both");
57+
}
58+
59+
if let Some(ref ids) = value.price_feed_ids {
60+
if ids.is_empty() {
61+
return Err("no price feed ids specified");
62+
}
63+
if !ids.iter().all_unique() {
64+
return Err("duplicate price feed ids specified");
65+
}
66+
}
67+
68+
if let Some(ref symbols) = value.symbols {
69+
if symbols.is_empty() {
70+
return Err("no symbols specified");
71+
}
72+
if !symbols.iter().all_unique() {
73+
return Err("duplicate symbols specified");
74+
}
75+
}
76+
77+
if !value.formats.iter().all_unique() {
78+
return Err("duplicate formats or chains specified");
79+
}
80+
if value.properties.is_empty() {
81+
return Err("no properties specified");
82+
}
83+
if !value.properties.iter().all_unique() {
84+
return Err("duplicate properties specified");
85+
}
86+
Ok(Self(value))
87+
}
88+
}
89+
90+
impl Deref for LatestPriceRequest {
91+
type Target = LatestPriceRequestRepr;
92+
93+
fn deref(&self) -> &Self::Target {
94+
&self.0
95+
}
96+
}
97+
impl DerefMut for LatestPriceRequest {
98+
fn deref_mut(&mut self) -> &mut Self::Target {
99+
&mut self.0
100+
}
101+
}
102+
35103
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
36104
#[serde(rename_all = "camelCase")]
37-
pub struct PriceRequest {
105+
pub struct PriceRequestRepr {
38106
pub timestamp: TimestampUs,
39107
// Either price feed ids or symbols must be specified.
40108
pub price_feed_ids: Option<Vec<PriceFeedId>>,
@@ -50,6 +118,73 @@ pub struct PriceRequest {
50118
pub channel: Channel,
51119
}
52120

121+
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
122+
#[serde(rename_all = "camelCase")]
123+
pub struct PriceRequest(PriceRequestRepr);
124+
125+
impl<'de> Deserialize<'de> for PriceRequest {
126+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
127+
where
128+
D: serde::Deserializer<'de>,
129+
{
130+
let value = PriceRequestRepr::deserialize(deserializer)?;
131+
Self::new(value).map_err(Error::custom)
132+
}
133+
}
134+
135+
impl PriceRequest {
136+
pub fn new(value: PriceRequestRepr) -> Result<Self, &'static str> {
137+
if value.price_feed_ids.is_none() && value.symbols.is_none() {
138+
return Err("either price feed ids or symbols must be specified");
139+
}
140+
if value.price_feed_ids.is_some() && value.symbols.is_some() {
141+
return Err("either price feed ids or symbols must be specified, not both");
142+
}
143+
144+
if let Some(ref ids) = value.price_feed_ids {
145+
if ids.is_empty() {
146+
return Err("no price feed ids specified");
147+
}
148+
if !ids.iter().all_unique() {
149+
return Err("duplicate price feed ids specified");
150+
}
151+
}
152+
153+
if let Some(ref symbols) = value.symbols {
154+
if symbols.is_empty() {
155+
return Err("no symbols specified");
156+
}
157+
if !symbols.iter().all_unique() {
158+
return Err("duplicate symbols specified");
159+
}
160+
}
161+
162+
if !value.formats.iter().all_unique() {
163+
return Err("duplicate formats or chains specified");
164+
}
165+
if value.properties.is_empty() {
166+
return Err("no properties specified");
167+
}
168+
if !value.properties.iter().all_unique() {
169+
return Err("duplicate properties specified");
170+
}
171+
Ok(Self(value))
172+
}
173+
}
174+
175+
impl Deref for PriceRequest {
176+
type Target = PriceRequestRepr;
177+
178+
fn deref(&self) -> &Self::Target {
179+
&self.0
180+
}
181+
}
182+
impl DerefMut for PriceRequest {
183+
fn deref_mut(&mut self) -> &mut Self::Target {
184+
&mut self.0
185+
}
186+
}
187+
53188
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
54189
#[serde(rename_all = "camelCase")]
55190
pub struct ReducePriceRequest {

0 commit comments

Comments
 (0)