diff --git a/Cargo.lock b/Cargo.lock index 2c5bbdc..d65c133 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml index 2d86699..901e845 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/src/doc.rs b/src/doc.rs index 5b28f79..376b153 100644 --- a/src/doc.rs +++ b/src/doc.rs @@ -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.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 { + 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 { 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 { + pub fn find_member(&self, name: &Fqn) -> Option { // 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() } } diff --git a/src/index.rs b/src/index.rs index 7783888..b54e1cf 100644 --- a/src/index.rs +++ b/src/index.rs @@ -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\": []}}", diff --git a/src/parser.rs b/src/parser.rs index 4e9408e..eabbe91 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -37,9 +37,26 @@ pub fn find_item>(path: P, item: &str) -> anyhow::Result>(path: P, item: &str) -> anyhow::Result { - let document = parse_file(path)?; - Ok(get_member(&document, item)?.is_some()) +pub fn find_member>( + path: P, + name: &doc::Fqn, +) -> anyhow::Result> { + 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>> { - 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 {