197 lines
5.9 KiB
Rust
197 lines
5.9 KiB
Rust
//! Check typed pending intent numbers before JSON could replace NaN/Inf with null.
|
|
//! This traverses the original Serialize representation without materializing it.
|
|
use serde::{Serialize, Serializer, ser};
|
|
|
|
#[derive(Clone, Copy)]
|
|
struct Finite;
|
|
|
|
pub(crate) fn validate(value: &impl Serialize) -> Result<(), serde_json::Error> {
|
|
value.serialize(Finite)
|
|
}
|
|
|
|
macro_rules! scalar {
|
|
($($method:ident: $ty:ty),* $(,)?) => {$(
|
|
fn $method(self, _: $ty) -> Result<(), Self::Error> { Ok(()) }
|
|
)*};
|
|
}
|
|
|
|
impl Serializer for Finite {
|
|
type Ok = ();
|
|
type Error = serde_json::Error;
|
|
type SerializeSeq = Self;
|
|
type SerializeTuple = Self;
|
|
type SerializeTupleStruct = Self;
|
|
type SerializeTupleVariant = Self;
|
|
type SerializeMap = Self;
|
|
type SerializeStruct = Self;
|
|
type SerializeStructVariant = Self;
|
|
|
|
scalar!(serialize_bool: bool, serialize_i8: i8, serialize_i16: i16,
|
|
serialize_i32: i32, serialize_i64: i64, serialize_i128: i128,
|
|
serialize_u8: u8, serialize_u16: u16, serialize_u32: u32,
|
|
serialize_u64: u64, serialize_u128: u128, serialize_char: char,
|
|
serialize_str: &str, serialize_bytes: &[u8]);
|
|
fn serialize_f32(self, value: f32) -> Result<(), Self::Error> {
|
|
self.serialize_f64(f64::from(value))
|
|
}
|
|
fn serialize_f64(self, value: f64) -> Result<(), Self::Error> {
|
|
if value.is_finite() {
|
|
Ok(())
|
|
} else {
|
|
Err(ser::Error::custom(
|
|
"pending strategy intent contains a non-finite number",
|
|
))
|
|
}
|
|
}
|
|
fn serialize_none(self) -> Result<(), Self::Error> {
|
|
Ok(())
|
|
}
|
|
fn serialize_some<T: ?Sized + Serialize>(self, value: &T) -> Result<(), Self::Error> {
|
|
value.serialize(self)
|
|
}
|
|
fn serialize_unit(self) -> Result<(), Self::Error> {
|
|
Ok(())
|
|
}
|
|
fn serialize_unit_struct(self, _: &'static str) -> Result<(), Self::Error> {
|
|
Ok(())
|
|
}
|
|
fn serialize_unit_variant(
|
|
self,
|
|
_: &'static str,
|
|
_: u32,
|
|
_: &'static str,
|
|
) -> Result<(), Self::Error> {
|
|
Ok(())
|
|
}
|
|
fn serialize_newtype_struct<T: ?Sized + Serialize>(
|
|
self,
|
|
_: &'static str,
|
|
value: &T,
|
|
) -> Result<(), Self::Error> {
|
|
value.serialize(self)
|
|
}
|
|
fn serialize_newtype_variant<T: ?Sized + Serialize>(
|
|
self,
|
|
_: &'static str,
|
|
_: u32,
|
|
_: &'static str,
|
|
value: &T,
|
|
) -> Result<(), Self::Error> {
|
|
value.serialize(self)
|
|
}
|
|
fn serialize_seq(self, _: Option<usize>) -> Result<Self, Self::Error> {
|
|
Ok(self)
|
|
}
|
|
fn serialize_tuple(self, _: usize) -> Result<Self, Self::Error> {
|
|
Ok(self)
|
|
}
|
|
fn serialize_tuple_struct(self, _: &'static str, _: usize) -> Result<Self, Self::Error> {
|
|
Ok(self)
|
|
}
|
|
fn serialize_tuple_variant(
|
|
self,
|
|
_: &'static str,
|
|
_: u32,
|
|
_: &'static str,
|
|
_: usize,
|
|
) -> Result<Self, Self::Error> {
|
|
Ok(self)
|
|
}
|
|
fn serialize_map(self, _: Option<usize>) -> Result<Self, Self::Error> {
|
|
Ok(self)
|
|
}
|
|
fn serialize_struct(self, _: &'static str, _: usize) -> Result<Self, Self::Error> {
|
|
Ok(self)
|
|
}
|
|
fn serialize_struct_variant(
|
|
self,
|
|
_: &'static str,
|
|
_: u32,
|
|
_: &'static str,
|
|
_: usize,
|
|
) -> Result<Self, Self::Error> {
|
|
Ok(self)
|
|
}
|
|
}
|
|
|
|
macro_rules! sequence {
|
|
($trait:ident, $method:ident) => {
|
|
impl ser::$trait for Finite {
|
|
type Ok = ();
|
|
type Error = serde_json::Error;
|
|
fn $method<T: ?Sized + Serialize>(&mut self, value: &T) -> Result<(), Self::Error> {
|
|
value.serialize(*self)
|
|
}
|
|
fn end(self) -> Result<(), Self::Error> {
|
|
Ok(())
|
|
}
|
|
}
|
|
};
|
|
}
|
|
sequence!(SerializeSeq, serialize_element);
|
|
sequence!(SerializeTuple, serialize_element);
|
|
sequence!(SerializeTupleStruct, serialize_field);
|
|
sequence!(SerializeTupleVariant, serialize_field);
|
|
|
|
impl ser::SerializeMap for Finite {
|
|
type Ok = ();
|
|
type Error = serde_json::Error;
|
|
fn serialize_key<T: ?Sized + Serialize>(&mut self, value: &T) -> Result<(), Self::Error> {
|
|
value.serialize(*self)
|
|
}
|
|
fn serialize_value<T: ?Sized + Serialize>(&mut self, value: &T) -> Result<(), Self::Error> {
|
|
value.serialize(*self)
|
|
}
|
|
fn end(self) -> Result<(), Self::Error> {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
macro_rules! structure {
|
|
($trait:ident) => {
|
|
impl ser::$trait for Finite {
|
|
type Ok = ();
|
|
type Error = serde_json::Error;
|
|
fn serialize_field<T: ?Sized + Serialize>(
|
|
&mut self,
|
|
_: &'static str,
|
|
value: &T,
|
|
) -> Result<(), Self::Error> {
|
|
value.serialize(*self)
|
|
}
|
|
fn end(self) -> Result<(), Self::Error> {
|
|
Ok(())
|
|
}
|
|
}
|
|
};
|
|
}
|
|
structure!(SerializeStruct);
|
|
structure!(SerializeStructVariant);
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::strategy::{OrderIntent, StrategyDecision};
|
|
|
|
#[test]
|
|
fn pending_numbers_cannot_be_silently_serialized_as_optional_nulls() {
|
|
for value in [f64::NAN, f64::INFINITY, f64::NEG_INFINITY] {
|
|
let decision = StrategyDecision {
|
|
order_intents: vec![
|
|
OrderIntent::LimitTargetPercent {
|
|
symbol: "000001.SZ".into(),
|
|
target_percent: 0.5,
|
|
limit_price: value,
|
|
reason: "test".into(),
|
|
}
|
|
.with_time_in_force(crate::strategy::OrderTimeInForce::Day),
|
|
],
|
|
..Default::default()
|
|
};
|
|
assert!(validate(&decision).is_err());
|
|
assert!(validate(&vec![Some(value)]).is_err());
|
|
}
|
|
assert!(validate(&(None::<f64>, vec![0., -0., 0.123456789], "NaN")).is_ok());
|
|
}
|
|
}
|