1use crate::node::{Kind, ListMode, Node};
6use crate::value::{js_date_string, js_number, json_text, Value};
7
8pub fn stringify_node(n: &Node, inline: bool) -> String {
11 match n.kind {
12 Kind::String => suffix(type_or_value(n, "String", inline), n),
13 Kind::Number => suffix(type_or_value(n, "Number", inline), n),
14 Kind::Boolean => suffix(type_or_value(n, "Boolean", inline), n),
15 Kind::Integer => suffix(type_or_value(n, "Integer", inline), n),
16 Kind::BigInt => suffix(type_or_value(n, "BigInt", inline), n),
17 Kind::Date => {
18 let base = match n.default {
19 Value::Date(ms) if !n.required && n.has_default => {
20 let iso = js_date_string(ms);
21 if inline {
22 iso
23 } else {
24 format!("\"{}\"", iso)
25 }
26 }
27 _ => "Date".to_string(),
28 };
29 suffix(base, n)
30 }
31 Kind::Null => "null".to_string(),
32 Kind::NaN => "NaN".to_string(),
33 Kind::Any => {
34 let base = if n.has_default && !n.default.is_undefined() {
36 format!("Any({})", inline_value(&n.default))
37 } else {
38 "Any".to_string()
39 };
40 let out = suffix(base.clone(), n);
41 match out.strip_prefix("Any.") {
45 Some(rest) if base == "Any" => rest.to_string(),
46 _ => out,
47 }
48 }
49 Kind::Never => suffix("Never".to_string(), n),
50 Kind::Regexp => match &n.regexp {
51 Some(_) => suffix(format!("/{}/", n.regexp_src), n),
52 None => suffix("Regexp".to_string(), n),
53 },
54 Kind::Check => suffix("Check".to_string(), n),
55 Kind::Function => suffix("Function".to_string(), n),
56 Kind::List => {
57 if let Some(d) = &n.disc {
58 return suffix(format!("Discriminated({},{})", d.tag, d.tags.join(",")), n);
59 }
60 let mode = match n.list_mode {
61 ListMode::Some => "Some",
62 ListMode::All => "All",
63 _ => "One",
64 };
65 let parts: Vec<String> = n.list.iter().map(|sn| stringify_node(sn, true)).collect();
66 suffix(format!("{}({})", mode, parts.join(",")), n)
67 }
68 Kind::Array => {
69 let mut parts: Vec<String> = Vec::new();
70 if !n.arr_children.is_empty() {
71 parts.extend(n.arr_children.iter().map(|sn| stringify_node(sn, true)));
72 } else if let Some(c) = &n.arr_child {
73 parts.push(stringify_node(c, true));
74 }
75 if let Some(r) = &n.arr_rest {
76 parts.push(format!("...{}", stringify_node(r, true)));
77 }
78 suffix(format!("[{}]", parts.join(", ")), n)
79 }
80 Kind::Object => {
81 let parts: Vec<String> = n
82 .obj_children
83 .iter()
84 .map(|(k, cn)| format!("{}: {}", k, stringify_node(cn, true)))
85 .collect();
86 let mut body = format!("{{{}}}", parts.join(", "));
87 match &n.obj_rest {
88 Some(rest) if rest.kind != Kind::Any => {
89 body.push_str(&format!(".Child({})", stringify_node(rest, true)));
90 }
91 Some(_) => body.push_str(".Open()"),
92 None => {}
93 }
94 suffix(body, n)
95 }
96 }
97}
98
99fn suffix(base: String, n: &Node) -> String {
104 let mut out = base;
105 for v in n.befores.iter().chain(n.afters.iter()) {
106 if let Some(s) = &v.suffix {
107 out.push('.');
108 out.push_str(s);
109 }
110 }
111 out
112}
113
114fn type_or_value(n: &Node, type_name: &str, inline: bool) -> String {
118 if n.required || !n.has_default {
119 return type_name.to_string();
120 }
121 if let Value::Str(s) = &n.default {
122 if !inline {
123 return json_text(s);
124 }
125 }
126 inline_value(&n.default)
127}
128
129pub(crate) fn inline_value(v: &Value) -> String {
132 match v {
133 Value::Str(s) => s.clone(),
134 Value::Num(n) => js_number(*n),
135 Value::Undefined => "undefined".to_string(),
136 other => crate::value::json_render(other),
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use super::*;
143 use crate::builders::*;
144 use crate::node::Token;
145 use crate::spec::{arr, obj, Spec};
146 use regex::Regex;
147
148 #[test]
149 fn renders_types_values_and_chains() {
150 assert_eq!(stringify_node(&buildize(Token::String), false), "String");
151 assert_eq!(stringify_node(&buildize("x"), false), "\"x\"");
152 assert_eq!(stringify_node(&buildize("x"), true), "x");
153 assert_eq!(stringify_node(&buildize(1.5), false), "1.5");
154 assert_eq!(stringify_node(&buildize(true), false), "true");
155 assert_eq!(stringify_node(&buildize(Token::Integer), false), "Integer");
156 assert_eq!(stringify_node(&buildize(Token::BigInt), false), "BigInt");
157 assert_eq!(stringify_node(&buildize(Token::Date), false), "Date");
158 assert_eq!(
159 stringify_node(&buildize(Spec::Value(Value::Date(0))), false),
160 "\"1970-01-01T00:00:00.000Z\""
161 );
162 assert_eq!(
163 stringify_node(&buildize(Spec::Value(Value::Date(0))), true),
164 "1970-01-01T00:00:00.000Z"
165 );
166 assert_eq!(
167 stringify_node(&buildize(Spec::Value(Value::Null)), false),
168 "null"
169 );
170 assert_eq!(stringify_node(&buildize(f64::NAN), false), "NaN");
171 assert_eq!(stringify_node(&any(), false), "Any");
172 assert_eq!(stringify_node(&min(2, any()), false), "Min(2)");
173 assert_eq!(
174 stringify_node(&min(2, Token::Number), false),
175 "Number.Min(2)"
176 );
177 let mut anyd = any();
178 anyd.has_default = true;
179 anyd.default = Value::from(3);
180 assert_eq!(stringify_node(&anyd, false), "Any(3)");
181 assert_eq!(stringify_node(&never(1), false), "Never");
182 assert_eq!(
183 stringify_node(&buildize(Regex::new("^a").unwrap()), false),
184 "/^a/"
185 );
186 let mut re = buildize(Token::String);
187 re.kind = Kind::Regexp;
188 assert_eq!(stringify_node(&re, false), "Regexp");
189 assert_eq!(
190 stringify_node(&check(|_, _| true, any()), false),
191 "Check.Check()"
192 );
193 assert_eq!(
194 stringify_node(&buildize(Token::Function), false),
195 "Function"
196 );
197 assert_eq!(
198 stringify_node(&one([Spec::from(Token::Number), Spec::from("a")]), false),
199 "One(Number,a)"
200 );
201 assert_eq!(
202 stringify_node(&some([Spec::from(Token::Number)]), false),
203 "Some(Number)"
204 );
205 assert_eq!(
206 stringify_node(&all([Spec::from(Token::Number)]), false),
207 "All(Number)"
208 );
209 assert_eq!(stringify_node(&buildize(Token::Array), false), "[]");
210 assert_eq!(
211 stringify_node(
212 &buildize(arr([Spec::from(1), Spec::from(Token::String)])),
213 false
214 ),
215 "[1, String]"
216 );
217 assert_eq!(
218 stringify_node(&rest(Token::Number, arr::<Spec>([])), false),
219 "[...Number]"
220 );
221 assert_eq!(
222 stringify_node(
223 &buildize(obj([
224 ("b", Spec::from(1)),
225 ("a", Spec::from(Token::String))
226 ])),
227 false
228 ),
229 "{b: 1, a: String}"
230 );
231 assert_eq!(stringify_node(&buildize(Token::Object), false), "{}.Open()");
232 assert_eq!(
233 stringify_node(&child(Token::Number, Token::Object), false),
234 "{}.Child(Number)"
235 );
236 assert_eq!(inline_value(&Value::Undefined), "undefined");
237 }
238}