Skip to content

Commit bbe0371

Browse files
committed
support inf/ninf/nan
1 parent 759a78f commit bbe0371

File tree

2 files changed

+252
-1
lines changed

2 files changed

+252
-1
lines changed

src/gen/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ mod numeric;
99
mod primitives;
1010
mod strings;
1111
mod tuples;
12+
mod value;
1213

1314
// public api
1415
pub use binary::binary;
@@ -322,7 +323,9 @@ pub fn generate_from_schema<T: serde::de::DeserializeOwned>(schema: &Value) -> T
322323
buffer_generated_value(&format!("Generated: {}", result));
323324
}
324325

325-
serde_json::from_value(result.clone()).unwrap_or_else(|e| {
326+
// Convert to HegelValue to handle NaN/Infinity sentinel strings
327+
let hegel_value = value::HegelValue::from(result.clone());
328+
value::from_hegel_value(hegel_value).unwrap_or_else(|e| {
326329
panic!(
327330
"hegel: failed to deserialize server response: {}\nValue: {}",
328331
e, result

src/gen/value.rs

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
use serde::de::{self, Deserializer, MapAccess, SeqAccess, Visitor};
2+
use serde::forward_to_deserialize_any;
3+
use std::collections::HashMap;
4+
use std::fmt;
5+
6+
const HEGEL_INF: &str = "hegel-inf-a928fa52";
7+
const HEGEL_NINF: &str = "hegel-ninf-a928fa52";
8+
const HEGEL_NAN: &str = "hegel-nan-a928fa52";
9+
10+
/// A JSON-like value that can hold NaN and Infinity.
11+
#[derive(Clone, Debug)]
12+
pub enum HegelValue {
13+
Null,
14+
Bool(bool),
15+
Number(f64),
16+
String(String),
17+
Array(Vec<HegelValue>),
18+
Object(HashMap<String, HegelValue>),
19+
}
20+
21+
impl From<serde_json::Value> for HegelValue {
22+
fn from(v: serde_json::Value) -> Self {
23+
match v {
24+
serde_json::Value::Null => HegelValue::Null,
25+
serde_json::Value::Bool(b) => HegelValue::Bool(b),
26+
serde_json::Value::Number(n) => HegelValue::Number(n.as_f64().unwrap_or(0.0)),
27+
serde_json::Value::String(s) => {
28+
// Check for sentinel strings
29+
match s.as_str() {
30+
HEGEL_INF => HegelValue::Number(f64::INFINITY),
31+
HEGEL_NINF => HegelValue::Number(f64::NEG_INFINITY),
32+
HEGEL_NAN => HegelValue::Number(f64::NAN),
33+
_ => HegelValue::String(s),
34+
}
35+
}
36+
serde_json::Value::Array(arr) => {
37+
HegelValue::Array(arr.into_iter().map(HegelValue::from).collect())
38+
}
39+
serde_json::Value::Object(map) => {
40+
HegelValue::Object(map.into_iter().map(|(k, v)| (k, HegelValue::from(v))).collect())
41+
}
42+
}
43+
}
44+
}
45+
46+
#[derive(Debug)]
47+
pub struct HegelValueError(String);
48+
49+
impl fmt::Display for HegelValueError {
50+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51+
write!(f, "{}", self.0)
52+
}
53+
}
54+
55+
impl std::error::Error for HegelValueError {}
56+
57+
impl de::Error for HegelValueError {
58+
fn custom<T: fmt::Display>(msg: T) -> Self {
59+
HegelValueError(msg.to_string())
60+
}
61+
}
62+
63+
impl<'de> Deserializer<'de> for HegelValue {
64+
type Error = HegelValueError;
65+
66+
fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
67+
match self {
68+
HegelValue::Null => visitor.visit_unit(),
69+
HegelValue::Bool(b) => visitor.visit_bool(b),
70+
HegelValue::Number(n) => {
71+
// For whole numbers that fit in i64, use visit_i64 so integer
72+
// deserialization works. NaN/Inf have fract() != 0, so they
73+
// go to visit_f64.
74+
if n.fract() == 0.0 && n >= i64::MIN as f64 && n <= i64::MAX as f64 {
75+
visitor.visit_i64(n as i64)
76+
} else {
77+
visitor.visit_f64(n)
78+
}
79+
}
80+
HegelValue::String(s) => visitor.visit_string(s),
81+
HegelValue::Array(arr) => visitor.visit_seq(HegelSeqAccess {
82+
iter: arr.into_iter(),
83+
}),
84+
HegelValue::Object(map) => visitor.visit_map(HegelMapAccess {
85+
iter: map.into_iter(),
86+
value: None,
87+
}),
88+
}
89+
}
90+
91+
fn deserialize_option<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
92+
match self {
93+
HegelValue::Null => visitor.visit_none(),
94+
_ => visitor.visit_some(self),
95+
}
96+
}
97+
98+
forward_to_deserialize_any! {
99+
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
100+
bytes byte_buf unit unit_struct newtype_struct seq tuple
101+
tuple_struct map struct enum identifier ignored_any
102+
}
103+
}
104+
105+
struct HegelSeqAccess {
106+
iter: std::vec::IntoIter<HegelValue>,
107+
}
108+
109+
impl<'de> SeqAccess<'de> for HegelSeqAccess {
110+
type Error = HegelValueError;
111+
112+
fn next_element_seed<T: de::DeserializeSeed<'de>>(
113+
&mut self,
114+
seed: T,
115+
) -> Result<Option<T::Value>, Self::Error> {
116+
match self.iter.next() {
117+
Some(value) => seed.deserialize(value).map(Some),
118+
None => Ok(None),
119+
}
120+
}
121+
}
122+
123+
struct HegelMapAccess {
124+
iter: std::collections::hash_map::IntoIter<String, HegelValue>,
125+
value: Option<HegelValue>,
126+
}
127+
128+
impl<'de> MapAccess<'de> for HegelMapAccess {
129+
type Error = HegelValueError;
130+
131+
fn next_key_seed<K: de::DeserializeSeed<'de>>(
132+
&mut self,
133+
seed: K,
134+
) -> Result<Option<K::Value>, Self::Error> {
135+
match self.iter.next() {
136+
Some((key, value)) => {
137+
self.value = Some(value);
138+
seed.deserialize(StringDeserializer(key)).map(Some)
139+
}
140+
None => Ok(None),
141+
}
142+
}
143+
144+
fn next_value_seed<V: de::DeserializeSeed<'de>>(
145+
&mut self,
146+
seed: V,
147+
) -> Result<V::Value, Self::Error> {
148+
let value = self
149+
.value
150+
.take()
151+
.expect("next_value called before next_key");
152+
seed.deserialize(value)
153+
}
154+
}
155+
156+
struct StringDeserializer(String);
157+
158+
impl<'de> Deserializer<'de> for StringDeserializer {
159+
type Error = HegelValueError;
160+
161+
fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
162+
visitor.visit_string(self.0)
163+
}
164+
165+
forward_to_deserialize_any! {
166+
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
167+
bytes byte_buf option unit unit_struct newtype_struct seq tuple
168+
tuple_struct map struct enum identifier ignored_any
169+
}
170+
}
171+
172+
pub fn from_hegel_value<T: de::DeserializeOwned>(value: HegelValue) -> Result<T, HegelValueError> {
173+
T::deserialize(value)
174+
}
175+
176+
#[cfg(test)]
177+
mod tests {
178+
use super::*;
179+
180+
#[test]
181+
fn test_deserialize_f64() {
182+
let v = HegelValue::Number(42.5);
183+
let result: f64 = from_hegel_value(v).unwrap();
184+
assert_eq!(result, 42.5);
185+
}
186+
187+
#[test]
188+
fn test_deserialize_nan() {
189+
let v = HegelValue::Number(f64::NAN);
190+
let result: f64 = from_hegel_value(v).unwrap();
191+
assert!(result.is_nan());
192+
}
193+
194+
#[test]
195+
fn test_deserialize_infinity() {
196+
let v = HegelValue::Number(f64::INFINITY);
197+
let result: f64 = from_hegel_value(v).unwrap();
198+
assert!(result.is_infinite() && result.is_sign_positive());
199+
}
200+
201+
#[test]
202+
fn test_deserialize_neg_infinity() {
203+
let v = HegelValue::Number(f64::NEG_INFINITY);
204+
let result: f64 = from_hegel_value(v).unwrap();
205+
assert!(result.is_infinite() && result.is_sign_negative());
206+
}
207+
208+
#[test]
209+
fn test_deserialize_vec_f64() {
210+
let v = HegelValue::Array(vec![
211+
HegelValue::Number(1.0),
212+
HegelValue::Number(f64::NAN),
213+
HegelValue::Number(f64::INFINITY),
214+
]);
215+
let result: Vec<f64> = from_hegel_value(v).unwrap();
216+
assert_eq!(result.len(), 3);
217+
assert_eq!(result[0], 1.0);
218+
assert!(result[1].is_nan());
219+
assert!(result[2].is_infinite());
220+
}
221+
222+
#[test]
223+
fn test_from_serde_json_sentinels() {
224+
let json = serde_json::json!([1.0, "hegel-nan-a928fa52", "hegel-inf-a928fa52"]);
225+
let hegel = HegelValue::from(json);
226+
let result: Vec<f64> = from_hegel_value(hegel).unwrap();
227+
assert_eq!(result[0], 1.0);
228+
assert!(result[1].is_nan());
229+
assert!(result[2].is_infinite());
230+
}
231+
232+
#[test]
233+
fn test_deserialize_struct() {
234+
#[derive(serde::Deserialize, Debug)]
235+
struct TestStruct {
236+
value: f64,
237+
name: String,
238+
}
239+
240+
let v = HegelValue::Object(HashMap::from([
241+
("value".to_string(), HegelValue::Number(f64::NAN)),
242+
("name".to_string(), HegelValue::String("test".to_string())),
243+
]));
244+
let result: TestStruct = from_hegel_value(v).unwrap();
245+
assert!(result.value.is_nan());
246+
assert_eq!(result.name, "test");
247+
}
248+
}

0 commit comments

Comments
 (0)