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
//
// Wildland Project
//
// Copyright © 2022 Golem Foundation
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 3 as published by
// the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

mod cli;
mod context;
mod dropins;
mod logger;
mod plugins;
mod sigint;

use std::path::PathBuf;

use context::Context;
use reedline_repl_rs::Repl;

const VERSION: &str = env!("CARGO_PKG_VERSION");

fn throwing_error_handler<Context, E: std::fmt::Debug + std::fmt::Display>(
    error: E,
    _repl: &Repl<Context, E>,
) -> Result<(), reedline_repl_rs::Error> {
    eprintln!("DevshellError: {error:?}");
    // Hack to get the error out for exiting in script mode
    // not necessary an UnknownCommand, reedline_repl_rs::Error does not have a Generic error type
    Err(reedline_repl_rs::Error::UnknownCommand("".to_string()))
}

fn interactive_error_handler<Context, E: std::fmt::Debug + std::fmt::Display>(
    error: E,
    _repl: &Repl<Context, E>,
) -> Result<(), reedline_repl_rs::Error> {
    eprintln!("DevshellError: {error:?}");
    Ok(())
}

fn main() -> anyhow::Result<()> {
    logger::init_subscriber()?;
    tracing::info!("Starting new instance of devshell");

    println!("=============================================================");
    println!("GIT_BRANCH: {}", env!("GIT_BRANCH"));
    println!("GIT_HASH: {}", env!("GIT_HASH"));
    println!("=============================================================");

    // load arguments
    let mut context = Context::default();
    let args = <cli::CliArguments as clap::Parser>::parse();
    args.check_io_eligible()?;

    // do the work related to used shortcuts
    if let Err(res) = cli::act_on_args(&args, &mut context) {
        eprintln!("{res}");
    }

    // resolve path to history file
    let mut devshell_home: Option<PathBuf> = None;
    if let Ok(dir) = std::env::var("DEVSHELL_HOME") {
        devshell_home = Some(PathBuf::from(dir));
    } else if let Ok(dir) = std::env::var("HOME") {
        devshell_home = Some(PathBuf::from(dir).join(".devshell"));
    };

    // prepare repl object
    let mut repl: Repl<Context, anyhow::Error> = Repl::new(context)
        .with_name("devshell>")
        .with_version(VERSION)
        .with_description("wildland developer shell");

    if args.uses_script() || args.uses_pipe() {
        repl = repl.with_error_handler(throwing_error_handler);
    }

    // load history file
    if let Some(devshell_home) = devshell_home {
        let historyfile = devshell_home.join("history");
        repl = repl.with_history(historyfile, 5000);
    }

    // load plugins
    let repl: Repl<Context, anyhow::Error> = dropins::lss::handler::extend(repl);
    let repl: Repl<Context, anyhow::Error> = dropins::sfo::handler::extend(repl);
    let repl: Repl<Context, anyhow::Error> = context::extend(repl);
    let repl: Repl<Context, anyhow::Error> = plugins::dfs::extend(repl);
    let repl: Repl<Context, anyhow::Error> = plugins::cargo::extend_all(repl);
    let repl: Repl<Context, anyhow::Error> = plugins::evs::extend_all(repl);
    let mut repl = repl;

    tracing::warn!("Starting REPL loop");
    args.process_io(&mut repl);

    if args.is_interactive() || !(args.uses_pipe() || args.uses_script()) {
        repl = repl.with_error_handler(interactive_error_handler);
        repl.run()?;
    }

    Ok(())
}