typst_batch/codegen/
serialize.rs1use serde_json::{Map, Value as JsonValue};
4use typst::foundations::{Content, Value};
5
6pub fn content_to_json(content: &Content) -> JsonValue {
10 let json = serde_json::to_value(content).unwrap_or(JsonValue::Null);
11 strip_nulls(json)
12}
13
14pub fn value_to_json(value: &Value) -> JsonValue {
18 let json = serde_json::to_value(value).unwrap_or(JsonValue::Null);
19 strip_nulls(json)
20}
21
22fn strip_nulls(json: JsonValue) -> JsonValue {
24 match json {
25 JsonValue::Object(obj) => {
26 let filtered: Map<String, JsonValue> = obj
27 .into_iter()
28 .filter(|(_, v)| !v.is_null())
29 .map(|(k, v)| (k, strip_nulls(v)))
30 .collect();
31 JsonValue::Object(filtered)
32 }
33 JsonValue::Array(arr) => JsonValue::Array(arr.into_iter().map(strip_nulls).collect()),
34 other => other,
35 }
36}
37
38pub fn json_to_simple_text(json: &JsonValue) -> JsonValue {
46 match json {
47 JsonValue::Object(obj) => {
48 if obj.contains_key("func") {
49 JsonValue::String(extract_content_text(json))
51 } else {
52 let simplified: Map<String, JsonValue> = obj
54 .iter()
55 .map(|(k, v)| (k.clone(), json_to_simple_text(v)))
56 .collect();
57 JsonValue::Object(simplified)
58 }
59 }
60 JsonValue::Array(arr) => {
61 JsonValue::Array(arr.iter().map(json_to_simple_text).collect())
62 }
63 other => other.clone(),
64 }
65}
66
67fn extract_content_text(json: &JsonValue) -> String {
69 match json {
70 JsonValue::Object(obj) => {
71 if let Some(text) = obj.get("text").and_then(|v| v.as_str()) {
73 return text.to_string();
74 }
75 if let Some(body) = obj.get("body") {
77 return extract_content_text(body);
78 }
79 if let Some(children) = obj.get("children").and_then(|v| v.as_array()) {
81 return children.iter().map(extract_content_text).collect();
82 }
83 if let Some(child) = obj.get("child") {
85 return extract_content_text(child);
86 }
87 String::new()
88 }
89 JsonValue::Array(arr) => arr.iter().map(extract_content_text).collect(),
90 JsonValue::String(s) => s.clone(),
91 _ => String::new(),
92 }
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98 use serde_json::json;
99
100 #[test]
101 fn test_simplify_text_content() {
102 let json = json!({"func": "text", "text": "Hello World"});
103 assert_eq!(json_to_simple_text(&json), json!("Hello World"));
104 }
105
106 #[test]
107 fn test_simplify_link_content() {
108 let json = json!({
109 "func": "link",
110 "dest": "/posts/hello",
111 "body": {"func": "text", "text": "Click here"}
112 });
113 assert_eq!(json_to_simple_text(&json), json!("Click here"));
114 }
115
116 #[test]
117 fn test_simplify_sequence_content() {
118 let json = json!({
119 "func": "sequence",
120 "children": [
121 {"func": "text", "text": "Hello "},
122 {"func": "strong", "body": {"func": "text", "text": "World"}}
123 ]
124 });
125 assert_eq!(json_to_simple_text(&json), json!("Hello World"));
126 }
127
128 #[test]
129 fn test_simplify_nested_dict() {
130 let json = json!({
131 "title": "My Post",
132 "summary": {"func": "text", "text": "A summary"},
133 "next": {
134 "func": "link",
135 "dest": "/next",
136 "body": {"func": "text", "text": "Next Post"}
137 }
138 });
139 let result = json_to_simple_text(&json);
140 assert_eq!(result["title"], "My Post");
141 assert_eq!(result["summary"], "A summary");
142 assert_eq!(result["next"], "Next Post");
143 }
144
145 #[test]
146 fn test_simplify_array_with_content() {
147 let json = json!([
148 {"title": "A", "summary": {"func": "text", "text": "Summary A"}},
149 {"title": "B", "summary": {"func": "text", "text": "Summary B"}}
150 ]);
151 let result = json_to_simple_text(&json);
152 assert_eq!(result[0]["summary"], "Summary A");
153 assert_eq!(result[1]["summary"], "Summary B");
154 }
155}