Move item lookup logic into Sources struct
This match adds the Sources struct that is a wrapper for a vector of Source structs. It now contains the lookup logic that was previously contained in functions in main.rs.
This commit is contained in:
parent
2c1b91462e
commit
44b1405595
98
src/main.rs
98
src/main.rs
@ -9,9 +9,8 @@
|
||||
//! and the `source` module. Per default, we look for documentation in the directory
|
||||
//! `share/doc/rust{,-doc}/html` relative to the Rust installation path (`rustc --print sysroot`
|
||||
//! or `usr`) and in `./target/doc`.
|
||||
//! 2. We try to look up the given keyword in all acailable sources, see the `find_doc` function
|
||||
//! and the `source` module for the lookup logic and the `doc` module for the loaded
|
||||
//! documentation.
|
||||
//! 2. We try to look up the given keyword in all acailable sources, see the and the `source`
|
||||
//! module for the lookup logic and the `doc` module for the loaded documentation.
|
||||
//! 3. If we didn’t find a match in the previous step, we load the search index from the
|
||||
//! `search-index.js` file for all sources and try to find a matching item. If we find one, we
|
||||
//! open the documentation for that item as in step 2. See the `search_doc` function and the
|
||||
@ -52,7 +51,7 @@ fn main() -> anyhow::Result<()> {
|
||||
|
||||
let args = args::Args::load()?;
|
||||
let sources = load_sources(&args.source_paths, !args.no_default_sources)?;
|
||||
let doc = if let Some(doc) = find_doc(&sources, &args.keyword, None)? {
|
||||
let doc = if let Some(doc) = sources.find(&args.keyword, None)? {
|
||||
Some(doc)
|
||||
} else if !args.no_search {
|
||||
search_doc(&sources, &args.keyword)?
|
||||
@ -80,11 +79,8 @@ fn main() -> anyhow::Result<()> {
|
||||
}
|
||||
|
||||
/// Load all sources given as a command-line argument and, if enabled, the default sources.
|
||||
fn load_sources(
|
||||
sources: &[String],
|
||||
load_default_sources: bool,
|
||||
) -> anyhow::Result<Vec<Box<dyn source::Source>>> {
|
||||
let mut vec: Vec<Box<dyn source::Source>> = Vec::new();
|
||||
fn load_sources(sources: &[String], load_default_sources: bool) -> anyhow::Result<source::Sources> {
|
||||
let mut vec = Vec::new();
|
||||
|
||||
if load_default_sources {
|
||||
for path in get_default_sources() {
|
||||
@ -106,7 +102,7 @@ fn load_sources(
|
||||
// The last source should be searched first --> reverse source vector
|
||||
vec.reverse();
|
||||
|
||||
Ok(vec)
|
||||
Ok(source::Sources::new(vec))
|
||||
}
|
||||
|
||||
fn get_default_sources() -> Vec<path::PathBuf> {
|
||||
@ -132,32 +128,14 @@ fn get_sysroot() -> Option<path::PathBuf> {
|
||||
.map(|s| s.trim().into())
|
||||
}
|
||||
|
||||
/// Find the documentation for an item with the given name (exact matches only).
|
||||
fn find_doc(
|
||||
sources: &[Box<dyn source::Source>],
|
||||
name: &doc::Name,
|
||||
ty: Option<doc::ItemType>,
|
||||
) -> anyhow::Result<Option<doc::Doc>> {
|
||||
let fqn = name.clone().into();
|
||||
for source in sources {
|
||||
if let Some(doc) = source.find_doc(&fqn, ty)? {
|
||||
return Ok(Some(doc));
|
||||
}
|
||||
}
|
||||
log::info!("Could not find item '{}'", fqn);
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Use the search index to find the documentation for an item that partially matches the given
|
||||
/// keyword.
|
||||
fn search_doc(
|
||||
sources: &[Box<dyn source::Source>],
|
||||
name: &doc::Name,
|
||||
) -> anyhow::Result<Option<doc::Doc>> {
|
||||
fn search_doc(sources: &source::Sources, name: &doc::Name) -> anyhow::Result<Option<doc::Doc>> {
|
||||
if let Some(item) = search_item(sources, name)? {
|
||||
use anyhow::Context;
|
||||
|
||||
let doc = find_doc(sources, &item.name, Some(item.ty))?
|
||||
let doc = sources
|
||||
.find(&item.name, Some(item.ty))?
|
||||
.with_context(|| format!("Could not find documentation for {}", &item.name))?;
|
||||
Ok(Some(doc))
|
||||
} else {
|
||||
@ -171,21 +149,10 @@ fn search_doc(
|
||||
|
||||
/// Use the search index to find an item that partially matches the given keyword.
|
||||
fn search_item(
|
||||
sources: &[Box<dyn source::Source>],
|
||||
sources: &source::Sources,
|
||||
name: &doc::Name,
|
||||
) -> anyhow::Result<Option<index::IndexItem>> {
|
||||
let indexes = sources
|
||||
.iter()
|
||||
.filter_map(|s| s.load_index().transpose())
|
||||
.collect::<anyhow::Result<Vec<_>>>()?;
|
||||
let mut items = indexes
|
||||
.iter()
|
||||
.map(|i| i.find(name))
|
||||
.collect::<Vec<_>>()
|
||||
.concat();
|
||||
items.sort_unstable();
|
||||
items.dedup();
|
||||
|
||||
let items = sources.search(name)?;
|
||||
if items.is_empty() {
|
||||
Err(anyhow::anyhow!(
|
||||
"Could not find documentation for {}",
|
||||
@ -213,7 +180,7 @@ fn select_item(
|
||||
name
|
||||
);
|
||||
|
||||
println!("Found mulitple matches for {} – select one of:", name);
|
||||
println!("Found multiple matches for {} – select one of:", name);
|
||||
println!();
|
||||
let width = (items.len() + 1).to_string().len();
|
||||
for (i, item) in items.iter().enumerate() {
|
||||
@ -240,35 +207,28 @@ mod tests {
|
||||
#[test]
|
||||
fn test_find_doc() {
|
||||
with_rustdoc("*", |_, path| {
|
||||
let sources = vec![source::get_source(path).unwrap()];
|
||||
let sources = source::Sources::new(vec![source::get_source(path).unwrap()]);
|
||||
|
||||
assert!(
|
||||
super::find_doc(&sources, &"kuchiki".to_owned().into(), None)
|
||||
.unwrap()
|
||||
.is_some()
|
||||
);
|
||||
assert!(
|
||||
super::find_doc(&sources, &"kuchiki::NodeRef".to_owned().into(), None)
|
||||
.unwrap()
|
||||
.is_some()
|
||||
);
|
||||
assert!(super::find_doc(
|
||||
&sources,
|
||||
&"kuchiki::NodeDataRef::as_node".to_owned().into(),
|
||||
None
|
||||
)
|
||||
assert!(sources
|
||||
.find(&"kuchiki".to_owned().into(), None)
|
||||
.unwrap()
|
||||
.is_some());
|
||||
assert!(
|
||||
super::find_doc(&sources, &"kuchiki::traits".to_owned().into(), None)
|
||||
assert!(sources
|
||||
.find(&"kuchiki::NodeRef".to_owned().into(), None)
|
||||
.unwrap()
|
||||
.is_some()
|
||||
);
|
||||
assert!(
|
||||
super::find_doc(&sources, &"kachiki".to_owned().into(), None)
|
||||
.is_some());
|
||||
assert!(sources
|
||||
.find(&"kuchiki::NodeDataRef::as_node".to_owned().into(), None)
|
||||
.unwrap()
|
||||
.is_none()
|
||||
);
|
||||
.is_some());
|
||||
assert!(sources
|
||||
.find(&"kuchiki::traits".to_owned().into(), None)
|
||||
.unwrap()
|
||||
.is_some());
|
||||
assert!(sources
|
||||
.find(&"kachiki".to_owned().into(), None)
|
||||
.unwrap()
|
||||
.is_none());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,9 @@ pub trait Source {
|
||||
fn load_index(&self) -> anyhow::Result<Option<index::Index>>;
|
||||
}
|
||||
|
||||
/// A collection of sources.
|
||||
pub struct Sources(Vec<Box<dyn Source>>);
|
||||
|
||||
/// Local directory containing documentation data.
|
||||
///
|
||||
/// The directory must contain documentation for one or more crates in subdirectories. Suitable
|
||||
@ -32,6 +35,45 @@ pub struct DirSource {
|
||||
path: path::PathBuf,
|
||||
}
|
||||
|
||||
impl Sources {
|
||||
pub fn new(sources: Vec<Box<dyn Source>>) -> Sources {
|
||||
Sources(sources)
|
||||
}
|
||||
|
||||
/// Find the documentation for an item with the given name (exact matches only).
|
||||
pub fn find(
|
||||
&self,
|
||||
name: &doc::Name,
|
||||
ty: Option<doc::ItemType>,
|
||||
) -> anyhow::Result<Option<doc::Doc>> {
|
||||
let fqn = name.clone().into();
|
||||
for source in &self.0 {
|
||||
if let Some(doc) = source.find_doc(&fqn, ty)? {
|
||||
return Ok(Some(doc));
|
||||
}
|
||||
}
|
||||
log::info!("Could not find item '{}'", fqn);
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Use the search index to find an item that partially matches the given keyword.
|
||||
pub fn search(&self, name: &doc::Name) -> anyhow::Result<Vec<index::IndexItem>> {
|
||||
let indexes = self
|
||||
.0
|
||||
.iter()
|
||||
.filter_map(|s| s.load_index().transpose())
|
||||
.collect::<anyhow::Result<Vec<_>>>()?;
|
||||
let mut items = indexes
|
||||
.iter()
|
||||
.map(|i| i.find(name))
|
||||
.collect::<Vec<_>>()
|
||||
.concat();
|
||||
items.sort_unstable();
|
||||
items.dedup();
|
||||
Ok(items)
|
||||
}
|
||||
}
|
||||
|
||||
impl DirSource {
|
||||
fn new(path: path::PathBuf) -> Self {
|
||||
log::info!("Created directory source at '{}'", path.display());
|
||||
|
@ -15,14 +15,14 @@ use crate::source;
|
||||
pub trait Viewer: fmt::Debug {
|
||||
fn open(
|
||||
&self,
|
||||
sources: Vec<Box<dyn source::Source>>,
|
||||
sources: source::Sources,
|
||||
args: args::ViewerArgs,
|
||||
doc: &doc::Doc,
|
||||
) -> anyhow::Result<()>;
|
||||
|
||||
fn open_examples(
|
||||
&self,
|
||||
sources: Vec<Box<dyn source::Source>>,
|
||||
sources: source::Sources,
|
||||
args: args::ViewerArgs,
|
||||
doc: &doc::Doc,
|
||||
examples: Vec<doc::Example>,
|
||||
|
@ -45,7 +45,7 @@ impl TextViewer {
|
||||
impl viewer::Viewer for TextViewer {
|
||||
fn open(
|
||||
&self,
|
||||
_sources: Vec<Box<dyn source::Source>>,
|
||||
_sources: source::Sources,
|
||||
args: args::ViewerArgs,
|
||||
doc: &doc::Doc,
|
||||
) -> anyhow::Result<()> {
|
||||
@ -54,7 +54,7 @@ impl viewer::Viewer for TextViewer {
|
||||
|
||||
fn open_examples(
|
||||
&self,
|
||||
_sources: Vec<Box<dyn source::Source>>,
|
||||
_sources: source::Sources,
|
||||
args: args::ViewerArgs,
|
||||
doc: &doc::Doc,
|
||||
examples: Vec<doc::Example>,
|
||||
|
@ -28,7 +28,7 @@ impl TuiViewer {
|
||||
|
||||
fn render<F>(
|
||||
&self,
|
||||
sources: Vec<Box<dyn source::Source>>,
|
||||
sources: source::Sources,
|
||||
args: args::ViewerArgs,
|
||||
doc: &doc::Doc,
|
||||
f: F,
|
||||
@ -49,7 +49,7 @@ impl TuiViewer {
|
||||
impl viewer::Viewer for TuiViewer {
|
||||
fn open(
|
||||
&self,
|
||||
sources: Vec<Box<dyn source::Source>>,
|
||||
sources: source::Sources,
|
||||
args: args::ViewerArgs,
|
||||
doc: &doc::Doc,
|
||||
) -> anyhow::Result<()> {
|
||||
@ -58,7 +58,7 @@ impl viewer::Viewer for TuiViewer {
|
||||
|
||||
fn open_examples(
|
||||
&self,
|
||||
sources: Vec<Box<dyn source::Source>>,
|
||||
sources: source::Sources,
|
||||
args: args::ViewerArgs,
|
||||
doc: &doc::Doc,
|
||||
examples: Vec<doc::Example>,
|
||||
@ -70,16 +70,13 @@ impl viewer::Viewer for TuiViewer {
|
||||
}
|
||||
|
||||
pub struct Context {
|
||||
pub sources: Vec<Box<dyn source::Source>>,
|
||||
pub sources: source::Sources,
|
||||
pub args: args::ViewerArgs,
|
||||
pub highlighter: Option<utils::Highlighter>,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
pub fn new(
|
||||
sources: Vec<Box<dyn source::Source>>,
|
||||
args: args::ViewerArgs,
|
||||
) -> anyhow::Result<Context> {
|
||||
pub fn new(sources: source::Sources, args: args::ViewerArgs) -> anyhow::Result<Context> {
|
||||
let highlighter = utils::get_highlighter(&args)?;
|
||||
Ok(Context {
|
||||
sources,
|
||||
@ -197,7 +194,7 @@ fn create_backend() -> anyhow::Result<Box<dyn cursive::backend::Backend>> {
|
||||
}
|
||||
|
||||
fn create_cursive(
|
||||
sources: Vec<Box<dyn source::Source>>,
|
||||
sources: source::Sources,
|
||||
args: args::ViewerArgs,
|
||||
) -> anyhow::Result<cursive::Cursive> {
|
||||
use cursive::event::{Event, Key};
|
||||
@ -264,26 +261,13 @@ fn handle_link(s: &mut cursive::Cursive, doc_name: &doc::Fqn, doc_ty: doc::ItemT
|
||||
}
|
||||
}
|
||||
|
||||
fn find_doc(
|
||||
sources: &[Box<dyn source::Source>],
|
||||
ty: Option<doc::ItemType>,
|
||||
name: &doc::Fqn,
|
||||
) -> anyhow::Result<doc::Doc> {
|
||||
for source in sources {
|
||||
if let Some(doc) = source.find_doc(name, ty)? {
|
||||
return Ok(doc);
|
||||
}
|
||||
}
|
||||
Err(anyhow::anyhow!(
|
||||
"Could not find documentation for item: {}",
|
||||
name
|
||||
))
|
||||
}
|
||||
|
||||
fn open_link(s: &mut cursive::Cursive, link: ResolvedLink) -> anyhow::Result<()> {
|
||||
match link {
|
||||
ResolvedLink::Doc(ty, name) => {
|
||||
let doc = find_doc(&context(s).sources, ty, &name)?;
|
||||
let doc = context(s)
|
||||
.sources
|
||||
.find(&name, ty)?
|
||||
.with_context(|| format!("Could not find documentation for item: {}", name))?;
|
||||
let mut renderer = context(s).create_renderer(&doc);
|
||||
renderer.render_doc(&doc).unwrap();
|
||||
let view = renderer.into_view();
|
||||
|
Loading…
Reference in New Issue
Block a user