From 48405481d07407b3911d4f70b8f1f8cf6d1f91de Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 6 Jun 2021 13:00:50 +0200 Subject: [PATCH] Support new search index format (Rust 1.52) This path adds support for the new search index format introduced in Rust 1.52.0. With this new format, the index items are no longer serialized as an array of structs but as arrays of the struct fields. --- CHANGELOG.md | 3 +- src/index.rs | 136 +++++++++++++++++++++++++++--------------- src/index/v1_44.rs | 26 ++++++++ src/index/v1_52.rs | 53 ++++++++++++++++ src/main.rs | 8 +-- src/viewer/tui/mod.rs | 2 +- 6 files changed, 173 insertions(+), 55 deletions(-) create mode 100644 src/index/v1_44.rs create mode 100644 src/index/v1_52.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 38c08dd..fb6b51d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,5 @@ @@ -9,6 +9,7 @@ SPDX-License-Identifier: MIT - Add `o` command for opening a documentation item to the tui viewer. - Add tests for Rust 1.48.0, 1.49.0, 1.50.0, 1.51.0, 1.52.0 and 1.52.1. +- Add support for the new search index format introduced in Rust 1.52.0. ## v0.4.1 (2020-10-11) diff --git a/src/index.rs b/src/index.rs index daf0ab3..54282f4 100644 --- a/src/index.rs +++ b/src/index.rs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2020 Robin Krahl +// SPDX-FileCopyrightText: 2020-2021 Robin Krahl // SPDX-License-Identifier: MIT //! Search index for a documentation source. @@ -6,14 +6,17 @@ //! The search index is read from the `search-index.js` file generated by rustdoc. It contains a //! list of items grouped by their crate. //! -//! For details on the format of the search index, see the `html/render.rs` file in `librustdoc`. -//! Note that the format of the search index changed in April 2020 (Rust 1.44.0) with commit -//! b4fb3069ce82f61f84a9487d17fb96389d55126a. We only support the new format as the old format is -//! much harder to parse. +//! For details on the format of the search index, see the `html/render/mod.rs` (previously +//! `html/render.rs`) file in `librustdoc`. Note that the format of the search index changed in +//! April 2020 (Rust 1.44.0) with commit b4fb3069ce82f61f84a9487d17fb96389d55126a. We only support +//! the new format as the old format is much harder to parse. //! //! For details on the generation of the search index, see the `html/render/cache.rs` file in //! `librustdoc`. +mod v1_44; +mod v1_52; + use std::collections; use std::fmt; use std::fs; @@ -57,18 +60,37 @@ struct Data { crates: collections::HashMap, } -#[derive(Debug, Default, PartialEq, serde::Deserialize)] +#[derive(Debug, Default, PartialEq)] struct CrateData { - #[serde(rename = "i")] items: Vec, - #[serde(rename = "p")] paths: Vec<(usize, String)>, } +impl<'de> serde::Deserialize<'de> for CrateData { + fn deserialize>(deserializer: D) -> Result { + CrateDataVersions::deserialize(deserializer).map(From::from) + } +} + +#[derive(Debug, PartialEq, serde::Deserialize)] +#[serde(untagged)] +enum CrateDataVersions { + V1_44(v1_44::CrateData), + V1_52(v1_52::CrateData), +} + +impl From for CrateData { + fn from(versions: CrateDataVersions) -> Self { + match versions { + CrateDataVersions::V1_44(data) => data.into(), + CrateDataVersions::V1_52(data) => data.into(), + } + } +} + #[derive(Debug, PartialEq, serde_tuple::Deserialize_tuple)] struct ItemData { - #[serde(deserialize_with = "deserialize_item_type")] - ty: doc::ItemType, + ty: ItemType, name: String, path: String, desc: String, @@ -76,41 +98,56 @@ struct ItemData { _ignored: serde_json::Value, } -fn deserialize_item_type<'de, D>(d: D) -> Result -where - D: serde::de::Deserializer<'de>, -{ - use doc::ItemType; - use serde::de::{Deserialize, Error}; +#[derive(Clone, Copy, Debug, PartialEq)] +struct ItemType(doc::ItemType); - match u8::deserialize(d)? { - 0 => Ok(ItemType::Module), - 1 => Ok(ItemType::ExternCrate), - 2 => Ok(ItemType::Import), - 3 => Ok(ItemType::Struct), - 4 => Ok(ItemType::Enum), - 5 => Ok(ItemType::Function), - 6 => Ok(ItemType::Typedef), - 7 => Ok(ItemType::Static), - 8 => Ok(ItemType::Trait), - 9 => Ok(ItemType::Impl), - 10 => Ok(ItemType::TyMethod), - 11 => Ok(ItemType::Method), - 12 => Ok(ItemType::StructField), - 13 => Ok(ItemType::Variant), - 14 => Ok(ItemType::Macro), - 15 => Ok(ItemType::Primitive), - 16 => Ok(ItemType::AssocType), - 17 => Ok(ItemType::Constant), - 18 => Ok(ItemType::AssocConst), - 19 => Ok(ItemType::Union), - 20 => Ok(ItemType::ForeignType), - 21 => Ok(ItemType::Keyword), - 22 => Ok(ItemType::OpaqueTy), - 23 => Ok(ItemType::ProcAttribute), - 24 => Ok(ItemType::ProcDerive), - 25 => Ok(ItemType::TraitAlias), - _ => Err(D::Error::custom("Unexpected item type")), +impl From for ItemType { + fn from(ty: doc::ItemType) -> Self { + Self(ty) + } +} + +impl From for doc::ItemType { + fn from(ty: ItemType) -> Self { + ty.0 + } +} + +impl<'de> serde::Deserialize<'de> for ItemType { + fn deserialize>(deserializer: D) -> Result { + use doc::ItemType; + use serde::de::Error; + + match u8::deserialize(deserializer)? { + 0 => Ok(ItemType::Module), + 1 => Ok(ItemType::ExternCrate), + 2 => Ok(ItemType::Import), + 3 => Ok(ItemType::Struct), + 4 => Ok(ItemType::Enum), + 5 => Ok(ItemType::Function), + 6 => Ok(ItemType::Typedef), + 7 => Ok(ItemType::Static), + 8 => Ok(ItemType::Trait), + 9 => Ok(ItemType::Impl), + 10 => Ok(ItemType::TyMethod), + 11 => Ok(ItemType::Method), + 12 => Ok(ItemType::StructField), + 13 => Ok(ItemType::Variant), + 14 => Ok(ItemType::Macro), + 15 => Ok(ItemType::Primitive), + 16 => Ok(ItemType::AssocType), + 17 => Ok(ItemType::Constant), + 18 => Ok(ItemType::AssocConst), + 19 => Ok(ItemType::Union), + 20 => Ok(ItemType::ForeignType), + 21 => Ok(ItemType::Keyword), + 22 => Ok(ItemType::OpaqueTy), + 23 => Ok(ItemType::ProcAttribute), + 24 => Ok(ItemType::ProcDerive), + 25 => Ok(ItemType::TraitAlias), + _ => Err(D::Error::custom("Unexpected item type")), + } + .map(Self) } } @@ -185,7 +222,8 @@ impl Index { &item.path }; - if item.ty == doc::ItemType::AssocType { + let ty = doc::ItemType::from(item.ty); + if ty == doc::ItemType::AssocType { continue; } @@ -201,7 +239,7 @@ impl Index { log::info!("Found index match '{}'", full_name); matches.push(IndexItem { name: full_name, - ty: item.ty, + ty, description: item.desc.clone(), }); } @@ -241,7 +279,7 @@ mod tests { let mut expected: Data = Default::default(); let mut krate: CrateData = Default::default(); krate.items.push(ItemData { - ty: ItemType::Module, + ty: ItemType::Module.into(), name: "name".to_owned(), path: "path".to_owned(), desc: "desc".to_owned(), @@ -277,7 +315,7 @@ mod tests { assert_eq!(empty, index.find(&"NodeDataReff".to_owned().into())); }); - with_rustdoc(">=1.50.0,<1.52.0", |_, path| { + with_rustdoc(">=1.50.0", |_, path| { let index = Index::load(path.join("search-index.js")).unwrap().unwrap(); let empty: Vec = Vec::new(); @@ -296,7 +334,7 @@ mod tests { assert_eq!(empty, index.find(&"NodeDataReff".to_owned().into())); }); - with_rustdoc(">=1.44.0,<1.52.0", |_, path| { + with_rustdoc(">=1.44.0", |_, path| { let index = Index::load(path.join("search-index.js")).unwrap().unwrap(); let empty: Vec = Vec::new(); diff --git a/src/index/v1_44.rs b/src/index/v1_44.rs new file mode 100644 index 0000000..86cf7f9 --- /dev/null +++ b/src/index/v1_44.rs @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: 2021 Robin Krahl +// SPDX-License-Identifier: MIT + +//! Search index format as of Rust 1.44.0. +//! +//! This module contains data structures specific to the search index format introduced with Rust +//! 1.44.0 (commit [b4fb3069ce82f61f84a9487d17fb96389d55126a][]). +//! +//! [b4fb3069ce82f61f84a9487d17fb96389d55126a]: https://github.com/rust-lang/rust/commit/b4fb3069ce82f61f84a9487d17fb96389d55126a + +#[derive(Debug, Default, PartialEq, serde::Deserialize)] +pub struct CrateData { + #[serde(rename = "i")] + items: Vec, + #[serde(rename = "p")] + paths: Vec<(usize, String)>, +} + +impl From for super::CrateData { + fn from(data: CrateData) -> Self { + Self { + items: data.items, + paths: data.paths, + } + } +} diff --git a/src/index/v1_52.rs b/src/index/v1_52.rs new file mode 100644 index 0000000..a5d0191 --- /dev/null +++ b/src/index/v1_52.rs @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: 2021 Robin Krahl +// SPDX-License-Identifier: MIT + +//! Search index format as of Rust 1.52.0. +//! +//! This module contains data structures specific to the search index format introduced with Rust +//! 1.52.0 (commit [3934dd1b3e7514959202de6ca0d2636bcae21830][]). +//! +//! [3934dd1b3e7514959202de6ca0d2636bcae21830]: https://github.com/rust-lang/rust/commit/3934dd1b3e7514959202de6ca0d2636bcae21830 + +#[derive(Debug, Default, PartialEq, serde::Deserialize)] +pub struct CrateData { + #[serde(rename = "t")] + item_types: Vec, + #[serde(rename = "n")] + item_names: Vec, + #[serde(rename = "q")] + item_paths: Vec, + #[serde(rename = "d")] + item_descs: Vec, + #[serde(rename = "i")] + item_parents: Vec, + #[serde(rename = "p")] + paths: Vec<(usize, String)>, +} + +impl From for super::CrateData { + fn from(data: CrateData) -> Self { + let items = data + .item_types + .into_iter() + .zip(data.item_names.into_iter()) + .zip(data.item_paths.into_iter()) + .zip(data.item_descs.into_iter()) + .zip(data.item_parents.into_iter()) + .map(|((((ty, name), path), desc), parent)| super::ItemData { + ty, + name, + path, + desc, + parent: match parent { + 0 => None, + parent => Some(parent - 1), + }, + _ignored: Default::default(), + }) + .collect(); + Self { + items, + paths: data.paths, + } + } +} diff --git a/src/main.rs b/src/main.rs index 55ea6fc..d7ccc91 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2020 Robin Krahl +// SPDX-FileCopyrightText: 2020-2021 Robin Krahl // SPDX-License-Identifier: MIT //! rusty-man is a command-line viewer for documentation generated by `rustdoc`. @@ -27,9 +27,9 @@ //! For details on the structure of the HTML files and the search index, you have to look at the //! `html::render` module in the `librustdoc` source code. //! -//! Note that the format of the search index changed in a recent Rust version (> 1.40 and <= 1.44). -//! We don’t support the old index format. As the format of the HTML files is not specified, -//! rusty-man might not work with new Rust versions that change the documentation format. +//! Note that the format of the search index changed in Rust 1.44. We don’t support the old index +//! format. As the format of the HTML files is not specified, rusty-man might not work with new +//! Rust versions that change the documentation format. // We have to disable some clippy lints as our MSRV is 1.40: #![allow( diff --git a/src/viewer/tui/mod.rs b/src/viewer/tui/mod.rs index b5d681f..6daf2f3 100644 --- a/src/viewer/tui/mod.rs +++ b/src/viewer/tui/mod.rs @@ -224,7 +224,7 @@ fn create_cursive( cursive.add_global_callback('o', open_doc_dialog); let mut theme = theme::Theme { - shadow: false, + shadow: false, ..Default::default() }; theme.palette[theme::PaletteColor::Background] = theme::Color::TerminalDefault;