ddoc 0.18.0

doc site generator
Documentation
use {
    crate::*,
    indexmap::IndexMap,
    std::{
        fs,
        path::Path,
    },
};

static TEMPLATE_INIT_HJSON: &str = r#"
# This is a configuration file for the ddoc static site generator.
# For details and instructions, see https://dystroy.org/ddoc/

title: <title>
description: <description>
favicon: null // eg "img/favicon.ico"
ddoc-version: "<ddoc-version>" // minimum required ddoc version to build this site

# You may add variables here and use them in elements of this config or
# of plugins with --var-name
vars: <vars>

# plugins to activate for this site
active-plugins: [
    # You should use only one theme plugin
    # (or none, but then you'll have to write your own CSS and HTML elements)
    "theme-columns"
    // "theme-top-menu"

    # Non theme plugins bring optional features
    "search"
    "toc-activate"
]

# All pages must be listed here
# One of them must be index.md
# You can have submenus, eg:
# pages: {
#     Home: index.md
#     Guide: {
#         "Getting Started": guide/getting_started.md
#         "Advanced Topics": guide/advanced_topics.md
#     }
# }
site-map: {
    Home: index.md
}

// # This describes the content of the <body> element of each page.
// # Elements starting with 'ddoc-' will be replaced by special items:
// # links, page TOC, global menu, main HTML translated from markdown, etc.
// #
// # Links (ddoc-link) can have { img, inline, href, label, alt, target}.
// # All these fields are optional.
// # Hrefs starting with '/' are relative to the site's root (eg '/guide/help.md')
// body: {
//     header: {
//         nav.before-menu: {
//             // this is a good place for a logo or a link to a wider site
//         }
//         ddoc-menu: {
//             // if true, the generated HTML includes a checkbox which
//             // can be styled into a hamburger menu for small screens
//             hamburger-checkbox: true
//         }
//         nav.after-menu: {
//             ddoc-link.previous-page-link: {
//                 inline: img/ddoc-left-arrow.svg
//                 href: --previous
//             }
//             ddoc-link.search-opener: {
//                 inline: img/ddoc-search.svg
//                 href: --search
//             }
//             ddoc-link.next-page-link: {
//                 inline: img/ddoc-right-arrow.svg
//                 href: --next
//             }
//         }
//     }
//     article: {
//         aside.page-nav: {
//             ddoc-toc: {
//                 // if true, a script is injected to highlight the part the user
//                 // is currently viewing (the 'active' class is added to the
//                 // corresponding TOC item)
//                 activate-visible-item: true
//             }
//         }
//         ddoc-main: {}
//     }
//     footer: {
//     }
// }

"#;

/// Initialize a ddoc.hjson file in the specified directory
/// (do nothing if one already exists)
///
/// # Errors
/// Return `DdError::InvalidConfig` if an existing ddoc.hjson
/// cannot be read, or other less likely `DdError` variants on
/// write errors when creating a new ddoc.hjson
pub fn init_hjson_in_dir(
    dir: &Path,
    init_values: &InitValues,
) -> DdResult<Config> {
    let path = dir.join("ddoc.hjson");
    if path.exists() {
        read_file(&path).map_err(|e| {
            error!("Error reading {}: {}", path.display(), e);
            // Return a specific error so that the caller can
            // issue a proper message to the user
            DdError::InvalidConfig
        })
    } else {
        // FIXME we should handle the case of this generation failing
        // to build a parsable hjson (then probably give up with the
        // init values)
        let mut hjson = TEMPLATE_INIT_HJSON.to_owned();
        let title = init_values.title.as_deref().unwrap_or("Unnamed Site");
        let description = init_values.description.as_deref().unwrap_or("");

        let mut vars: IndexMap<String, String> = IndexMap::default();
        if let Some(github_repo) = &init_values.github_repo {
            vars.insert("github-url".to_string(), github_repo.to_string());
        }
        let vars = serde_json::to_string_pretty(&vars).unwrap_or_else(|_| "{}".to_string());

        hjson = hjson
            .replace("<title>", &escape_hjson_string(title))
            .replace("<description>", &escape_hjson_string(description))
            .replace("<ddoc-version>", DDOC_VERSION)
            .replace("<vars>", &vars);

        fs::write(&path, hjson)?;
        eprintln!("Created {}", path.display());
        read_file(&path)
    }
}

pub fn escape_hjson_string(s: &str) -> String {
    format!("{:?}", s)
}