List variants for enums

This patch updates parse_item_doc to look for variants.
This commit is contained in:
Robin Krahl 2020-07-22 19:07:12 +02:00
parent 958019449d
commit 8bfb7393e5
No known key found for this signature in database
GPG Key ID: 8E9B0870524F69D8
4 changed files with 114 additions and 1 deletions

1
Cargo.lock generated
View File

@ -477,6 +477,7 @@ dependencies = [
"anyhow 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", "anyhow 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)",
"html2text 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", "html2text 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
"kuchiki 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "kuchiki 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"markup5ever 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pager 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", "pager 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@ -16,6 +16,7 @@ license = "MIT"
anyhow = "1.0.31" anyhow = "1.0.31"
html2text = "0.1.12" html2text = "0.1.12"
kuchiki = "0.8.0" kuchiki = "0.8.0"
markup5ever = "0.10.0"
pager = "0.15.0" pager = "0.15.0"
serde_json = "1.0.56" serde_json = "1.0.56"
serde_repr = "0.1.6" serde_repr = "0.1.6"

View File

@ -245,6 +245,37 @@ impl ItemType {
} }
} }
pub fn class(&self) -> &str {
match self {
ItemType::Module => "module",
ItemType::ExternCrate => "extern-crate",
ItemType::Import => "import",
ItemType::Struct => "struct",
ItemType::Enum => "enum",
ItemType::Function => "function",
ItemType::Typedef => "typedef",
ItemType::Static => "static",
ItemType::Trait => "trait",
ItemType::Impl => "impl",
ItemType::TyMethod => "required-method",
ItemType::Method => "method",
ItemType::StructField => "field",
ItemType::Variant => "variant",
ItemType::Macro => "macro",
ItemType::Primitive => "primitive",
ItemType::AssocType => "associated-type",
ItemType::Constant => "constant",
ItemType::AssocConst => "associated-const",
ItemType::Union => "union",
ItemType::ForeignType => "foreign-type",
ItemType::Keyword => "keyword",
ItemType::OpaqueTy => "opaque-type",
ItemType::ProcAttribute => "proc-attribute",
ItemType::ProcDerive => "proc-derive",
ItemType::TraitAlias => "trait-alias",
}
}
pub fn group_name(&self) -> &str { pub fn group_name(&self) -> &str {
match self { match self {
ItemType::Module => "Modules", ItemType::Module => "Modules",

View File

@ -47,7 +47,7 @@ pub fn find_member<P: AsRef<path::Path>>(
.as_node() .as_node()
.parent() .parent()
.context("Member element does not have a parent")?; .context("Member element does not have a parent")?;
if let Some(parent_id) = get_attribute(parent.as_element().unwrap(), "id") { if let Some(parent_id) = get_node_attribute(&parent, "id") {
let item_type: doc::ItemType = parent_id.splitn(2, '.').next().unwrap().parse()?; let item_type: doc::ItemType = parent_id.splitn(2, '.').next().unwrap().parse()?;
return Ok(Some(doc::Item::new( return Ok(Some(doc::Item::new(
name.clone(), name.clone(),
@ -84,6 +84,12 @@ pub fn parse_item_doc(item: &doc::Item) -> anyhow::Result<doc::Doc> {
let mut doc = doc::Doc::new(item.name.clone(), item.ty); let mut doc = doc::Doc::new(item.name.clone(), item.ty);
doc.description = description.map(|n| get_html(n.as_node())).transpose()?; doc.description = description.map(|n| get_html(n.as_node())).transpose()?;
doc.definition = definition.map(|n| get_html(n.as_node())).transpose()?; doc.definition = definition.map(|n| get_html(n.as_node())).transpose()?;
let (ty, groups) = get_variants(&document, item)?;
if !groups.is_empty() {
doc.groups.push((ty, groups));
}
Ok(doc) Ok(doc)
} }
@ -135,6 +141,52 @@ pub fn parse_member_doc(item: &doc::Item) -> anyhow::Result<doc::Doc> {
Ok(doc) Ok(doc)
} }
fn get_variants(
document: &kuchiki::NodeRef,
parent: &doc::Item,
) -> anyhow::Result<(doc::ItemType, Vec<doc::MemberGroup>)> {
let ty = doc::ItemType::Variant;
let mut variants: Vec<doc::Doc> = Vec::new();
let heading = select_first(document, &format!("#{}", ty.group_id()))?;
let mut next = heading.and_then(|n| next_sibling_element(n.as_node()));
let mut name: Option<String> = None;
while let Some(element) = &next {
if is_element(element, markup5ever::local_name!("div")) {
if has_class(element, ty.class()) {
if let Some(name) = &name {
variants.push(doc::Doc::new(parent.name.child(name), ty));
}
name = get_node_attribute(element, "id")
.and_then(|s| s.splitn(2, '.').nth(1).map(ToOwned::to_owned));
} else if has_class(element, "docblock") {
if let Some(name) = &name {
let mut doc = doc::Doc::new(parent.name.child(name), ty);
// TODO: use inner_html() instead
doc.description = Some(element.text_contents());
variants.push(doc);
}
name = None;
}
next = element.next_sibling();
} else {
if let Some(name) = &name {
variants.push(doc::Doc::new(parent.name.child(name), ty));
}
next = None;
}
}
let mut groups: Vec<doc::MemberGroup> = Vec::new();
if !variants.is_empty() {
let mut group = doc::MemberGroup::new();
group.members = variants;
groups.push(group);
}
Ok((ty, groups))
}
fn get_members( fn get_members(
document: &kuchiki::NodeRef, document: &kuchiki::NodeRef,
parent: &doc::Item, parent: &doc::Item,
@ -168,6 +220,34 @@ fn get_attribute(element: &kuchiki::ElementData, name: &str) -> Option<String> {
element.attributes.borrow().get(name).map(ToOwned::to_owned) element.attributes.borrow().get(name).map(ToOwned::to_owned)
} }
fn get_node_attribute(node: &kuchiki::NodeRef, name: &str) -> Option<String> {
node.as_element().and_then(|e| get_attribute(e, name))
}
fn next_sibling_element(node: &kuchiki::NodeRef) -> Option<kuchiki::NodeRef> {
let mut next = node.next_sibling();
while let Some(node) = &next {
if node.as_element().is_some() {
break;
}
next = node.next_sibling();
}
next
}
fn is_element(node: &kuchiki::NodeRef, name: markup5ever::LocalName) -> bool {
node.as_element()
.map(|e| e.name.local == name)
.unwrap_or(false)
}
fn has_class(node: &kuchiki::NodeRef, class: &str) -> bool {
node.as_element()
.and_then(|e| get_attribute(&e, "class"))
.map(|a| a.split(' ').any(|s| s == class))
.unwrap_or(false)
}
fn get_html(node: &kuchiki::NodeRef) -> anyhow::Result<String> { fn get_html(node: &kuchiki::NodeRef) -> anyhow::Result<String> {
let mut vec: Vec<u8> = Vec::new(); let mut vec: Vec<u8> = Vec::new();
node.serialize(&mut vec)?; node.serialize(&mut vec)?;