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
//!  Angreal - project templating and task management
//!
//!  A package for templating code based projects and providing methods
//! for the creation and management of common operational tasks associated with the
//! project.
//!

#[macro_use]
extern crate version;
#[macro_use]
pub mod macros;

pub mod builder;
pub mod git;
pub mod init;
pub mod py_logger;
pub mod task;
pub mod utils;

use task::ANGREAL_TASKS;
use builder::build_app;

use log::{debug, error};
use pyo3::types::IntoPyDict;
use std::ops::Not;
use std::vec::Vec;

use std::process::exit;

use pyo3::prelude::*;

/// The main function is just an entry point to be called from the core angreal library.
#[pyfunction]
fn main() -> PyResult<()> {
    // we have to do this because we're calling the main function through python - when lib+bin build support is available, we can factor away
    let mut argvs: Vec<String> = std::env::args().collect();
    argvs.remove(0);
    argvs.remove(0);

    env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("trace"))
        // .format_timestamp(None)
        // .format_module_path(true)
        .init();

    // Load any angreal task assets that are available to us
    let in_angreal_project = utils::is_angreal_project().is_ok();

    if in_angreal_project {
        let angreal_path = utils::is_angreal_project().unwrap();
        debug!("Angreal project detected, loading found tasks.");
        // get a list of files
        let angreal_tasks_to_load = utils::get_task_files(angreal_path);

        // Explicitly capture error with exit
        let _angreal_tasks_to_load = match angreal_tasks_to_load {
            Ok(tasks) => tasks,
            Err(_) => exit(1),
        };

        // load the files , IF a file has command or task decorators - they'll register themselves now
        for task in _angreal_tasks_to_load.iter() {
            utils::load_python(task.clone()).unwrap_or(());
        }
    }

    let app = build_app();
    let mut app_copy = app.clone();
    let sub_command = app.get_matches_from(&argvs);

    match sub_command.subcommand() {
        Some(("init", _sub_matches)) => init::init(
            _sub_matches.value_of("template").unwrap(),
            _sub_matches.is_present("force"),
            _sub_matches.is_present("defaults").not(),
        ),
        Some((task, sub_m)) => {
            if !in_angreal_project {
                error!("This doesn't appear to be an angreal project.");
                exit(1)
            }
            let some_command = ANGREAL_TASKS.lock().unwrap().clone();
            let some_command = some_command.iter().find(|&x| x.name == task);

            let command = match some_command {
                None => {
                    error!("Task {}, not found.", task.clone());
                    app_copy.print_help().unwrap_or(());
                    exit(1)
                }
                Some(some_command) => some_command,
            };

            let args = builder::select_args(task.to_string());

            Python::with_gil(|py| {
                let mut kwargs: Vec<(&str, PyObject)> = Vec::new();

                for arg in args.into_iter() {
                    let n = Box::leak(Box::new(arg.name));
                    let v = sub_m.value_of(n.clone());
                    println!("{:?}", arg.python_type);
                    match v {
                        None => (),
                        Some(v) => {
                            match arg.python_type.unwrap().as_str() {
                                "str" => kwargs.push((n.as_str(), v.to_object(py))),
                                "int" => kwargs
                                    .push((n.as_str(), v.parse::<i32>().unwrap().to_object(py))),
                                "float" => kwargs
                                    .push((n.as_str(), v.parse::<f32>().unwrap().to_object(py))),
                                _ => kwargs.push((n.as_str(), v.to_object(py))),
                            }
                        }
                    }
                }
                let r_value = command.func.call(py, (), Some(kwargs.into_py_dict(py)));

                match r_value {
                    Ok(_r_value) => {}
                    Err(r_value) => {
                        error!("An error occured :");
                        error!("{:?}", r_value.traceback(py).unwrap().format());
                        exit(1);
                    }
                }
            });
        }
        _ => {
            println!("process for current context")
        }
    }

    Ok(())
}

/// registering the angreal namespace for import from python
#[pymodule]
fn angreal(_py: Python, m: &PyModule) -> PyResult<()> {
    m.add_function(wrap_pyfunction!(main, m)?)?;
    task::register(_py, m)?;
    py_logger::register();
    Ok(())
}