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