1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#[macro_use] extern crate serde_derive;

use std::env;
use std::path::PathBuf;

use clap::{App, SubCommand, AppSettings};

pub mod project;
pub mod music;
pub mod parser;
pub mod book;
pub mod render;
pub mod watch;
pub mod util;
pub mod error;

use crate::project::Project;
use crate::watch::{Watch, WatchEvent};
use crate::error::*;


fn get_cwd() -> Result<PathBuf> {
    let cwd = env::current_dir().context("Could not read current directory") ?;
    ensure!(cwd.as_path().to_str().is_some(),
        format!("Path is not valid unicode: '{}'", cwd.display()));
    Ok(cwd)
}

pub fn bard_init() -> Result<()> {
    let cwd = get_cwd() ?;

    Project::init(&cwd)
        .context("Could not initialize a new project")
}

pub fn bard_make() -> Result<Project> {
    let cwd = get_cwd() ?;

    Project::new(&cwd).and_then(|project| {
        project.render() ?;
        Ok(project)
    }).context("Could not make project")
}

pub fn bard_watch() -> Result<()> {
    let (mut watch, cancellation) = Watch::new() ?;

    let _ = ctrlc::set_handler(move || {
        cancellation.cancel();
    });

    loop {
        eprintln!("Making project ...");
        let project = bard_make() ?;

        eprintln!("Watching for changes ...");
        match watch.watch(&project)? {
            WatchEvent::Path(path) => eprintln!("Modification detected at '{}' ...", path.display()),
            WatchEvent::Pathless => eprintln!("Modification detected ..."),
            WatchEvent::Cancel => break,
        }
    }

    Ok(())
}

pub fn bard() -> Result<()> {
    let args = App::new("bard")
        .version("0.3")
        .author("Vojtech Kral <vojtech@kral.hk>")
        .about("bard: Songbook compiler")
        .setting(AppSettings::VersionlessSubcommands)
        .setting(AppSettings::ArgRequiredElseHelp)
        .subcommand(SubCommand::with_name("init")
            .about("Initialize and empty project with default settings"))
        .subcommand(SubCommand::with_name("make")
            .about("Process the current project and generate output files"))
        .subcommand(SubCommand::with_name("watch")
            .about("Watch the current project and its input files for changes, and re-make the project each time there's a change"))
        .get_matches();

    if let Some(_args) = args.subcommand_matches("init") {
        bard_init() ?;
    } else if let Some(_args) = args.subcommand_matches("make") {
        bard_make() ?;
    } else if let Some(_args) = args.subcommand_matches("watch") {
        bard_watch() ?;
    }

    Ok(())
}