1use serde_json::{Map, Value as JsonValue};
4use typst::comemo::Tracked;
5use typst::ecow::EcoVec;
6use typst::engine::Engine;
7use typst::foundations::{Arg, Args, CastInfo, Content, Context, Dict, Func, Str, Value};
8use typst::foundations::SymbolElem;
9use typst::syntax::{Span, Spanned};
10use typst::text::{SpaceElem, TextElem};
11use typst::Library;
12
13use super::error::ConvertError;
14use super::literal::{parse_typst_literal, parse_length, parse_angle, parse_ratio, parse_color};
15use super::lookup::{find_element_funcs, find_element_in_scope};
16
17pub fn json_to_content(
31 engine: &mut Engine,
32 context: Tracked<Context>,
33 library: &Library,
34 json: &JsonValue,
35) -> Result<Content, ConvertError> {
36 json_to_content_with_ancestors(engine, context, library, json, &[])
37}
38
39fn json_to_content_with_ancestors(
45 engine: &mut Engine,
46 context: Tracked<Context>,
47 library: &Library,
48 json: &JsonValue,
49 ancestors: &[Func],
50) -> Result<Content, ConvertError> {
51 let obj = json.as_object().ok_or(ConvertError::NotObject(json_type_name(json)))?;
52 let func_name = obj
53 .get("func")
54 .and_then(|v| v.as_str())
55 .ok_or(ConvertError::MissingFunc)?;
56
57 match func_name {
59 "text" => {
60 let text = obj
61 .get("text")
62 .and_then(|v| v.as_str())
63 .ok_or(ConvertError::MissingField("text"))?;
64 return Ok(TextElem::packed(text));
65 }
66 "space" => {
67 return Ok(SpaceElem::shared().clone());
68 }
69 "symbol" => {
70 let text = obj
72 .get("text")
73 .and_then(|v| v.as_str())
74 .ok_or(ConvertError::MissingField("text"))?;
75 return Ok(SymbolElem::packed(text.chars().next().unwrap_or('?')));
76 }
77 "sequence" => {
78 let children = obj
79 .get("children")
80 .and_then(|v| v.as_array())
81 .ok_or(ConvertError::MissingField("children"))?;
82 let contents: Result<Vec<Content>, _> = children
83 .iter()
84 .map(|c| json_to_content_with_ancestors(engine, context, library, c, ancestors))
85 .collect();
86 return Ok(Content::sequence(contents?));
87 }
88 "styled" => {
89 let child = obj
92 .get("child")
93 .ok_or(ConvertError::MissingField("child"))?;
94 return json_to_content_with_ancestors(engine, context, library, child, ancestors);
95 }
96 _ => {}
97 }
98
99 let func = ancestors
101 .iter()
102 .rev()
103 .find_map(|ancestor| find_element_in_scope(ancestor, func_name))
104 .or_else(|| find_best_matching_element(library, func_name, obj))
106 .ok_or_else(|| ConvertError::UnknownElement(func_name.to_string()))?;
107
108 let args = build_args(&func, obj, engine, context, library, ancestors)?;
109
110 func.call(engine, context, args)
111 .map_err(|e| ConvertError::CallFailed {
112 func: func_name.to_string(),
113 reason: e.iter().map(|d| d.message.to_string()).collect::<Vec<_>>().join("; "),
114 })?
115 .cast::<Content>()
116 .map_err(|_| ConvertError::CallFailed {
117 func: func_name.to_string(),
118 reason: "result is not Content".to_string(),
119 })
120}
121
122pub fn json_to_value(
129 engine: &mut Engine,
130 context: Tracked<Context>,
131 library: &Library,
132 json: &JsonValue,
133) -> Result<Value, ConvertError> {
134 json_to_value_with_ancestors(engine, context, library, json, &[])
135}
136
137fn json_to_value_with_ancestors(
151 engine: &mut Engine,
152 context: Tracked<Context>,
153 library: &Library,
154 json: &JsonValue,
155 ancestors: &[Func],
156) -> Result<Value, ConvertError> {
157 match json {
158 JsonValue::Null => Ok(Value::None),
159 JsonValue::Bool(b) => Ok(Value::Bool(*b)),
160 JsonValue::Number(n) => {
161 if let Some(i) = n.as_i64() {
162 Ok(Value::Int(i))
163 } else {
164 Ok(Value::Float(n.as_f64().ok_or(ConvertError::ValueConversion)?))
165 }
166 }
167 JsonValue::String(s) => {
168 if let Some(value) = parse_typst_literal(s) {
170 return Ok(value);
171 }
172 Ok(Value::Str(s.as_str().into()))
173 }
174 JsonValue::Array(arr) => {
175 let items: Result<Vec<Value>, _> = arr
176 .iter()
177 .map(|v| json_to_value_with_ancestors(engine, context, library, v, ancestors))
178 .collect();
179 Ok(Value::Array(items?.into_iter().collect()))
180 }
181 JsonValue::Object(obj) => {
182 if let Some(type_tag) = obj.get("_typst_type").and_then(|v| v.as_str()) {
184 return parse_typed_value(type_tag, obj);
185 }
186
187 if obj.contains_key("func") {
189 let content = json_to_content_with_ancestors(engine, context, library, json, ancestors)?;
190 Ok(Value::Content(content))
191 } else {
192 let dict: Result<Dict, _> = obj
194 .iter()
195 .map(|(k, v)| {
196 let value = json_to_value_with_ancestors(engine, context, library, v, ancestors)?;
197 Ok((Str::from(k.as_str()), value))
198 })
199 .collect();
200 Ok(Value::Dict(dict?))
201 }
202 }
203 }
204}
205
206fn parse_typed_value(type_tag: &str, obj: &Map<String, JsonValue>) -> Result<Value, ConvertError> {
210 let value_str = obj
211 .get("value")
212 .and_then(|v| v.as_str())
213 .ok_or(ConvertError::MissingField("value"))?;
214
215 match type_tag {
216 "length" => parse_length(value_str)
217 .map(Value::Length)
218 .ok_or_else(|| ConvertError::InvalidLiteral {
219 type_name: "length",
220 value: value_str.to_string(),
221 }),
222 "angle" => parse_angle(value_str)
223 .map(Value::Angle)
224 .ok_or_else(|| ConvertError::InvalidLiteral {
225 type_name: "angle",
226 value: value_str.to_string(),
227 }),
228 "ratio" => parse_ratio(value_str)
229 .map(Value::Ratio)
230 .ok_or_else(|| ConvertError::InvalidLiteral {
231 type_name: "ratio",
232 value: value_str.to_string(),
233 }),
234 "color" => parse_color(value_str)
235 .map(Value::Color)
236 .ok_or_else(|| ConvertError::InvalidLiteral {
237 type_name: "color",
238 value: value_str.to_string(),
239 }),
240 "str" | "string" => {
241 Ok(Value::Str(value_str.into()))
243 }
244 _ => Err(ConvertError::UnknownTypeTag(type_tag.to_string())),
245 }
246}
247
248fn build_args(
259 func: &Func,
260 obj: &Map<String, JsonValue>,
261 engine: &mut Engine,
262 context: Tracked<Context>,
263 library: &Library,
264 ancestors: &[Func],
265) -> Result<Args, ConvertError> {
266 let span = Span::detached();
267 let mut items: EcoVec<Arg> = EcoVec::new();
268
269 let params = func.params().ok_or(ConvertError::ValueConversion)?;
270
271 let mut new_ancestors = ancestors.to_vec();
273 new_ancestors.push(func.clone());
274
275 let positional_only: Vec<_> = params
277 .iter()
278 .filter(|p| p.positional && !p.named)
279 .collect();
280
281 for param in &positional_only {
283 if let Some(value) = obj.get(param.name) {
284 if param.variadic {
285 if let Some(arr) = value.as_array() {
287 for item in arr {
288 let typst_value = json_to_value_with_ancestors(
289 engine, context, library, item, &new_ancestors,
290 )?;
291 items.push(Arg {
292 span,
293 name: None,
294 value: Spanned::new(typst_value, span),
295 });
296 }
297 } else {
298 let typst_value = json_to_value_with_ancestors(
299 engine, context, library, value, &new_ancestors,
300 )?;
301 items.push(Arg {
302 span,
303 name: None,
304 value: Spanned::new(typst_value, span),
305 });
306 }
307 } else {
308 let typst_value = json_to_value_with_ancestors(
309 engine, context, library, value, &new_ancestors,
310 )?;
311 items.push(Arg {
312 span,
313 name: None,
314 value: Spanned::new(typst_value, span),
315 });
316 }
317 } else if !param.required && param_accepts_none(param) {
318 items.push(Arg {
321 span,
322 name: None,
323 value: Spanned::new(Value::None, span),
324 });
325 }
326 }
329
330 let positional_only_names: std::collections::HashSet<_> =
332 positional_only.iter().map(|p| p.name).collect();
333
334 for (key, value) in obj.iter() {
336 if key == "func" || positional_only_names.contains(key.as_str()) {
337 continue;
338 }
339
340 let param = params.iter().find(|p| p.name == key);
342 if let Some(param) = param
343 && param.variadic {
344 if let Some(arr) = value.as_array() {
346 for item in arr {
347 let typst_value = json_to_value_with_ancestors(
348 engine, context, library, item, &new_ancestors,
349 )?;
350 items.push(Arg {
351 span,
352 name: None,
353 value: Spanned::new(typst_value, span),
354 });
355 }
356 continue;
357 }
358 }
359
360 let typst_value = json_to_value_with_ancestors(engine, context, library, value, &new_ancestors)?;
362 items.push(Arg {
363 span,
364 name: Some(Str::from(key.as_str())),
365 value: Spanned::new(typst_value, span),
366 });
367 }
368
369 Ok(Args { span, items })
370}
371
372fn param_accepts_none(param: &typst::foundations::ParamInfo) -> bool {
374 let mut accepts_none = false;
375 param.input.walk(|info| {
376 match info {
377 CastInfo::Any => accepts_none = true,
378 CastInfo::Type(ty) if ty.short_name() == "none" => accepts_none = true,
379 _ => {}
380 }
381 });
382 accepts_none
383}
384
385fn json_type_name(json: &JsonValue) -> &'static str {
386 match json {
387 JsonValue::Null => "null",
388 JsonValue::Bool(_) => "bool",
389 JsonValue::Number(_) => "number",
390 JsonValue::String(_) => "string",
391 JsonValue::Array(_) => "array",
392 JsonValue::Object(_) => "object",
393 }
394}
395
396fn find_best_matching_element(
401 library: &Library,
402 func_name: &str,
403 obj: &Map<String, JsonValue>,
404) -> Option<Func> {
405 let candidates: Vec<_> = find_element_funcs(library, func_name).collect();
406
407 if candidates.len() <= 1 {
408 return candidates.into_iter().next();
409 }
410
411 let json_fields: std::collections::HashSet<_> = obj.keys().filter(|k| *k != "func").collect();
413
414 candidates
415 .into_iter()
416 .max_by_key(|func| {
417 let params = func.params().unwrap_or_default();
418 let param_names: std::collections::HashSet<_> = params.iter().map(|p| p.name).collect();
419
420 let matches = json_fields
422 .iter()
423 .filter(|f| param_names.contains(f.as_str()))
424 .count();
425
426 let missing_required = params
428 .iter()
429 .filter(|p| p.required && !json_fields.contains(&p.name.to_string()))
430 .count();
431
432 (matches as i32) - (missing_required as i32)
434 })
435}