playdate_bindgen/gen/docs/
parser.rs1use std::io::{Error as IoError, ErrorKind};
11use std::borrow::BorrowMut;
12use std::collections::HashMap;
13use html2md::NodeData;
14use html2md::RcDom;
15use html2md::StructuredPrinter;
16use html2md::TagHandler;
17use html5ever::parse_document;
18use html5ever::tendril::TendrilSink;
19use html5ever::tree_builder::TreeSink;
20use markup5ever_rcdom::Handle;
21use markup5ever_rcdom::SerializableHandle;
22use utils::toolchain::sdk::Sdk;
23
24use crate::Result;
25use super::DocsMap;
26
27
28pub fn parse(sdk: &Sdk) -> Result<DocsMap> {
29 let path = sdk.path().join("Inside Playdate with C.html");
30 parse_file(&path).map_err(Into::into)
31}
32
33
34pub fn parse_file(path: &std::path::Path) -> Result<DocsMap, IoError> {
35 if !path.try_exists()? {
36 return Err(IoError::new(ErrorKind::NotFound, path.display().to_string()));
37 }
38
39 let mut results = DocsMap::new();
40 let dom = parse_document(RcDom::default(), Default::default()).from_utf8()
41 .from_file(path)?
42 .finish();
43 if !dom.errors.is_empty() {
44 eprintln!("errors: {:#?}", dom.errors);
45 }
46
47 walk(&dom.document, &mut results);
48
49 #[cfg(feature = "log")]
50 for (k, _) in &results {
51 println!("Doc found for {k}");
52 }
53
54 Ok(results)
55}
56
57
58fn walk(handle: &Handle, results: &mut DocsMap) {
60 let node = handle;
61 let mut found = None;
62 match node.data {
63 NodeData::Element { ref name, ref attrs, .. } => {
64 found = if name.local == *"div" {
65 let attrs = attrs.borrow();
66 let attr = attrs.iter()
67 .find(|attr| attr.name.local == *"id" && attr.value.starts_with("f-"));
68 attr.map(|attr| {
69 attr.value
70 .strip_prefix("f-")
71 .expect("prefix 'f-' must be there")
72 .to_string()
73 })
74 } else {
75 None
76 };
77
78 if let Some(_key) = found.as_ref() {
79 let mut children = node.children.borrow_mut();
83 let title = children.iter_mut().find(|child| {
84 match &child.data {
85 NodeData::Element { name, attrs, .. } => {
86 name.local == *"div" &&
87 attrs.borrow()
88 .iter()
89 .find(|attr| {
90 attr.name.local == *"class" &&
91 attr.value.contains("title")
92 })
93 .is_some()
94 },
95 _ => false,
96 }
97 });
98 if let Some(title) = title {
99 let mut data = {
100 match &title.data {
101 NodeData::Element { name,
102 attrs,
103 template_contents,
104 mathml_annotation_xml_integration_point, } => {
105 let mut code = name.clone();
106 code.borrow_mut().local = html5ever::ATOM_LOCALNAME__63_6F_64_65;
107 NodeData::Element { name: code,
108 attrs: attrs.clone(),
109 template_contents: template_contents.clone(),
110 mathml_annotation_xml_integration_point:
111 *mathml_annotation_xml_integration_point }.into()
112 },
113 _ => None,
114 }
115 };
116
117 if let Some(data) = data.take() {
118 unsafe {
119 std::rc::Rc::get_mut_unchecked(title).data = data;
120 }
121 }
122 }
123 }
124 },
125
126 _ => {},
127 }
128
129 for child in node.children.borrow().iter().filter(|child| {
130 match child.data {
131 NodeData::Text { .. } | NodeData::Element { .. } => true,
132 _ => false,
133 }
134 })
135 {
136 walk(child, results);
137 }
138
139 if let Some(key) = found {
140 let document: SerializableHandle = node.clone().into();
141 let mut render = Vec::new();
142 html5ever::serialize(&mut render, &document, Default::default()).ok()
143 .expect("serialization failed");
144 let html = std::str::from_utf8(&render).unwrap();
145
146 use html2md::TagHandlerFactory;
147 struct PreAsIsTagFactory;
148 impl TagHandlerFactory for PreAsIsTagFactory {
149 fn instantiate(&self) -> Box<dyn TagHandler> {
150 Box::new(CodeHandler { lang: "cpp",
151 ..Default::default() })
152 }
153 }
154 let mut tag_factory: HashMap<String, Box<dyn TagHandlerFactory>> = HashMap::new();
156 tag_factory.insert(String::from("pre"), Box::new(PreAsIsTagFactory));
157 let md = html2md::parse_html_custom(html, &tag_factory);
158
159 results.insert(key, md);
160 }
161}
162
163
164#[derive(Default)]
165pub struct CodeHandler {
168 lang: &'static str,
170 code_type: String,
171}
172
173impl CodeHandler {
174 fn do_handle(&mut self, printer: &mut StructuredPrinter, start: bool) {
176 let immediate_parent = printer.parent_chain.last().unwrap().to_owned();
177 if self.code_type == "code" && immediate_parent == "pre" {
178 return;
180 }
181
182 match self.code_type.as_ref() {
183 "pre" => {
184 if start {
186 printer.insert_newline();
187 printer.append_str(&format!("\n```{}\n", self.lang));
188 }
189 if !start {
191 printer.append_str("\n```\n");
192 printer.insert_newline();
193 }
194 },
195 "code" | "samp" => printer.append_str("`"),
196 _ => {},
197 }
198 }
199}
200
201impl TagHandler for CodeHandler {
202 fn handle(&mut self, tag: &Handle, printer: &mut StructuredPrinter) {
203 self.code_type = match tag.data {
204 NodeData::Element { ref name, .. } => name.local.to_string(),
205 _ => String::new(),
206 };
207
208 self.do_handle(printer, true);
209 }
210 fn after_handle(&mut self, printer: &mut StructuredPrinter) { self.do_handle(printer, false); }
211}