resufancy 0.1.1

Supercharge your resume with the power of your favourite web development tools
Documentation
use std::sync::mpsc;
use std::time::Duration;

use clap::{App, ArgMatches};
use filesystem::FileSystem;
use notify::{self, RecursiveMode, Watcher};
use resufancy::error::Result;
use resufancy::resume::RawResume;
use resufancy::{HtmlCompiler, PdfCompiler};

use super::args::{Arg, SKIP_HTML, SKIP_PDF, WATCH};
use super::Command;
use crate::{CSS_PATH, FILE_SYSTEM, HTML_PATH, PDF_PATH, PUG_PATH};

const NAME: &str = "build";

#[derive(Debug, Clone, Default)]
pub struct Build;

impl Build {
    pub fn new() -> Self {
        Self
    }
}

impl Command for Build {
    fn name(&self) -> &str {
        NAME
    }

    fn build(&self) -> App {
        App::new(NAME)
            .args(&[SKIP_HTML.build(), SKIP_PDF.build(), WATCH.build()])
            .about("Compile the resume into HTML and/or a PDF")
    }

    fn run(&self, matches: &ArgMatches) -> Result<Option<String>> {
        let skip_html = SKIP_HTML.value_from(matches);
        let skip_pdf = SKIP_PDF.value_from(matches);
        let pdf_compiler = PdfCompiler::default();

        if WATCH.value_from(matches) {
            let (tx, rx) = mpsc::channel();
            let mut watcher =
                notify::watcher(tx, Duration::from_millis(250)).expect("Failed to create watcher");

            watcher
                .watch(PUG_PATH, RecursiveMode::NonRecursive)
                .expect("Failed to watch file");
            watcher
                .watch(CSS_PATH, RecursiveMode::NonRecursive)
                .expect("Failed to watch file");

            let recompile = || {
                match compile(skip_html, skip_pdf, &pdf_compiler) {
                    Ok(_) => println!("Successfully rebuilt"),
                    Err(err) => eprintln!("Failed to build: {}", err),
                };
            };

            recompile();

            loop {
                if rx.recv().is_ok() {
                    recompile();
                }
            }
        } else {
            compile(skip_html, skip_pdf, &pdf_compiler)
        }
    }
}

fn compile(skip_html: bool, skip_pdf: bool, pdf_compiler: &PdfCompiler) -> Result<Option<String>> {
    let pug = FILE_SYSTEM.read_file(PUG_PATH)?;
    let css = FILE_SYSTEM.read_file(CSS_PATH)?;
    let raw_resume = RawResume::new(&pug, &css);
    let html_compiler = HtmlCompiler::default();
    let resume = html_compiler.compile(&raw_resume)?;

    if !skip_html {
        FILE_SYSTEM.write_file(HTML_PATH, resume.html())?;
        FILE_SYSTEM.write_file(CSS_PATH, resume.stylesheet())?;
    }

    if !skip_pdf {
        let pdf = pdf_compiler.compile(&resume)?;

        FILE_SYSTEM.write_file(PDF_PATH, pdf)?;
    }

    Ok(None)
}