Allow for optional multiple shelves

This commit is contained in:
Coil 2024-03-30 18:29:34 +01:00
parent 2903f0711f
commit 3c58bf4132
3 changed files with 115 additions and 48 deletions

View File

@ -3,14 +3,14 @@ use std::io::Read;
use std::io::Write; use std::io::Write;
use std::path::PathBuf; use std::path::PathBuf;
use mdbook::config::BookshelfConfig;
use resolve_path::PathResolveExt; use resolve_path::PathResolveExt;
use super::command_prelude::*; use super::command_prelude::*;
use mdbook::config::ShelfConfig;
use mdbook::errors::Result; use mdbook::errors::Result;
use mdbook::MDBook; use mdbook::MDBook;
const SHELF_DIR: &str = "shelf"; const INDEX_BOOK_DIR: &str = "index";
const REPOS_DIR: &str = "repositories"; const REPOS_DIR: &str = "repositories";
const INDEX_MD_FILE: &str = "index.md"; const INDEX_MD_FILE: &str = "index.md";
const INDEX_HTML_FILE: &str = "index.html"; const INDEX_HTML_FILE: &str = "index.html";
@ -28,7 +28,7 @@ struct BookContext {
authors: String, authors: String,
} }
struct ShelfContext { struct BookshelfContext {
book_dir: PathBuf, book_dir: PathBuf,
source_dir: PathBuf, source_dir: PathBuf,
url_prefix: String, url_prefix: String,
@ -37,7 +37,7 @@ struct ShelfContext {
summary_file_name: PathBuf, summary_file_name: PathBuf,
} }
fn update_index( fn update_index_with_book(
index_file: &mut File, index_file: &mut File,
summary_file: &mut File, summary_file: &mut File,
shelf_source: &PathBuf, shelf_source: &PathBuf,
@ -46,7 +46,7 @@ fn update_index(
) -> Result<()> { ) -> Result<()> {
// Create post in index file // Create post in index file
let book_link = format!( let book_link = format!(
"## [{title}](<{prefix}/{BOOKSHELF_DIR}/{BOOKS_DIR}/{title}/{INDEX_HTML_FILE}>)", "### [{title}](<{prefix}/{BOOKSHELF_DIR}/{BOOKS_DIR}/{title}/{INDEX_HTML_FILE}>)",
title = context.title, title = context.title,
prefix = root_prefix prefix = root_prefix
); );
@ -73,7 +73,6 @@ fn update_index(
"- [{title}](./{file_name})", "- [{title}](./{file_name})",
title = context.title title = context.title
)?; )?;
writeln!(summary_file)?;
Ok(()) Ok(())
} }
@ -101,8 +100,8 @@ fn process_book(path: &str, books_dir: &PathBuf, shelf_url: &str) -> Result<Book
Ok(book_context) Ok(book_context)
} }
fn setup_shelf_book(config: &ShelfConfig) -> Result<ShelfContext> { fn setup_bookshelf_book(config: &BookshelfConfig) -> Result<BookshelfContext> {
let book_dir = format!("{BOOKSHELF_DIR}/{SHELF_DIR}"); let book_dir = format!("{BOOKSHELF_DIR}/{INDEX_BOOK_DIR}");
let book = MDBook::init(&book_dir).build()?; let book = MDBook::init(&book_dir).build()?;
let build_dir = book.config.build.build_dir.to_str().unwrap_or_default(); let build_dir = book.config.build.build_dir.to_str().unwrap_or_default();
let url_prefix = if !config.root_url_prefix.is_empty() { let url_prefix = if !config.root_url_prefix.is_empty() {
@ -120,7 +119,7 @@ fn setup_shelf_book(config: &ShelfConfig) -> Result<ShelfContext> {
let mut summary_file_name = book.source_dir(); let mut summary_file_name = book.source_dir();
summary_file_name.push(SUMMARY_MD_FILE); summary_file_name.push(SUMMARY_MD_FILE);
Ok(ShelfContext { Ok(BookshelfContext {
book_dir: book_dir.into(), book_dir: book_dir.into(),
source_dir: book.source_dir(), source_dir: book.source_dir(),
url_prefix, url_prefix,
@ -131,32 +130,44 @@ fn setup_shelf_book(config: &ShelfConfig) -> Result<ShelfContext> {
} }
pub fn execute(_args: &ArgMatches) -> Result<()> { pub fn execute(_args: &ArgMatches) -> Result<()> {
let mut file = File::open("shelf.toml")?; // Make sure everything is clean
let mut contents = String::new();
file.read_to_string(&mut contents)?;
let shelf_config: ShelfConfig = toml::from_str(&contents)?;
let _ = std::fs::remove_dir_all(BOOKSHELF_DIR); let _ = std::fs::remove_dir_all(BOOKSHELF_DIR);
let _ = std::fs::remove_dir_all(REPOS_DIR); let _ = std::fs::remove_dir_all(REPOS_DIR);
let shelf_context = setup_shelf_book(&shelf_config)?; let mut file = File::open("shelf.toml")?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
let bookshelf_config: BookshelfConfig = toml::from_str(&contents)?;
let shelf_context = setup_bookshelf_book(&bookshelf_config)?;
let mut index_file = File::create(shelf_context.index_file_name).unwrap(); let mut index_file = File::create(shelf_context.index_file_name).unwrap();
writeln!(index_file, "# {title}", title = shelf_config.title)?; writeln!(index_file, "# {title}", title = bookshelf_config.title)?;
writeln!(index_file)?; writeln!(index_file)?;
let mut summary_file = File::create(shelf_context.summary_file_name).unwrap(); let mut summary_file = File::create(shelf_context.summary_file_name).unwrap();
writeln!(summary_file, "# Summary")?; writeln!(summary_file, "# Summary")?;
writeln!( writeln!(
summary_file, summary_file,
"- [{title}](./{INDEX_MD_FILE})", "[{title}](./{INDEX_MD_FILE})",
title = shelf_config.title title = bookshelf_config.title
)?; )?;
let mut books_build_dir = std::env::current_dir()?; let mut books_build_dir = std::env::current_dir()?;
books_build_dir.push(BOOKSHELF_DIR); books_build_dir.push(BOOKSHELF_DIR);
books_build_dir.push(BOOKS_DIR); books_build_dir.push(BOOKS_DIR);
let books_build_dir = books_build_dir;
let shelves = if let Some(shelves) = bookshelf_config.shelves {
shelves
} else if let Some(shelf) = bookshelf_config.shelf_config {
vec![shelf]
} else {
error!("No shelves or default shelf found in config");
Vec::new()
};
for shelf_config in shelves {
let _ = start_shelf(&mut index_file, &mut summary_file, &shelf_config.title);
for sb in &shelf_config.books { for sb in &shelf_config.books {
let book_path = if let Some(url) = &sb.git_url { let book_path = if let Some(url) = &sb.git_url {
prepare_git(sb, url) prepare_git(sb, url)
@ -169,7 +180,7 @@ pub fn execute(_args: &ArgMatches) -> Result<()> {
if let Some(path) = book_path { if let Some(path) = book_path {
let update_context = process_book(&path, &books_build_dir, &shelf_context.url)?; let update_context = process_book(&path, &books_build_dir, &shelf_context.url)?;
let _ = update_index( let _ = update_index_with_book(
&mut index_file, &mut index_file,
&mut summary_file, &mut summary_file,
&shelf_context.source_dir, &shelf_context.source_dir,
@ -178,6 +189,7 @@ pub fn execute(_args: &ArgMatches) -> Result<()> {
)?; )?;
} }
} }
}
let shelf = MDBook::load(&shelf_context.book_dir)?; let shelf = MDBook::load(&shelf_context.book_dir)?;
shelf.build()?; shelf.build()?;
@ -185,6 +197,14 @@ pub fn execute(_args: &ArgMatches) -> Result<()> {
Ok(()) Ok(())
} }
fn start_shelf(index_file: &mut File, summary_file: &mut File, title: &str) -> Result<()> {
writeln!(summary_file, "# {title}")?;
writeln!(summary_file)?;
writeln!(index_file, "## {title}")?;
Ok(())
}
fn prepare_git(sb: &mdbook::config::ShelfBook, url: &String) -> Option<String> { fn prepare_git(sb: &mdbook::config::ShelfBook, url: &String) -> Option<String> {
println!("{:?}", sb); println!("{:?}", sb);
@ -252,20 +272,20 @@ git_url = "secondurl"
[[book]] [[book]]
path = "../test_book" path = "../test_book"
"#; "#;
let cfg: ShelfConfig = toml::from_str(&toml).unwrap(); let cfg: BookshelfConfig = toml::from_str(&toml).unwrap();
assert_eq!(cfg.root_url_prefix, "myprefix"); assert_eq!(cfg.root_url_prefix, "myprefix");
let book = &cfg.books[0]; let book = &cfg.shelf_config.clone().unwrap().books[0];
assert_eq!(book.git_url.clone().unwrap(), "firsturl"); assert_eq!(book.git_url.clone().unwrap(), "firsturl");
assert_eq!(book.git_ref.clone().unwrap(), "shelf"); assert_eq!(book.git_ref.clone().unwrap(), "shelf");
assert_eq!(book.path.clone().unwrap(), "guide"); assert_eq!(book.path.clone().unwrap(), "guide");
let book = &cfg.books[1]; let book = &cfg.shelf_config.clone().unwrap().books[1];
assert_eq!(book.git_url.clone().unwrap(), "secondurl"); assert_eq!(book.git_url.clone().unwrap(), "secondurl");
assert!(book.git_ref.is_none()); assert!(book.git_ref.is_none());
assert!(book.path.is_none()); assert!(book.path.is_none());
let book = &cfg.books[2]; let book = &cfg.shelf_config.clone().unwrap().books[2];
assert_eq!(book.path.clone().unwrap(), "../test_book"); assert_eq!(book.path.clone().unwrap(), "../test_book");
} }
@ -275,7 +295,7 @@ fn test_config_defaults() {
[[book]] [[book]]
path = "../test_book" path = "../test_book"
"#; "#;
let cfg: ShelfConfig = toml::from_str(&toml).unwrap(); let cfg: BookshelfConfig = toml::from_str(&toml).unwrap();
assert_eq!(cfg.root_url_prefix, "".to_owned()); assert_eq!(cfg.root_url_prefix, "".to_owned());
assert_eq!(cfg.title, "Bookshelf".to_owned()); assert_eq!(cfg.title, "Overview".to_owned());
} }

View File

@ -637,7 +637,7 @@ impl HtmlConfig {
} }
} }
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug, Clone)]
/// Represents a book in a shelf /// Represents a book in a shelf
pub struct ShelfBook { pub struct ShelfBook {
/// Path to filesystem local book /// Path to filesystem local book
@ -651,27 +651,44 @@ pub struct ShelfBook {
pub git_ref: Option<String>, pub git_ref: Option<String>,
} }
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug, Clone)]
/// Represents a shelf that contains a lot of books /// Represents a shelf that contains a lot of books
pub struct ShelfConfig { pub struct ShelfConfig {
/// The books in the shelf /// The books in the shelf
#[serde(alias = "book")] #[serde(alias = "book")]
pub books: Vec<ShelfBook>, pub books: Vec<ShelfBook>,
/// Name of the shelf
#[serde(default = "default_shelf_title")]
pub title: String,
}
fn default_shelf_title() -> String {
"Bookshelf".to_owned()
}
#[derive(Deserialize, Debug)]
///
pub struct BookshelfConfig {
///
#[serde(alias = "shelf")]
pub shelves: Option<Vec<ShelfConfig>>,
///
#[serde(flatten)]
pub shelf_config: Option<ShelfConfig>,
/// this will be prepeneded to the backreference url /// this will be prepeneded to the backreference url
/// Say you want to publish to www.example.com/mydocs /// Say you want to publish to www.example.com/mydocs
/// you would set this to "mydocs" and then find your bookshelf at /// you would set this to "mydocs" and then find your bookshelf at
/// www.example.com/mydocs/bookshelf/shelf/book/index.html /// www.example.com/mydocs/bookshelf/shelf/book/index.html
#[serde(default = "default_shelf_root_url")] #[serde(default = "default_shelf_root_url")]
pub root_url_prefix: String, pub root_url_prefix: String,
/// Name of the shelf ///
#[serde(default = "default_shelf_title")] #[serde(default = "default_bookshelf_title")]
pub title: String, pub title: String,
} }
fn default_shelf_root_url() -> String { fn default_shelf_root_url() -> String {
"".to_owned() "".to_owned()
} }
fn default_shelf_title() -> String { fn default_bookshelf_title() -> String {
"Bookshelf".to_owned() "Overview".to_owned()
} }
/// Configuration for how to render the print icon, print.html, and print.css. /// Configuration for how to render the print icon, print.html, and print.css.

View File

@ -1,3 +1,9 @@
# Set this to your full path /<full_path>/test_shelf to be able to browse locally
# or any kind of prefix that you need for when you publish online
root_url_prefix = ""
# Use the first setup here for the simple one shelf structure
[[book]] [[book]]
git_url = "https://github.com/Coi-l/mdBook.git" git_url = "https://github.com/Coi-l/mdBook.git"
git_ref = "shelf" git_ref = "shelf"
@ -8,3 +14,27 @@ git_url = "https://github.com/rust-lang/book.git"
[[book]] [[book]]
path = "../test_book" path = "../test_book"
##########################
##########################
##########################
# Uncomment the setup below to test the multishelf/category structure
# title = "Documentation"
# [[shelf]]
# title = "MdBook"
# [[shelf.book]]
# git_url = "https://github.com/Coi-l/mdBook.git"
# git_ref = "shelf"
# path = "guide"
# [[shelf.book]]
# path = "../test_book"
# [[shelf]]
# title = "Rust"
# [[shelf.book]]
# git_url = "https://github.com/rust-lang/book.git"