playdate_bindgen/gen/docs/
parser.rs1use std::io::{Error as IoError, ErrorKind};
11use std::borrow::BorrowMut;
12use markup5ever_rcdom::NodeData;
13use markup5ever_rcdom::RcDom;
14
15use html5ever::parse_document;
16use html5ever::tendril::TendrilSink;
17use html5ever::tree_builder::TreeSink;
18use markup5ever_rcdom::Handle;
19use markup5ever_rcdom::SerializableHandle;
20use utils::toolchain::sdk::Sdk;
21
22use crate::Result;
23use super::DocsMap;
24
25
26pub fn parse(sdk: &Sdk) -> Result<DocsMap> {
27 let path = sdk.path().join("Inside Playdate with C.html");
28 parse_file(&path).map_err(Into::into)
29}
30
31
32pub fn parse_file(path: &std::path::Path) -> Result<DocsMap, IoError> {
33 if !path.try_exists()? {
34 return Err(IoError::new(ErrorKind::NotFound, path.display().to_string()));
35 }
36
37 let mut results = DocsMap::new();
38 let dom = parse_document(RcDom::default(), Default::default()).from_utf8()
39 .from_file(path)?
40 .finish();
41 if !dom.errors.is_empty() {
42 eprintln!("errors: {:#?}", dom.errors);
43 }
44
45 walk(&dom.document, &mut results);
46
47 #[cfg(feature = "log")]
48 for (k, _) in &results {
49 println!("Doc found for {k}");
50 }
51
52 Ok(results)
53}
54
55
56fn walk(handle: &Handle, results: &mut DocsMap) {
58 let node = handle;
59 let mut found = None;
60 if let NodeData::Element { ref name, ref attrs, .. } = node.data {
61 found = if name.local == *"div" {
62 let attrs = attrs.borrow();
63 let attr = attrs.iter()
64 .find(|attr| attr.name.local == *"id" && attr.value.starts_with("f-"));
65 attr.map(|attr| {
66 attr.value
67 .strip_prefix("f-")
68 .expect("prefix 'f-' must be there")
69 .to_string()
70 })
71 } else {
72 None
73 };
74
75 if let Some(_key) = found.as_ref() {
76 let mut children = node.children.borrow_mut();
80 let title = children.iter_mut().find(|child| {
81 match &child.data {
82 NodeData::Element { name, attrs, .. } => {
83 name.local.eq("div") &&
84 attrs.borrow().iter().any(|attr| {
85 attr.name.local.eq("class") &&
86 attr.value.contains("title")
87 })
88 },
89 _ => false,
90 }
91 });
92 if let Some(title) = title {
93 let mut data = {
94 match &title.data {
95 NodeData::Element { name,
96 attrs,
97 template_contents,
98 mathml_annotation_xml_integration_point, } => {
99 let mut code = name.clone();
100 code.borrow_mut().local = html5ever::LocalName::from("code");
101 NodeData::Element { name: code,
102 attrs: attrs.clone(),
103 template_contents: template_contents.clone(),
104 mathml_annotation_xml_integration_point:
105 *mathml_annotation_xml_integration_point }.into()
106 },
107 _ => None,
108 }
109 };
110
111 if let Some(data) = data.take() {
112 unsafe {
113 std::rc::Rc::get_mut_unchecked(title).data = data;
114 }
115 }
116 }
117 }
118 }
119
120 for child in
121 node.children
122 .borrow()
123 .iter()
124 .filter(|child| matches!(child.data, NodeData::Text { .. } | NodeData::Element { .. }))
125 {
126 walk(child, results);
127 }
128
129 if let Some(key) = found {
130 let document: SerializableHandle = node.clone().into();
131 let mut render = Vec::new();
132 html5ever::serialize(&mut render, &document, Default::default()).expect("serialization failed");
133 let html = std::str::from_utf8(&render).unwrap();
134 let mut node = html2md::parser::safe_parse_html(html.to_owned()).expect("parsing failed");
135 if let Some(node) = node.children.first_mut() {
136 if node.tag_name == Some(html2md::structs::NodeType::Code) &&
137 node.attributes
138 .as_ref()
139 .filter(|a| a.get_class().map(String::as_str) == Some("title"))
140 .is_some()
141 {
142 node.children.clear();
143 }
144 }
145
146 let mut md = html2md::to_md::to_md(node);
147 md = md.strip_prefix("```\n```\n")
148 .map(ToString::to_string)
149 .unwrap_or(md);
150 if !md.trim().is_empty() {
151 results.insert(key, md);
152 }
153 }
154}