Implement basic doc item data structures

This patch adds the doc and source module with the basic data structures
for the documentation items:
- A Source loads the documentation items from e. g. the file system.
- The documentation items (modules, traits, etc.) are grouped by crates.

It also adds a simple Source implementation, DirSource, that reads the
data from a local directory.
This commit is contained in:
Robin Krahl 2020-01-13 15:21:25 +01:00
parent 11b6d27b8a
commit e6f299635e
3 changed files with 168 additions and 1 deletions

48
src/doc.rs Normal file
View File

@ -0,0 +1,48 @@
// SPDX-FileCopyrightText: 2020 Robin Krahl <robin.krahl@ireas.org>
// SPDX-License-Identifier: MIT
use std::path;
#[derive(Clone, Debug, PartialEq)]
pub struct Crate {
pub name: String,
pub path: path::PathBuf,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Item {
pub path: path::PathBuf,
pub name: String,
}
#[derive(Clone, Debug, Default)]
pub struct Doc {
pub title: String,
pub description: Option<String>,
pub definition: Option<String>,
}
impl Crate {
pub fn new(name: String, path: path::PathBuf) -> Self {
Crate { name, path }
}
pub fn find_item(&self, _item: &[&str]) -> anyhow::Result<Option<Item>> {
Ok(None)
}
}
impl Item {
pub fn load_doc(&self) -> anyhow::Result<Doc> {
Ok(Doc::new(self.name.clone()))
}
}
impl Doc {
pub fn new(title: String) -> Self {
Self {
title,
..Default::default()
}
}
}

View File

@ -1,6 +1,9 @@
// SPDX-FileCopyrightText: 2020 Robin Krahl <robin.krahl@ireas.org>
// SPDX-License-Identifier: MIT
mod doc;
mod source;
use structopt::StructOpt;
/// Command-line interface for rustdoc documentation
@ -19,6 +22,40 @@ struct Opt {
fn main() -> anyhow::Result<()> {
let opt = Opt::from_args();
println!("Documentation for {}", &opt.keyword);
let sources = load_sources(&opt.source_paths)?;
let doc = find_doc(&sources, &opt.keyword)?;
println!("{}", doc.title);
Ok(())
}
fn load_sources(sources: &[String]) -> anyhow::Result<Vec<Box<dyn source::Source>>> {
let mut vec: Vec<Box<dyn source::Source>> = Vec::new();
for s in sources {
vec.push(source::get_source(s)?);
}
// The last source should be searched first --> reverse source vector
vec.reverse();
Ok(vec)
}
fn find_doc(sources: &[Box<dyn source::Source>], keyword: &str) -> anyhow::Result<doc::Doc> {
use anyhow::Context;
let parts: Vec<&str> = keyword.split("::").collect();
let crate_ = find_crate(sources, parts[0])?;
let item = crate_
.find_item(&parts[1..])?
.with_context(|| format!("Could not find the item {}", keyword))?;
item.load_doc()
}
fn find_crate(sources: &[Box<dyn source::Source>], name: &str) -> anyhow::Result<doc::Crate> {
use anyhow::Context;
sources
.iter()
.filter_map(|s| s.find_crate(name))
.next()
.with_context(|| format!("Could not find the crate {}", name))
}

82
src/source.rs Normal file
View File

@ -0,0 +1,82 @@
// SPDX-FileCopyrightText: 2020 Robin Krahl <robin.krahl@ireas.org>
// SPDX-License-Identifier: MIT
//! Handles documentation sources, for example local directories.
use std::path;
use anyhow::anyhow;
use crate::doc;
/// Documentation source, for example a local directory.
pub trait Source {
fn find_crate(&self, name: &str) -> Option<doc::Crate>;
}
/// Local directory containing documentation data.
///
/// The directory must contain documentation for one or more crates in subdirectories. Suitable
/// directories are the `doc` directory generated by `cargo doc` or the root directory of the Rust
/// documentation.
#[derive(Clone, Debug, PartialEq)]
pub struct DirSource {
path: path::PathBuf,
}
impl DirSource {
fn new(path: path::PathBuf) -> Self {
Self { path }
}
}
impl Source for DirSource {
fn find_crate(&self, name: &str) -> Option<doc::Crate> {
let crate_path = self.path.join(name);
if crate_path.join("all.html").is_file() {
Some(doc::Crate::new(name.to_owned(), crate_path))
} else {
None
}
}
}
pub fn get_source<P: AsRef<path::Path>>(path: P) -> anyhow::Result<Box<dyn Source>> {
if path.as_ref().is_dir() {
Ok(Box::new(DirSource::new(path.as_ref().to_path_buf())))
} else {
Err(anyhow!(
"This source is not supported: {}",
path.as_ref().display()
))
}
}
#[cfg(test)]
mod tests {
use std::path;
use super::Source;
#[test]
fn dir_source_find_crate() {
fn assert_crate(source: &dyn super::Source, path: &path::PathBuf, name: &str) {
assert_eq!(
source.find_crate(name),
Some(super::doc::Crate::new(name.to_owned(), path.join(name)))
);
}
let doc = path::PathBuf::from("./target/doc");
assert!(
doc.is_dir(),
"You have to run `cargo doc` before running this test case."
);
let source = super::DirSource::new(doc.clone());
assert_crate(&source, &doc, "clap");
assert_crate(&source, &doc, "lazy_static");
assert_eq!(source.find_crate("lazystatic"), None);
}
}