vivacity_core/
phpserialize.rs1use crate::error::{Error, Result};
11use serde_json::Value;
12use std::fmt::Write as _;
13
14pub fn serialize(v: &Value) -> Result<String> {
16 let mut out = String::new();
17 write_value(v, &mut out)?;
18 Ok(out)
19}
20
21fn write_value(v: &Value, out: &mut String) -> Result<()> {
22 match v {
23 Value::Null => out.push_str("N;"),
24 Value::Bool(b) => {
25 let _ = write!(out, "b:{};", u8::from(*b));
26 }
27 Value::Number(n) => {
28 if let Some(i) = n.as_i64() {
29 let _ = write!(out, "i:{i};");
30 } else if let Some(f) = n.as_f64() {
31 let _ = write!(
33 out,
34 "d:{};",
35 crate::phpjson::php_double(f)?.replace('e', "E")
36 );
37 } else {
38 return Err(Error::NonFiniteFloat(f64::NAN));
39 }
40 }
41 Value::String(s) => write_string(s, out),
42 Value::Array(items) => {
43 let _ = write!(out, "a:{}:{{", items.len());
44 for (i, item) in items.iter().enumerate() {
45 let _ = write!(out, "i:{i};");
46 write_value(item, out)?;
47 }
48 out.push('}');
49 }
50 Value::Object(map) => {
51 let _ = write!(out, "a:{}:{{", map.len());
52 for (k, item) in map {
53 match php_int_key(k) {
54 Some(i) => {
55 let _ = write!(out, "i:{i};");
56 }
57 None => write_string(k, out),
58 }
59 write_value(item, out)?;
60 }
61 out.push('}');
62 }
63 }
64 Ok(())
65}
66
67fn write_string(s: &str, out: &mut String) {
68 let _ = write!(out, "s:{}:\"{s}\";", s.len());
69}
70
71fn php_int_key(k: &str) -> Option<i64> {
75 let digits = k.strip_prefix('-').unwrap_or(k);
76 if digits.is_empty() || !digits.bytes().all(|b| b.is_ascii_digit()) {
77 return None;
78 }
79 if digits.len() > 1 && digits.starts_with('0') {
80 return None;
81 }
82 if k == "-0" {
83 return None;
84 }
85 k.parse::<i64>().ok()
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91 use serde_json::json;
92
93 #[test]
94 fn path_repository_options() {
95 assert_eq!(
96 serialize(&json!({"relative": true})).unwrap(),
97 "a:1:{s:8:\"relative\";b:1;}"
98 );
99 assert_eq!(
100 serialize(&json!({"symlink": false, "versions": {"acme/beta": "2.0.0"}, "relative": true}))
101 .unwrap(),
102 "a:3:{s:7:\"symlink\";b:0;s:8:\"versions\";a:1:{s:9:\"acme/beta\";s:5:\"2.0.0\";}s:8:\"relative\";b:1;}"
103 );
104 }
105
106 #[test]
107 fn scalars_and_keys_like_php() {
108 let v: Value = serde_json::from_str(
110 r#"{"a":1.5,"b":10,"c":null,"d":[1,"x"],"e":{},"f":[],"g":0.1,"h":1.0,"i":100000000000000000000,"j":1e-7,"k":-0.0,"l":"é"}"#,
111 )
112 .unwrap();
113 assert_eq!(
114 serialize(&v).unwrap(),
115 "a:12:{s:1:\"a\";d:1.5;s:1:\"b\";i:10;s:1:\"c\";N;s:1:\"d\";a:2:{i:0;i:1;i:1;s:1:\"x\";}s:1:\"e\";a:0:{}s:1:\"f\";a:0:{}s:1:\"g\";d:0.1;s:1:\"h\";d:1;s:1:\"i\";d:1.0E+20;s:1:\"j\";d:1.0E-7;s:1:\"k\";d:-0;s:1:\"l\";s:2:\"é\";}"
116 );
117 let v: Value = serde_json::from_str(
118 r#"{"1":"x","05":"y","-3":"z","1.0":"w","9223372036854775807":1,"9223372036854775808":2}"#,
119 )
120 .unwrap();
121 assert_eq!(
122 serialize(&v).unwrap(),
123 "a:6:{i:1;s:1:\"x\";s:2:\"05\";s:1:\"y\";i:-3;s:1:\"z\";s:3:\"1.0\";s:1:\"w\";i:9223372036854775807;i:1;s:19:\"9223372036854775808\";i:2;}"
124 );
125 let v: Value = serde_json::from_str(
126 "[18446744073709551615, 9223372036854775808, -9223372036854775808]",
127 )
128 .unwrap();
129 assert_eq!(
130 serialize(&v).unwrap(),
131 "a:3:{i:0;d:1.8446744073709552E+19;i:1;d:9.223372036854776E+18;i:2;i:-9223372036854775808;}"
132 );
133 }
134}