1use crate::value::Value;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum CanonicalFailureCode {
16 NanOrInf,
17 InvalidValue,
18}
19
20impl CanonicalFailureCode {
21 pub fn as_str(self) -> &'static str {
22 match self {
23 CanonicalFailureCode::NanOrInf => "NAN_OR_INF",
24 CanonicalFailureCode::InvalidValue => "INVALID_VALUE",
25 }
26 }
27}
28
29#[derive(Debug, Clone)]
30pub struct CanonicalFailure {
31 pub code: CanonicalFailureCode,
32 pub message: String,
33}
34
35impl std::fmt::Display for CanonicalFailure {
36 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37 write!(f, "{}: {}", self.code.as_str(), self.message)
38 }
39}
40impl std::error::Error for CanonicalFailure {}
41
42fn fail<T>(code: CanonicalFailureCode, message: impl Into<String>) -> Result<T, CanonicalFailure> {
43 Err(CanonicalFailure {
44 code,
45 message: message.into(),
46 })
47}
48
49pub fn py_float_repr(f: f64) -> Result<String, CanonicalFailure> {
53 if f.is_nan() || f.is_infinite() {
54 return fail(
55 CanonicalFailureCode::NanOrInf,
56 format!("non-finite float cannot be serialized: {f}"),
57 );
58 }
59 if f == 0.0 {
62 return Ok(if f.is_sign_negative() {
63 "-0.0".into()
64 } else {
65 "0.0".into()
66 });
67 }
68
69 let (neg, digits, decpt) = shortest_digits(f);
70 let n = digits.len() as i32;
71
72 let out = if decpt <= -4 || decpt > 16 {
74 let mut mant = String::new();
75 mant.push(digits.as_bytes()[0] as char);
76 if n > 1 {
77 mant.push('.');
78 mant.push_str(&digits[1..]);
79 }
80 let e = decpt - 1;
81 let esign = if e < 0 { '-' } else { '+' };
82 let mut eabs = e.abs().to_string();
83 if eabs.len() < 2 {
84 eabs = format!("0{eabs}");
85 }
86 format!("{mant}e{esign}{eabs}")
87 } else if decpt <= 0 {
88 format!("0.{}{}", "0".repeat((-decpt) as usize), digits)
89 } else if decpt >= n {
90 format!("{}{}.0", digits, "0".repeat((decpt - n) as usize))
91 } else {
92 format!(
93 "{}.{}",
94 &digits[..decpt as usize],
95 &digits[decpt as usize..]
96 )
97 };
98
99 Ok(if neg { format!("-{out}") } else { out })
100}
101
102fn shortest_digits(f: f64) -> (bool, String, i32) {
106 let mut chosen: Option<String> = None;
107 for p in 0..=17usize {
108 let s = format!("{:.*e}", p, f);
109 if s.parse::<f64>() == Ok(f) {
110 chosen = Some(s);
111 break;
112 }
113 }
114 let s = chosen.unwrap_or_else(|| format!("{:.17e}", f));
115 let mut bytes = s.as_str();
116 let neg = bytes.starts_with('-');
117 if neg {
118 bytes = &bytes[1..];
119 }
120 let (mantissa, exp_part) = match bytes.split_once('e') {
121 Some((m, e)) => (m, e),
122 None => (bytes, "0"),
123 };
124 let exp: i32 = exp_part.parse().expect("f64 {:e} exponent is integer");
125 let (int_part, frac_part) = match mantissa.split_once('.') {
126 Some((i, f)) => (i, f),
127 None => (mantissa, ""),
128 };
129 let mut digits: String = format!("{int_part}{frac_part}");
130 let mut decpt = int_part.len() as i32 + exp;
131
132 let lead = digits.len() - digits.trim_start_matches('0').len();
133 if lead == digits.len() {
134 return (neg, "0".to_string(), 1);
135 }
136 digits = digits[lead..].to_string();
137 decpt -= lead as i32;
138
139 let trimmed = digits.trim_end_matches('0');
140 let digits = if trimmed.is_empty() {
141 "0".to_string()
142 } else {
143 trimmed.to_string()
144 };
145 (neg, digits, decpt)
146}
147
148pub fn canonical_value(v: &Value) -> Result<String, CanonicalFailure> {
152 if let Value::Obj(pairs) = v {
153 let mut keyed: Vec<&(String, Value)> = pairs.iter().collect();
154 keyed.sort_by(|a, b| a.0.cmp(&b.0)); let mut parts = Vec::with_capacity(keyed.len());
156 for (k, val) in keyed {
157 parts.push(format!("{}:{}", json_string(k), encode_nested(val)?));
158 }
159 Ok(format!("{{{}}}", parts.join(",")))
160 } else {
161 encode_nested(v)
162 }
163}
164
165pub fn canonical_json(v: &Value) -> Result<String, CanonicalFailure> {
169 match v {
170 Value::Obj(pairs) => {
171 let mut keyed: Vec<&(String, Value)> = pairs.iter().collect();
172 keyed.sort_by(|a, b| a.0.cmp(&b.0));
173 let mut parts = Vec::with_capacity(keyed.len());
174 for (k, val) in keyed {
175 parts.push(format!("{}:{}", json_string(k), canonical_json(val)?));
176 }
177 Ok(format!("{{{}}}", parts.join(",")))
178 }
179 Value::Arr(a) => {
180 let mut parts = Vec::with_capacity(a.len());
181 for e in a {
182 parts.push(canonical_json(e)?);
183 }
184 Ok(format!("[{}]", parts.join(",")))
185 }
186 _ => scalar_json(v),
187 }
188}
189
190fn encode_nested(v: &Value) -> Result<String, CanonicalFailure> {
191 match v {
192 Value::Obj(pairs) => {
193 let mut parts = Vec::with_capacity(pairs.len());
194 for (k, val) in pairs {
195 parts.push(format!("{}:{}", json_string(k), encode_nested(val)?));
196 }
197 Ok(format!("{{{}}}", parts.join(",")))
198 }
199 Value::Arr(a) => {
200 let mut parts = Vec::with_capacity(a.len());
201 for e in a {
202 parts.push(encode_nested(e)?);
203 }
204 Ok(format!("[{}]", parts.join(",")))
205 }
206 _ => scalar_json(v),
207 }
208}
209
210fn scalar_json(v: &Value) -> Result<String, CanonicalFailure> {
211 match v {
212 Value::Null => Ok("null".into()),
213 Value::Bool(b) => Ok(if *b { "true".into() } else { "false".into() }),
214 Value::Str(s) => Ok(json_string(s)),
215 Value::Int(i) => Ok(i.to_string()),
216 Value::Float(f) => py_float_repr(*f),
217 _ => fail(
218 CanonicalFailureCode::InvalidValue,
219 "cannot serialize value of unknown type",
220 ),
221 }
222}
223
224fn json_string(s: &str) -> String {
227 let mut out = String::with_capacity(s.len() + 2);
228 out.push('"');
229 for c in s.chars() {
230 match c {
231 '"' => out.push_str("\\\""),
232 '\\' => out.push_str("\\\\"),
233 '\n' => out.push_str("\\n"),
234 '\r' => out.push_str("\\r"),
235 '\t' => out.push_str("\\t"),
236 '\u{08}' => out.push_str("\\b"),
237 '\u{0c}' => out.push_str("\\f"),
238 c if (c as u32) < 0x20 => out.push_str(&format!("\\u{:04x}", c as u32)),
239 c => out.push(c),
240 }
241 }
242 out.push('"');
243 out
244}