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.
master
Robin Krahl 3 years ago
parent 37ff920d30
commit 48405481d0
No known key found for this signature in database
GPG Key ID: 8E9B0870524F69D8

@ -1,5 +1,5 @@
<!---
SPDX-FileCopyrightText: 2020 Robin Krahl <robin.krahl@ireas.org>
SPDX-FileCopyrightText: 2020-2021 Robin Krahl <robin.krahl@ireas.org>
SPDX-License-Identifier: MIT
-->
@ -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)

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2020 Robin Krahl <robin.krahl@ireas.org>
// SPDX-FileCopyrightText: 2020-2021 Robin Krahl <robin.krahl@ireas.org>
// 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<String, CrateData>,
}
#[derive(Debug, Default, PartialEq, serde::Deserialize)]
#[derive(Debug, Default, PartialEq)]
struct CrateData {
#[serde(rename = "i")]
items: Vec<ItemData>,
#[serde(rename = "p")]
paths: Vec<(usize, String)>,
}
impl<'de> serde::Deserialize<'de> for CrateData {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
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<CrateDataVersions> 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<doc::ItemType, D::Error>
where
D: serde::de::Deserializer<'de>,
{
use doc::ItemType;
use serde::de::{Deserialize, Error};
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")),
#[derive(Clone, Copy, Debug, PartialEq)]
struct ItemType(doc::ItemType);
impl From<doc::ItemType> for ItemType {
fn from(ty: doc::ItemType) -> Self {
Self(ty)
}
}
impl From<ItemType> for doc::ItemType {
fn from(ty: ItemType) -> Self {
ty.0
}
}
impl<'de> serde::Deserialize<'de> for ItemType {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
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<IndexItem> = 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<IndexItem> = Vec::new();

@ -0,0 +1,26 @@
// SPDX-FileCopyrightText: 2021 Robin Krahl <robin.krahl@ireas.org>
// 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<super::ItemData>,
#[serde(rename = "p")]
paths: Vec<(usize, String)>,
}
impl From<CrateData> for super::CrateData {
fn from(data: CrateData) -> Self {
Self {
items: data.items,
paths: data.paths,
}
}
}

@ -0,0 +1,53 @@
// SPDX-FileCopyrightText: 2021 Robin Krahl <robin.krahl@ireas.org>
// 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<super::ItemType>,
#[serde(rename = "n")]
item_names: Vec<String>,
#[serde(rename = "q")]
item_paths: Vec<String>,
#[serde(rename = "d")]
item_descs: Vec<String>,
#[serde(rename = "i")]
item_parents: Vec<usize>,
#[serde(rename = "p")]
paths: Vec<(usize, String)>,
}
impl From<CrateData> 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,
}
}
}

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2020 Robin Krahl <robin.krahl@ireas.org>
// SPDX-FileCopyrightText: 2020-2021 Robin Krahl <robin.krahl@ireas.org>
// 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 dont 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 dont 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(

@ -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;

Loading…
Cancel
Save