Import item types from librustdoc

Previously, our ItemType enum only had three values: Module, Item and
Member.  With this patch we import the ItemType variants from librustdoc
(see html/item_type.rs) for better compatibility and easier parsing.
This commit is contained in:
Robin Krahl 2020-07-22 16:21:27 +02:00
parent bb3b361d02
commit f799d860ca
No known key found for this signature in database
GPG Key ID: 8E9B0870524F69D8
5 changed files with 125 additions and 47 deletions

12
Cargo.lock generated
View File

@ -480,6 +480,7 @@ dependencies = [
"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_json 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_repr 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_tuple 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"structopt 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
"termion 1.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
@ -537,6 +538,16 @@ dependencies = [
"serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_repr"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_tuple"
version = "0.5.0"
@ -812,6 +823,7 @@ dependencies = [
"checksum serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)" = "5317f7588f0a5078ee60ef675ef96735a1442132dc645eb1d12c018620ed8cd3"
"checksum serde_derive 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)" = "2a0be94b04690fbaed37cddffc5c134bf537c8e3329d53e982fe04c374978f8e"
"checksum serde_json 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)" = "3433e879a558dde8b5e8feb2a04899cf34fdde1fafb894687e52105fc1162ac3"
"checksum serde_repr 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2dc6b7951b17b051f3210b063f12cc17320e2fe30ae05b0fe2a3abb068551c76"
"checksum serde_tuple 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f4f025b91216f15a2a32aa39669329a475733590a015835d1783549a56d09427"
"checksum serde_tuple_macros 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4076151d1a2b688e25aaf236997933c66e18b870d0369f8b248b8ab2be630d7e"
"checksum servo_arc 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432"

View File

@ -18,6 +18,7 @@ html2text = "0.1.12"
kuchiki = "0.8.0"
pager = "0.15.0"
serde_json = "1.0.56"
serde_repr = "0.1.6"
serde_tuple = "0.5.0"
termion = "1.5.5"

View File

@ -21,12 +21,35 @@ pub struct Name {
#[derive(Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
pub struct Fqn(Name);
#[derive(Clone, Copy, Debug, PartialEq)]
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, serde_repr::Deserialize_repr)]
#[repr(u8)]
pub enum ItemType {
Crate,
Module,
Item,
Member,
Module = 0,
ExternCrate = 1,
Import = 2,
Struct = 3,
Enum = 4,
Function = 5,
Typedef = 6,
Static = 7,
Trait = 8,
Impl = 9,
TyMethod = 10,
Method = 11,
StructField = 12,
Variant = 13,
Macro = 14,
Primitive = 15,
AssocType = 16,
Constant = 17,
AssocConst = 18,
Union = 19,
ForeignType = 20,
Keyword = 21,
OpaqueTy = 22,
ProcAttribute = 23,
ProcDerive = 24,
TraitAlias = 25,
}
#[derive(Clone, Debug, PartialEq)]
@ -143,10 +166,6 @@ impl Fqn {
self.first()
}
pub fn is_crate(&self) -> bool {
self.0.is_singleton()
}
pub fn parent(&self) -> Option<Self> {
self.0.parent().map(From::from)
}
@ -188,6 +207,42 @@ impl ops::Deref for Fqn {
}
}
impl str::FromStr for ItemType {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"mod" => Ok(ItemType::Module),
"externcrate" => Ok(ItemType::ExternCrate),
"import" => Ok(ItemType::Import),
"struct" => Ok(ItemType::Struct),
"union" => Ok(ItemType::Union),
"enum" => Ok(ItemType::Enum),
"fn" => Ok(ItemType::Function),
"type" => Ok(ItemType::Typedef),
"static" => Ok(ItemType::Static),
"trait" => Ok(ItemType::Trait),
"impl" => Ok(ItemType::Impl),
"tymethod" => Ok(ItemType::TyMethod),
"method" => Ok(ItemType::Method),
"structfield" => Ok(ItemType::StructField),
"variant" => Ok(ItemType::Variant),
"macro" => Ok(ItemType::Macro),
"primitive" => Ok(ItemType::Primitive),
"associatedtype" => Ok(ItemType::AssocType),
"constant" => Ok(ItemType::Constant),
"associatedconstant" => Ok(ItemType::AssocConst),
"foreigntype" => Ok(ItemType::ForeignType),
"keyword" => Ok(ItemType::Keyword),
"opaque" => Ok(ItemType::OpaqueTy),
"attr" => Ok(ItemType::ProcAttribute),
"derive" => Ok(ItemType::ProcDerive),
"traitalias" => Ok(ItemType::TraitAlias),
_ => Err(anyhow::anyhow!("Unsupported item type: {}", s)),
}
}
}
impl Crate {
pub fn new(name: String, path: path::PathBuf) -> Self {
Crate { name, path }
@ -198,10 +253,12 @@ impl Crate {
if let Some(local_name) = name.rest() {
if let Some(path) = parser::find_item(self.path.join("all.html"), local_name)? {
let path = path::PathBuf::from(path);
let file_name = path.file_name().unwrap().to_str().unwrap();
let item_type: ItemType = file_name.splitn(2, '.').next().unwrap().parse()?;
return Ok(Some(Item::new(
name.clone(),
self.path.join(path),
ItemType::Item,
item_type,
)));
}
}
@ -221,12 +278,7 @@ impl Crate {
};
let path = self.path.join(module_path).join("index.html");
if path.is_file() {
let item_type = if name.is_crate() {
ItemType::Crate
} else {
ItemType::Module
};
Some(Item::new(name.clone(), path, item_type))
Some(Item::new(name.clone(), path, ItemType::Module))
} else {
None
}
@ -240,7 +292,7 @@ impl Crate {
// TODO: error
self.find_item(&parent)
.unwrap()
.and_then(|i| i.find_member(name.last()))
.and_then(|i| i.find_member(name))
} else {
None
}
@ -254,23 +306,20 @@ impl Item {
pub fn load_doc(&self) -> anyhow::Result<Doc> {
match self.ty {
ItemType::Member => parser::parse_member_doc(&self.path, &self.name),
ItemType::Module | ItemType::Crate => parser::parse_module_doc(&self.path, &self.name),
ItemType::Item => parser::parse_item_doc(&self.path, &self.name),
ItemType::TyMethod
| ItemType::Method
| ItemType::StructField
| ItemType::Variant
| ItemType::AssocType
| ItemType::AssocConst => parser::parse_member_doc(&self.path, &self.name),
ItemType::Module => parser::parse_module_doc(&self.path, &self.name),
_ => parser::parse_item_doc(&self.path, &self.name),
}
}
pub fn find_member(&self, name: &str) -> Option<Item> {
pub fn find_member(&self, name: &Fqn) -> Option<Item> {
// TODO: error handling
if parser::find_member(&self.path, name).unwrap() {
Some(Item::new(
self.name.child(name),
self.path.clone(),
ItemType::Member,
))
} else {
None
}
parser::find_member(&self.path, name).unwrap()
}
}

View File

@ -57,9 +57,9 @@ struct CrateData {
paths: Vec<(usize, String)>,
}
#[derive(Debug, Default, PartialEq, serde_tuple::Deserialize_tuple)]
#[derive(Debug, PartialEq, serde_tuple::Deserialize_tuple)]
struct ItemData {
ty: usize,
ty: doc::ItemType,
name: String,
path: String,
desc: String,
@ -122,8 +122,7 @@ impl Index {
&item.path
};
if item.ty == 16 {
// Skip associated types (== item type 16)
if item.ty == doc::ItemType::AssocType {
continue;
}
@ -152,6 +151,7 @@ impl Index {
#[cfg(test)]
mod tests {
use super::{CrateData, Data, ItemData};
use crate::doc::ItemType;
#[test]
fn test_empty() {
@ -174,11 +174,14 @@ mod tests {
fn test_one_item() {
let mut expected: Data = Default::default();
let mut krate: CrateData = Default::default();
let mut item: ItemData = Default::default();
item.name = "name".to_owned();
item.path = "path".to_owned();
item.desc = "desc".to_owned();
krate.items.push(item);
krate.items.push(ItemData {
ty: ItemType::Module,
name: "name".to_owned(),
path: "path".to_owned(),
desc: "desc".to_owned(),
parent: None,
_ignored: Default::default(),
});
expected.crates.insert("test".to_owned(), krate);
let actual: Data = serde_json::from_str(
"{\"test\": {\"i\": [[0, \"name\", \"path\", \"desc\", null, null]], \"p\": []}}",

View File

@ -37,9 +37,26 @@ pub fn find_item<P: AsRef<path::Path>>(path: P, item: &str) -> anyhow::Result<Op
Ok(item)
}
pub fn find_member<P: AsRef<path::Path>>(path: P, item: &str) -> anyhow::Result<bool> {
let document = parse_file(path)?;
Ok(get_member(&document, item)?.is_some())
pub fn find_member<P: AsRef<path::Path>>(
path: P,
name: &doc::Fqn,
) -> anyhow::Result<Option<doc::Item>> {
let document = parse_file(path.as_ref())?;
if let Some(member) = get_member(&document, name.last())? {
let parent = member
.as_node()
.parent()
.context("Member element does not have a parent")?;
if let Some(parent_id) = get_attribute(parent.as_element().unwrap(), "id") {
let item_type: doc::ItemType = parent_id.splitn(2, '.').next().unwrap().parse()?;
return Ok(Some(doc::Item::new(
name.clone(),
path.as_ref().to_owned(),
item_type,
)));
}
}
Ok(None)
}
fn select(
@ -154,11 +171,7 @@ fn get_member(
document: &kuchiki::NodeRef,
name: &str,
) -> anyhow::Result<Option<kuchiki::NodeDataRef<kuchiki::ElementData>>> {
document
.select(&format!("#{}\\.v", name))
.ok()
.context("Could not select member by id")
.map(|mut i| i.next())
select_first(document, &format!("#{}\\.v", name))
}
fn get_attribute(element: &kuchiki::ElementData, name: &str) -> Option<String> {