Skip to main content

terminal_magic/modules/
install.rs

1use std::{path::{Path, PathBuf}, process::Command};
2
3use colored::Colorize;
4use indexmap::IndexMap;
5use mustache::MapBuilder;
6
7use crate::{modules::{update::update, read_config}, models::{GlobalConfig, PluginType, FileSystemEntry, PluginInfo}, prompts::read, template::{add_files_as_vars, render}};
8
9// Copyright (c) 2022 Patrick Amrein <amrein@ubique.ch>
10// 
11// This software is released under the MIT License.
12// https://opensource.org/licenses/MIT
13
14
15pub fn install(global_config: &GlobalConfig, git_repo: &str, plugin_name: &str) {
16    let home_path = global_config.home.join(plugin_name);
17    if home_path.exists() {
18        update(global_config, git_repo, plugin_name, false, true);
19        return;
20    }
21    let path_to_module = Path::new(git_repo).join(plugin_name);
22    if !path_to_module.exists() {
23        eprintln!(
24            "{}",
25            "Could not find module in the git repo. Did you execute `git pull`?".red()
26        );
27        std::process::exit(1);
28    }
29    let mustache = mustache::compile_path(path_to_module.join("template.sh"))
30        .expect("Could not parse mustache template");
31
32    let mut toml = read_config(&path_to_module.join("config.toml")).expect("Cannot find TOML");
33    if let Some(internal_deps) = toml.plugin_info.internal_dependencies.as_mut() {
34        for dep in internal_deps {
35            install(global_config, git_repo, dep);
36        }
37    }
38    if let Some(external_deps) = toml.plugin_info.external_dependencies.as_ref() {
39        for dep in external_deps {
40            println!(
41                "[{}] needs external dependency {}",
42                plugin_name.yellow(),
43                dep.yellow()
44            );
45        }
46    }
47    let mut mustache_map_builder = MapBuilder::new();
48    if let Some(placeholders) = toml.placeholders.as_mut() {
49        for placeholder in placeholders.iter_mut() {
50            println!("Read {}", placeholder.0);
51            let (new_mustache_map_builder, object) =
52                read(placeholder.0, placeholder.1, mustache_map_builder);
53            mustache_map_builder = new_mustache_map_builder;
54
55            mustache_map_builder = mustache_map_builder
56                .insert(placeholder.0, &object)
57                .expect("Could not parse object");
58            *placeholder.1 = object;
59        }
60    }
61    let home_path = global_config.home.join(plugin_name);
62    if std::fs::create_dir_all(&home_path).is_ok() {
63        println!("Created Plugin directory");
64    }
65    println!("Copying supporting files");
66    if let Some(files) = &toml.supporting_files {
67        mustache_map_builder = add_files_as_vars(
68            files,
69            mustache_map_builder,
70            &home_path,
71            &path_to_module,
72            &home_path,
73            true,
74        );
75    }
76    if let PluginType::RustPackage { path, git, tag } = &toml.plugin_info.plugin_type {
77        let mut install_command = Command::new("cargo");
78        if let Some(git) = git {
79            install_command
80                .env("CARGO_NET_GIT_FETCH_WITH_CLI", "true")
81                .arg("install")
82                .arg("--git")
83                .arg(git);
84            if let Some(tag) = tag {
85                install_command.arg("--tag").arg(tag);
86            }
87        } else if let Some(path) = path {
88            install_command
89                .arg("install")
90                .arg("--path")
91                .arg(&path_to_module.join(path));
92        } else {
93            panic!("either path or git should be set");
94        };
95        match install_command
96            .spawn()
97            .expect("Could not install module")
98            .wait_with_output()
99        {
100            Ok(_) => println!("Successfully installed rust-module"),
101            Err(err) => panic!("{:?}", err),
102        }
103    }
104
105    let mustache_map = mustache_map_builder.build();
106    let script = render(mustache, mustache_map);
107    write_file(global_config, toml, script, plugin_name, &path_to_module);
108}
109
110
111pub fn write_supporting_files(
112    files: &IndexMap<String, FileSystemEntry>,
113    home: &Path,
114    path_to_module: &Path,
115    cwd: &Path,
116) {
117    for (_, file) in files {
118        match file {
119            FileSystemEntry::File {
120                version,
121                path,
122                destination,
123            } => {
124                let destination = if let Some(destination) = destination {
125                    destination
126                        .to_owned()
127                        .parse()
128                        .expect("Could not parse path")
129                } else {
130                    cwd.join(path)
131                };
132                if std::fs::remove_file(&destination).is_ok() {
133                    println!(
134                        "{:?} existed, overwriting with new version: {}",
135                        destination, version
136                    );
137                }
138                let source_relative = path.parse::<PathBuf>().expect("Source path is invalid");
139                let source = path_to_module.join(source_relative);
140                if let Err(err) = std::fs::copy(&source, &destination) {
141                    panic!(
142                        "Could not copy file from source {:?} to {:?}\n{:?}",
143                        source, destination, err
144                    );
145                }
146            }
147            FileSystemEntry::Directory {
148                version,
149                path,
150                destination,
151                files,
152            } => {
153                let destination = if let Some(destination) = destination {
154                    destination
155                        .to_owned()
156                        .parse()
157                        .expect("Could not parse path")
158                } else {
159                    cwd.join(path)
160                };
161
162                if std::fs::create_dir_all(&destination).is_ok() {
163                    println!("Created {:?} [{}]", destination, version);
164                }
165                write_supporting_files(files, home, path_to_module, &destination);
166            }
167        }
168    }
169}
170
171
172pub fn write_file(global_config: &GlobalConfig, toml: PluginInfo, script: String, plugin_name: &str, path_to_module: &Path) {
173    let home_path = global_config.home.join(plugin_name);
174    if std::fs::create_dir_all(&home_path).is_ok() {
175        println!("Created directory");
176    }
177    if std::fs::remove_file(home_path.join("script.sh")).is_ok() {
178        println!("script.sh already existed");
179    }
180    if std::fs::copy(
181        path_to_module.join("config.toml"),
182        home_path.join("config.toml"),
183    )
184    .is_ok()
185    {}
186
187    // if let Some(files) = &toml.supporting_files {
188    //     write_supporting_files(files, &home_path, path_to_module, &home_path);
189    // }
190
191    if std::fs::write(home_path.join("script.sh"), script).is_ok() {
192        if std::fs::remove_file(home_path.join("data.toml")).is_ok() {
193            println!("data.toml File existed");
194        }
195        if std::fs::write(
196            home_path.join("data.toml"),
197            toml::to_vec(&toml).expect("could not serialize data"),
198        )
199        .is_ok()
200        {
201            println!("Successfully wrote plugin {}!", plugin_name);
202        }
203    }
204}