1use std::collections::HashMap;
4use std::fmt;
5
6#[derive(Debug, Clone, PartialEq)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9#[cfg_attr(feature = "serde", serde(untagged))]
10pub enum Value {
11 Null,
12 Bool(bool),
13 Int(i64),
14 Float(f64),
15 String(String),
16 Array(Vec<Value>),
17 Object(HashMap<String, Value>),
18}
19
20impl Value {
21 pub fn is_truthy(&self) -> bool {
24 match self {
25 Value::Null => false,
26 Value::Bool(b) => *b,
27 Value::Int(i) => *i != 0,
28 Value::Float(f) => *f != 0.0,
29 Value::String(s) => !s.is_empty(),
30 Value::Array(_) => true,
31 Value::Object(_) => true,
32 }
33 }
34
35 pub fn is_null(&self) -> bool {
36 matches!(self, Value::Null)
37 }
38
39 pub fn type_name(&self) -> &'static str {
40 match self {
41 Value::Null => "null",
42 Value::Bool(_) => "bool",
43 Value::Int(_) => "int",
44 Value::Float(_) => "float",
45 Value::String(_) => "string",
46 Value::Array(_) => "array",
47 Value::Object(_) => "object",
48 }
49 }
50
51 pub fn to_display_string(&self) -> String {
53 match self {
54 Value::Null => "null".to_string(),
55 Value::Bool(b) => b.to_string(),
56 Value::Int(i) => i.to_string(),
57 Value::Float(f) => {
58 if f.fract() == 0.0 && f.abs() < 1e15 {
59 format!("{}", *f as i64)
60 } else {
61 f.to_string()
62 }
63 }
64 Value::String(s) => s.clone(),
65 Value::Array(arr) => {
66 let parts: Vec<String> = arr.iter().map(|v| v.to_display_string()).collect();
67 parts.join(",")
68 }
69 Value::Object(_) => "[object Object]".to_string(),
70 }
71 }
72
73 pub fn to_json_string(&self) -> String {
75 match self {
76 Value::Null => "null".to_string(),
77 Value::Bool(b) => b.to_string(),
78 Value::Int(i) => i.to_string(),
79 Value::Float(f) => f.to_string(),
80 Value::String(s) => {
81 let mut out = String::with_capacity(s.len() + 2);
82 out.push('"');
83 for c in s.chars() {
84 match c {
85 '"' => out.push_str("\\\""),
86 '\\' => out.push_str("\\\\"),
87 '\n' => out.push_str("\\n"),
88 '\r' => out.push_str("\\r"),
89 '\t' => out.push_str("\\t"),
90 c => out.push(c),
91 }
92 }
93 out.push('"');
94 out
95 }
96 Value::Array(arr) => {
97 let parts: Vec<String> = arr.iter().map(|v| v.to_json_string()).collect();
98 format!("[{}]", parts.join(","))
99 }
100 Value::Object(obj) => {
101 let mut pairs: Vec<String> = obj
102 .iter()
103 .map(|(k, v)| format!("\"{}\":{}", json_escape_str(k), v.to_json_string()))
104 .collect();
105 pairs.sort(); format!("{{{}}}", pairs.join(","))
107 }
108 }
109 }
110
111 pub fn html_escaped(&self) -> String {
113 html_escape(&self.to_display_string())
114 }
115
116 pub fn length(&self) -> Option<usize> {
117 match self {
118 Value::String(s) => Some(s.chars().count()),
119 Value::Array(a) => Some(a.len()),
120 Value::Object(o) => Some(o.len()),
121 _ => None,
122 }
123 }
124
125 pub fn is_empty(&self) -> bool {
126 match self {
127 Value::String(s) => s.is_empty(),
128 Value::Array(a) => a.is_empty(),
129 Value::Object(o) => o.is_empty(),
130 _ => false,
131 }
132 }
133}
134
135pub fn html_escape(s: &str) -> String {
137 let mut out = String::with_capacity(s.len());
138 for c in s.chars() {
139 match c {
140 '&' => out.push_str("&"),
141 '<' => out.push_str("<"),
142 '>' => out.push_str(">"),
143 '"' => out.push_str("""),
144 '\'' => out.push_str("'"),
145 c => out.push(c),
146 }
147 }
148 out
149}
150
151pub fn urlencode(s: &str) -> String {
153 let mut out = String::new();
154 for byte in s.bytes() {
155 match byte {
156 b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'.' | b'~' => {
157 out.push(byte as char)
158 }
159 b => out.push_str(&format!("%{:02X}", b)),
160 }
161 }
162 out
163}
164
165fn json_escape_str(s: &str) -> String {
166 let mut out = String::with_capacity(s.len());
167 for c in s.chars() {
168 match c {
169 '"' => out.push_str("\\\""),
170 '\\' => out.push_str("\\\\"),
171 '\n' => out.push_str("\\n"),
172 '\r' => out.push_str("\\r"),
173 '\t' => out.push_str("\\t"),
174 c => out.push(c),
175 }
176 }
177 out
178}
179
180impl fmt::Display for Value {
181 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182 write!(f, "{}", self.to_display_string())
183 }
184}
185
186impl From<bool> for Value {
187 fn from(b: bool) -> Self {
188 Value::Bool(b)
189 }
190}
191impl From<i64> for Value {
192 fn from(i: i64) -> Self {
193 Value::Int(i)
194 }
195}
196impl From<f64> for Value {
197 fn from(f: f64) -> Self {
198 Value::Float(f)
199 }
200}
201impl From<String> for Value {
202 fn from(s: String) -> Self {
203 Value::String(s)
204 }
205}
206impl From<&str> for Value {
207 fn from(s: &str) -> Self {
208 Value::String(s.to_string())
209 }
210}
211impl From<Vec<Value>> for Value {
212 fn from(v: Vec<Value>) -> Self {
213 Value::Array(v)
214 }
215}
216impl From<HashMap<String, Value>> for Value {
217 fn from(m: HashMap<String, Value>) -> Self {
218 Value::Object(m)
219 }
220}
221
222#[cfg(feature = "serde")]
223impl Value {
224 pub fn from_serialize<T: serde::Serialize>(val: &T) -> Self {
230 serde_json::to_value(val)
231 .map(Into::into)
232 .unwrap_or(Value::Null)
233 }
234}
235
236#[cfg(feature = "serde")]
237impl From<serde_json::Value> for Value {
238 fn from(v: serde_json::Value) -> Self {
239 match v {
240 serde_json::Value::Null => Value::Null,
241 serde_json::Value::Bool(b) => Value::Bool(b),
242 serde_json::Value::Number(n) => {
243 if let Some(i) = n.as_i64() {
244 Value::Int(i)
245 } else {
246 Value::Float(n.as_f64().unwrap_or(f64::NAN))
247 }
248 }
249 serde_json::Value::String(s) => Value::String(s),
250 serde_json::Value::Array(a) => Value::Array(a.into_iter().map(Into::into).collect()),
251 serde_json::Value::Object(o) => {
252 Value::Object(o.into_iter().map(|(k, v)| (k, v.into())).collect())
253 }
254 }
255 }
256}