bookyard 0.1.1

Build and locally edit a bookshelf for multiple mdBook projects.
use std::path::{Path, PathBuf};

use anyhow::Context;
use bookyard_core::{BookConfig, detect_book_source, insert_book, slugify_book_id};

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RegisteredBook {
    pub id: String,
    pub title: String,
}

pub fn register_book(
    source: PathBuf,
    folder: String,
    title: Option<String>,
    id: Option<String>,
    tags: Vec<String>,
) -> anyhow::Result<RegisteredBook> {
    let path = crate::commands::config_path()?;
    let root = crate::commands::root_dir()?;
    let mut config = crate::commands::load_config()?;
    let detected = detect_book_source(root.join(&source))
        .with_context(|| format!("failed to detect book source at {}", source.display()))?;
    let title = title
        .or(detected.title)
        .unwrap_or_else(|| title_from_path(&source));
    let id = id.unwrap_or_else(|| slugify_book_id(&title));
    let next_order = config
        .books
        .iter()
        .map(|book| book.order)
        .max()
        .unwrap_or(0)
        + 10;
    let mut book = BookConfig::new_mdbook(
        id.clone(),
        title.clone(),
        path_to_config_source(&source),
        vec![folder],
    );
    book.tags = tags;
    book.order = next_order;
    insert_book(&mut config, book)?;
    config
        .save_to(&path)
        .with_context(|| format!("failed to write {}", path.display()))?;
    Ok(RegisteredBook { id, title })
}

pub fn title_from_path(path: &Path) -> String {
    path.file_name()
        .and_then(|p| p.to_str())
        .unwrap_or("Book")
        .to_owned()
}

fn path_to_config_source(path: &Path) -> String {
    path.to_string_lossy().replace('\\', "/")
}