1use crate::examples::Examples;
41use crate::render::{capability_short, direction_short, wit_definition};
42use crate::{ApiFunctionKind, ApiTypeKind, PluginApiCatalog};
43
44pub fn to_json(cat: &PluginApiCatalog, examples: Option<&Examples>) -> String {
47 let interfaces = cat
48 .interfaces
49 .iter()
50 .map(|i| {
51 let examples_for = |item: Option<&str>| {
52 let target = match item {
53 Some(item) => format!("{}.{item}", i.name),
54 None => i.name.clone(),
55 };
56 Json::Arr(
57 examples
58 .map(|ex| {
59 ex.for_target(&target)
60 .into_iter()
61 .map(|e| {
62 Json::obj([
63 ("id", s(&e.id)),
64 ("caption", s(&e.caption)),
65 ("source", s(&e.source)),
66 ("language", s("rust")),
67 ("code", s(&e.code)),
68 ])
69 })
70 .collect()
71 })
72 .unwrap_or_default(),
73 )
74 };
75 let functions = i
76 .functions
77 .iter()
78 .map(|f| function_json(f, examples_for(Some(&f.display_name()))))
79 .collect();
80 let types = i
81 .types
82 .iter()
83 .map(|t| {
84 let members = t
85 .kind
86 .members()
87 .iter()
88 .map(|m| {
89 Json::obj([
90 ("name", s(&m.name)),
91 ("type", opt(m.ty.as_deref())),
92 ("doc", opt(m.doc.as_deref())),
93 ])
94 })
95 .collect();
96 let kind = match t.kind {
97 ApiTypeKind::Alias(_) => "alias",
98 _ => t.kind.keyword(),
99 };
100 Json::obj([
101 ("name", s(&t.name)),
102 ("kind", s(kind)),
103 ("doc", opt(t.doc.as_deref())),
104 ("definition", s(&wit_definition(t))),
105 ("members", Json::Arr(members)),
106 ("examples", examples_for(Some(&t.name))),
107 ])
108 })
109 .collect();
110 let uses = i
111 .uses
112 .iter()
113 .map(|u| {
114 Json::obj([
115 ("name", s(&u.name)),
116 ("from", s(&u.from)),
117 ("original", s(&u.original)),
118 ])
119 })
120 .collect();
121 Json::obj([
122 ("name", s(&i.name)),
123 ("doc", opt(i.doc.as_deref())),
124 ("direction", s(direction_short(i.direction))),
125 ("capability", s(capability_json(i.capability))),
126 ("examples", examples_for(None)),
127 ("functions", Json::Arr(functions)),
128 ("types", Json::Arr(types)),
129 ("uses", Json::Arr(uses)),
130 ])
131 })
132 .collect();
133 let worlds = cat
134 .worlds
135 .iter()
136 .map(|w| {
137 Json::obj([
138 ("name", s(&w.name)),
139 ("doc", opt(w.doc.as_deref())),
140 (
141 "imports",
142 Json::Arr(w.imports.iter().map(|n| s(n)).collect()),
143 ),
144 (
145 "exports",
146 Json::Arr(w.exports.iter().map(|n| s(n)).collect()),
147 ),
148 (
149 "export_functions",
150 Json::Arr(
151 w.export_functions
152 .iter()
153 .map(|f| function_json(f, Json::Arr(Vec::new())))
154 .collect(),
155 ),
156 ),
157 (
158 "import_functions",
159 Json::Arr(
160 w.import_functions
161 .iter()
162 .map(|f| function_json(f, Json::Arr(Vec::new())))
163 .collect(),
164 ),
165 ),
166 ])
167 })
168 .collect();
169 let root = Json::obj([
170 ("package", s(crate::PACKAGE)),
171 ("interfaces", Json::Arr(interfaces)),
172 ("worlds", Json::Arr(worlds)),
173 ]);
174 let mut out = String::new();
175 root.write(&mut out, 0);
176 out.push('\n');
177 out
178}
179
180fn function_json(f: &crate::ApiFunction, examples: Json) -> Json {
182 let (kind, resource) = match &f.kind {
183 ApiFunctionKind::Freestanding => ("freestanding", None),
184 ApiFunctionKind::Method(r) => ("method", Some(r.as_str())),
185 ApiFunctionKind::Static(r) => ("static", Some(r.as_str())),
186 ApiFunctionKind::Constructor(r) => ("constructor", Some(r.as_str())),
187 };
188 let params = f
189 .params
190 .iter()
191 .map(|p| Json::obj([("name", s(&p.name)), ("type", s(&p.ty))]))
192 .collect();
193 Json::obj([
194 ("name", s(&f.name)),
195 ("display_name", s(&f.display_name())),
196 ("kind", s(kind)),
197 ("resource", opt(resource)),
198 ("async", Json::Bool(f.is_async)),
199 ("params", Json::Arr(params)),
200 ("result", opt(f.result.as_deref())),
201 ("signature", s(&f.signature())),
202 ("doc", opt(f.doc.as_deref())),
203 ("examples", examples),
204 ])
205}
206
207fn capability_json(c: crate::Capability) -> &'static str {
210 match capability_short(c) {
211 "-" => "none",
212 other => other,
213 }
214}
215
216enum Json {
218 Str(String),
219 Bool(bool),
220 Null,
221 Arr(Vec<Json>),
222 Obj(Vec<(&'static str, Json)>),
224}
225
226fn s(v: &str) -> Json {
227 Json::Str(v.to_string())
228}
229
230fn opt(v: Option<&str>) -> Json {
231 v.map(s).unwrap_or(Json::Null)
232}
233
234impl Json {
235 fn obj<const N: usize>(fields: [(&'static str, Json); N]) -> Json {
236 Json::Obj(fields.into_iter().collect())
237 }
238
239 fn write(&self, out: &mut String, depth: usize) {
240 let pad = |d: usize| " ".repeat(d);
241 match self {
242 Json::Str(v) => write_str(out, v),
243 Json::Bool(b) => out.push_str(if *b { "true" } else { "false" }),
244 Json::Null => out.push_str("null"),
245 Json::Arr(items) if items.is_empty() => out.push_str("[]"),
246 Json::Arr(items) => {
247 out.push_str("[\n");
248 for (n, item) in items.iter().enumerate() {
249 out.push_str(&pad(depth + 1));
250 item.write(out, depth + 1);
251 out.push_str(if n + 1 < items.len() { ",\n" } else { "\n" });
252 }
253 out.push_str(&pad(depth));
254 out.push(']');
255 }
256 Json::Obj(fields) => {
257 out.push_str("{\n");
258 for (n, (key, value)) in fields.iter().enumerate() {
259 out.push_str(&pad(depth + 1));
260 write_str(out, key);
261 out.push_str(": ");
262 value.write(out, depth + 1);
263 out.push_str(if n + 1 < fields.len() { ",\n" } else { "\n" });
264 }
265 out.push_str(&pad(depth));
266 out.push('}');
267 }
268 }
269 }
270}
271
272fn write_str(out: &mut String, v: &str) {
276 out.push('"');
277 for c in v.chars() {
278 match c {
279 '"' => out.push_str("\\\""),
280 '\\' => out.push_str("\\\\"),
281 '\n' => out.push_str("\\n"),
282 '\r' => out.push_str("\\r"),
283 '\t' => out.push_str("\\t"),
284 c if (c as u32) < 0x20 => out.push_str(&format!("\\u{:04x}", c as u32)),
285 c => out.push(c),
286 }
287 }
288 out.push('"');
289}
290
291#[cfg(test)]
292mod tests {
293 use super::*;
294
295 #[test]
296 fn strings_are_escaped_per_rfc_8259() {
297 let mut out = String::new();
298 write_str(&mut out, "a\"b\\c\nd\u{1}é");
299 assert_eq!(out, r#""a\"b\\c\nd\u0001é""#);
300 }
301
302 #[test]
303 fn nesting_is_indented_and_empty_arrays_are_inline() {
304 let mut out = String::new();
305 Json::obj([("k", Json::Arr(vec![])), ("v", Json::Null)]).write(&mut out, 0);
306 assert_eq!(out, "{\n \"k\": [],\n \"v\": null\n}");
307 }
308}