1#[cfg(not(feature = "std"))]
17use alloc::{format, string::String, string::ToString, vec::Vec};
18
19use base64::{engine::general_purpose::STANDARD, Engine as _};
20
21#[cfg(feature = "std")]
25pub fn canonical_json(value: &serde_json::Value) -> Vec<u8> {
26 let mut out = String::new();
27 encode_value(value, &mut out);
28 out.into_bytes()
29}
30
31#[cfg(feature = "std")]
32fn encode_value(v: &serde_json::Value, out: &mut String) {
33 use serde_json::Value;
34 match v {
35 Value::Null => out.push_str("null"),
36 Value::Bool(true) => out.push_str("true"),
37 Value::Bool(false) => out.push_str("false"),
38 Value::Number(n) => out.push_str(&encode_number(n)),
39 Value::String(s) => encode_string(s, out),
40 Value::Array(arr) => {
41 out.push('[');
42 for (i, item) in arr.iter().enumerate() {
43 if i > 0 {
44 out.push(',');
45 }
46 encode_value(item, out);
47 }
48 out.push(']');
49 }
50 Value::Object(obj) => {
51 let mut keys: Vec<&String> = obj.keys().collect();
53 keys.sort();
54 out.push('{');
55 let mut first = true;
56 for k in keys {
57 let val = &obj[k];
58 if val.is_null() {
60 continue;
61 }
62 if !first {
63 out.push(',');
64 }
65 encode_string(k, out);
66 out.push(':');
67 encode_value(val, out);
68 first = false;
69 }
70 out.push('}');
71 }
72 }
73}
74
75#[cfg(feature = "std")]
81fn encode_number(n: &serde_json::Number) -> String {
82 if let Some(i) = n.as_i64() {
83 return i.to_string();
84 }
85 if let Some(u) = n.as_u64() {
86 return u.to_string();
87 }
88 if let Some(f) = n.as_f64() {
89 if f.is_finite() && f.fract() == 0.0 && f.abs() < 1e15 {
90 return (f as i64).to_string();
92 }
93 return f.to_string();
94 }
95 n.to_string()
96}
97
98fn encode_string(s: &str, out: &mut String) {
99 out.push('"');
100 for c in s.chars() {
101 match c {
102 '"' => out.push_str("\\\""),
103 '\\' => out.push_str("\\\\"),
104 '\u{0008}' => out.push_str("\\b"),
105 '\u{0009}' => out.push_str("\\t"),
106 '\u{000A}' => out.push_str("\\n"),
107 '\u{000C}' => out.push_str("\\f"),
108 '\u{000D}' => out.push_str("\\r"),
109 '\u{2028}' => out.push_str("\\u2028"),
110 '\u{2029}' => out.push_str("\\u2029"),
111 c if (c as u32) < 0x20 => {
112 out.push_str(&format!("\\u{:04x}", c as u32));
113 }
114 c => out.push(c),
116 }
117 }
118 out.push('"');
119}
120
121pub fn base64_std_encode(data: &[u8]) -> String {
123 STANDARD.encode(data)
124}
125
126pub fn base64_std_decode(s: &str) -> Result<Vec<u8>, base64::DecodeError> {
128 STANDARD.decode(s)
129}
130
131pub fn hex_encode(data: &[u8]) -> String {
133 hex::encode(data)
134}
135
136pub fn hex_decode(s: &str) -> Result<Vec<u8>, hex::FromHexError> {
138 hex::decode(s)
139}
140
141pub fn encode_str(s: &str, out: &mut String) {
150 encode_string(s, out);
151}
152
153pub fn encode_i64(n: i64, out: &mut String) {
155 out.push_str(&n.to_string());
156}
157
158pub fn encode_i32(n: i32, out: &mut String) {
160 out.push_str(&n.to_string());
161}
162
163pub fn encode_f64(n: f64, out: &mut String) {
166 if n.is_finite() && n.fract() == 0.0 && n.abs() < 1e15 {
167 out.push_str(&(n as i64).to_string());
168 } else {
169 out.push_str(&n.to_string());
170 }
171}
172
173pub fn encode_bool(b: bool, out: &mut String) {
175 out.push_str(if b { "true" } else { "false" });
176}
177
178pub fn encode_bytes_b64(b: &[u8], out: &mut String) {
180 let s = base64_std_encode(b);
181 encode_string(&s, out);
182}
183
184pub fn encode_str_array(arr: &[String], out: &mut String) {
186 out.push('[');
187 for (i, s) in arr.iter().enumerate() {
188 if i > 0 {
189 out.push(',');
190 }
191 encode_string(s, out);
192 }
193 out.push(']');
194}
195
196pub fn encode_points_array(pts: &[[f64; 2]], out: &mut String) {
198 out.push('[');
199 for (i, pt) in pts.iter().enumerate() {
200 if i > 0 {
201 out.push(',');
202 }
203 out.push('[');
204 encode_f64(pt[0], out);
205 out.push(',');
206 encode_f64(pt[1], out);
207 out.push(']');
208 }
209 out.push(']');
210}
211
212pub fn encode_constraint(c: &crate::types::Constraint, out: &mut String) {
215 match c.kind.as_str() {
216 "geo_circle" => {
217 out.push('{');
219 out.push_str("\"lat\":"); encode_f64(c.lat, out);
220 out.push_str(",\"lon\":"); encode_f64(c.lon, out);
221 out.push_str(",\"radius_m\":"); encode_f64(c.radius_m, out);
222 out.push_str(",\"type\":"); encode_string(&c.kind, out);
223 out.push('}');
224 }
225 "geo_polygon" => {
226 out.push('{');
228 out.push_str("\"points\":"); encode_points_array(&c.points, out);
229 out.push_str(",\"type\":"); encode_string(&c.kind, out);
230 out.push('}');
231 }
232 "geo_bbox" => {
233 out.push('{');
236 let has_alt = c.min_alt_m != 0.0 || c.max_alt_m != 0.0;
237 if has_alt {
238 out.push_str("\"max_alt_m\":"); encode_f64(c.max_alt_m, out);
239 out.push(',');
240 }
241 out.push_str("\"max_lat\":"); encode_f64(c.max_lat, out);
242 out.push_str(",\"max_lon\":"); encode_f64(c.max_lon, out);
243 if has_alt {
244 out.push_str(",\"min_alt_m\":"); encode_f64(c.min_alt_m, out);
245 }
246 out.push_str(",\"min_lat\":"); encode_f64(c.min_lat, out);
247 out.push_str(",\"min_lon\":"); encode_f64(c.min_lon, out);
248 out.push_str(",\"type\":"); encode_string(&c.kind, out);
249 out.push('}');
250 }
251 "time_window" => {
252 out.push('{');
254 out.push_str("\"end\":"); encode_string(&c.end, out);
255 out.push_str(",\"start\":"); encode_string(&c.start, out);
256 out.push_str(",\"type\":"); encode_string(&c.kind, out);
257 out.push_str(",\"tz\":"); encode_string(&c.tz, out);
258 out.push('}');
259 }
260 "max_speed_mps" => {
261 out.push('{');
263 out.push_str("\"max_mps\":"); encode_f64(c.max_mps, out);
264 out.push_str(",\"type\":"); encode_string(&c.kind, out);
265 out.push('}');
266 }
267 "max_amount" => {
268 out.push('{');
270 out.push_str("\"currency\":"); encode_string(&c.currency, out);
271 out.push_str(",\"max_amount\":"); encode_f64(c.max_amount, out);
272 out.push_str(",\"type\":"); encode_string(&c.kind, out);
273 out.push('}');
274 }
275 "max_rate" => {
276 out.push('{');
278 out.push_str("\"count\":"); encode_i64(c.count, out);
279 out.push_str(",\"type\":"); encode_string(&c.kind, out);
280 out.push_str(",\"window_s\":"); encode_i64(c.window_s, out);
281 out.push('}');
282 }
283 _ => {
285 out.push('{');
286 out.push_str("\"type\":"); encode_string(&c.kind, out);
287 out.push('}');
288 }
289 }
290}
291
292pub fn encode_constraints(cs: &[crate::types::Constraint], out: &mut String) {
294 out.push('[');
295 for (i, c) in cs.iter().enumerate() {
296 if i > 0 {
297 out.push(',');
298 }
299 encode_constraint(c, out);
300 }
301 out.push(']');
302}
303
304pub fn encode_hybrid_pub_key(pk: &crate::types::HybridPublicKey, out: &mut String) {
307 out.push('{');
308 out.push_str("\"ed25519\":"); encode_bytes_b64(&pk.ed25519, out);
309 out.push_str(",\"ml_dsa_65\":"); encode_bytes_b64(&pk.ml_dsa_65, out);
310 out.push('}');
311}
312
313pub fn encode_hybrid_sig(sig: &crate::types::HybridSignature, out: &mut String) {
315 out.push('{');
316 out.push_str("\"ed25519\":"); encode_bytes_b64(&sig.ed25519, out);
317 out.push_str(",\"ml_dsa_65\":"); encode_bytes_b64(&sig.ml_dsa_65, out);
318 out.push('}');
319}
320
321pub mod base64_bytes {
323 #[cfg(not(feature = "std"))]
324 use alloc::{format, string::String, vec::Vec};
325 use super::{base64_std_decode, base64_std_encode};
326 use serde::{de::Error, Deserialize, Deserializer, Serializer};
327
328 pub fn serialize<S>(bytes: &Vec<u8>, serializer: S) -> Result<S::Ok, S::Error>
329 where
330 S: Serializer,
331 {
332 serializer.serialize_str(&base64_std_encode(bytes))
333 }
334
335 pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
336 where
337 D: Deserializer<'de>,
338 {
339 let s = String::deserialize(deserializer)?;
340 base64_std_decode(&s).map_err(|e| D::Error::custom(format!("base64 decode: {e}")))
341 }
342}