aboutsummaryrefslogtreecommitdiff
path: root/helix-term/src/main.rs
blob: 0b55921ab18a62cccb02d89ec36ef43b56290823 (plain) (blame)
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#![allow(unused)]

mod application;
mod commands;
mod compositor;
mod keymap;
mod ui;

use application::Application;

use std::path::PathBuf;

use anyhow::{Context, Error, Result};

fn setup_logging(logpath: PathBuf, verbosity: u64) -> Result<()> {
    let mut base_config = fern::Dispatch::new();

    // Let's say we depend on something which whose "info" level messages are too
    // verbose to include in end-user output. If we don't need them,
    // let's not include them.
    // .level_for("overly-verbose-target", log::LevelFilter::Warn)

    base_config = match verbosity {
        0 => base_config.level(log::LevelFilter::Warn),
        1 => base_config.level(log::LevelFilter::Info),
        2 => base_config.level(log::LevelFilter::Debug),
        _3_or_more => base_config.level(log::LevelFilter::Trace),
    };

    // Separate file config so we can include year, month and day in file logs
    let file_config = fern::Dispatch::new()
        .format(|out, message, record| {
            out.finish(format_args!(
                "{} {} [{}] {}",
                chrono::Local::now().format("%Y-%m-%dT%H:%M:%S%.3f"),
                record.target(),
                record.level(),
                message
            ))
        })
        .chain(fern::log_file(logpath)?);

    base_config.chain(file_config).apply()?;

    Ok(())
}

pub struct Args {
    display_help: bool,
    display_version: bool,
    verbosity: u64,
    files: Vec<PathBuf>,
}

fn parse_args(mut args: Args) -> Result<Args> {
    let argv: Vec<String> = std::env::args().collect();
    let mut iter = argv.iter();

    iter.next(); // skip the program, we don't care about that

    while let Some(arg) = iter.next() {
        match arg.as_str() {
            "--" => break, // stop parsing at this point treat the remaining as files
            "--version" => args.display_version = true,
            "--help" => args.display_help = true,
            arg if arg.starts_with("--") => {
                return Err(Error::msg(format!(
                    "unexpected double dash argument: {}",
                    arg
                )))
            }
            arg if arg.starts_with('-') => {
                let arg = arg.get(1..).unwrap().chars();
                for chr in arg {
                    match chr {
                        'v' => args.verbosity += 1,
                        'V' => args.display_version = true,
                        'h' => args.display_help = true,
                        _ => return Err(Error::msg(format!("unexpected short arg {}", chr))),
                    }
                }
            }
            arg => args.files.push(PathBuf::from(arg)),
        }
    }

    // push the remaining args, if any to the files
    for filename in iter {
        args.files.push(PathBuf::from(filename));
    }

    Ok(args)
}

#[tokio::main]
async fn main() -> Result<()> {
    let cache_dir = helix_core::cache_dir();
    if !cache_dir.exists() {
        std::fs::create_dir(&cache_dir);
    }

    let logpath = cache_dir.join("helix.log");
    let help = format!(
        "\
{} {}
{}
{}

USAGE:
    hx [FLAGS] [files]...

ARGS:
    <files>...    Sets the input file to use

FLAGS:
    -h, --help       Prints help information
    -v               Increases logging verbosity each use for up to 3 times
                     (default file: {})
    -V, --version    Prints version information
",
        env!("CARGO_PKG_NAME"),
        env!("CARGO_PKG_VERSION"),
        env!("CARGO_PKG_AUTHORS"),
        env!("CARGO_PKG_DESCRIPTION"),
        logpath.display(),
    );

    let mut args: Args = Args {
        display_help: false,
        display_version: false,
        verbosity: 0,
        files: [].to_vec(),
    };

    args = parse_args(args).context("could not parse arguments")?;

    // Help has a higher priority and should be handled separately.
    if args.display_help {
        print!("{}", help);
        std::process::exit(0);
    }

    if args.display_version {
        println!("{} {}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"));
        std::process::exit(0);
    }

    let conf_dir = helix_core::config_dir();
    if !conf_dir.exists() {
        std::fs::create_dir(&conf_dir);
    }

    setup_logging(logpath, args.verbosity).context("failed to initialize logging")?;

    // TODO: use the thread local executor to spawn the application task separately from the work pool
    let mut app = Application::new(args).context("unable to create new appliction")?;
    app.run().await;

    Ok(())
}