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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
/*
 * Vento, a CLI inventory for your files.
 * Copyright (C) 2022 Lux Aliaga
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * 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/>.
 *
 */

use super::{
    common,
    message::{append_emoji, throw_error, EmojiType, ErrorType},
};
use anyhow::{bail, Context, Result};
use colored::Colorize;
use size_format::SizeFormatterBinary;
use std::io::{self, Write};
use std::path::{Path, PathBuf};
use std::{fs, process};

/// Initializes Vento by creating the respective directories it will use
pub fn init() -> Result<()> {
    let ventodir = &common::env_config()?.vento_dir;

    if ventodir.is_dir() {
        // Checks if Vento has already been initialized and prompts the user if they want to initialize it again
        let mut answer = String::new();
        print!("{}{} Vento has already been initialized. Reinitializing will delete all files on the directory for Vento. Do you wish to proceed? (y/N) ", append_emoji(EmojiType::Warning)?, "WARNING:".bold().red());
        let _ = io::stdout().flush();
        io::stdin().read_line(&mut answer)?;
        match answer.as_str().trim() {
            "y" | "Y" => fs::remove_dir_all(ventodir)?,
            _ => process::exit(0),
        };
    };

    create_slots()?;
    Ok(())
}

/// Lists files in the provided slot and/or directory
pub fn list(slot: &str, dir: &str, display_slot: bool) -> Result<()> {
    let ventodir = &common::env_config()?.vento_dir;

    if !ventodir.is_dir() {
        // Detects if Vento hasn't been initialized and bails if so
        throw_error(ErrorType::NotInitialized)?;
    }

    let mut slotdir: PathBuf = match slot {
        "active" | "a" => common::env_config()?.active_dir,
        "inactive" | "i" => common::env_config()?.inactive_dir,
        _ => PathBuf::new(),
    };

    if !dir.is_empty() {
        // Detects if the directory argument is not empty, and if so appends the path provided to the slot directory variable
        slotdir = [&slotdir, &Path::new(dir).to_path_buf()].iter().collect();
    }

    if dir.to_string().contains("..") {
        // Basically preventing from listing anything out of bounds. ls and dir exist for that
        throw_error(ErrorType::NoAccessParent)?;
    }

    if !slotdir.is_dir() {
        // Detects if the consulted slot or directory exists
        bail!(
            "{}",
            format!(
                "No such slot or directory. Valid slots are {} and {}",
                "active".green().bold(),
                "inactive".blue().bold()
            )
            .red()
        );
    };

    if fs::read_dir(&slotdir).unwrap().count() == 0 {
        // Detects if the slot or directory has any contents
        println!(
            "{}{}",
            append_emoji(EmojiType::Inventory)?,
            format!(
                "No files in {}{}",
                if display_slot || !dir.is_empty() {
                    match slot {
                        "active" => slot.bold(),
                        _ => slot.blue().bold(),
                    }
                } else {
                    "inventory".clear()
                },
                if !dir.is_empty() {
                    if cfg!(windows) {
                        format!("\\{}", dir)
                    } else {
                        format!("/{}", dir)
                    }
                } else {
                    "".to_string()
                }
            )
            .green()
        );
    } else {
        println!(
            "{}{}",
            append_emoji(EmojiType::Inventory)?,
            format!(
                "Files in{}{} ({}):",
                if display_slot || !dir.is_empty() {
                    format!(
                        " {}",
                        match slot {
                            "active" => slot.bold(),
                            _ => slot.blue().bold(),
                        },
                    )
                } else {
                    String::new()
                },
                if !dir.is_empty() {
                    if cfg!(windows) {
                        format!("\\{}", dir)
                    } else {
                        format!("/{}", dir)
                    }
                } else {
                    " inventory".to_string()
                },
                format!("{}", fs::read_dir(&slotdir).unwrap().count())
                    .white()
                    .bold()
            )
            .green()
        );
        for file in fs::read_dir(&slotdir).unwrap() {
            let file = file.unwrap().path();

            println!(
                "   - [{}] {}{}",
                if file.clone().is_dir() {
                    "D".blue()
                } else if file.clone().is_symlink() {
                    "S".yellow()
                } else {
                    "F".green()
                },
                file.clone()
                    .file_name()
                    .unwrap()
                    .to_os_string()
                    .into_string()
                    .unwrap(),
                if file.clone().is_file() {
                    format!(
                        " ({}B)",
                        SizeFormatterBinary::new(file.clone().metadata().unwrap().len())
                    )
                } else {
                    String::new()
                }
            );
        }
    }
    Ok(())
}

/// Switches inevntory slots between each other, making the currently active inventory inactive and viceversa
pub fn switch(message: bool) -> Result<()> {
    let ventodir = &common::env_config()?.vento_dir;
    let active = &common::env_config()?.active_dir;
    let inactive = &common::env_config()?.inactive_dir;
    let temp: PathBuf = [ventodir.to_path_buf(), Path::new("temp").to_path_buf()]
        .iter()
        .collect();

    let rename_error = "Vento was unable to switch slots. Try running \"vento -i\" and try again";

    fs::rename(active, &temp).context(rename_error)?;
    fs::rename(inactive, active).context(rename_error)?;
    fs::rename(&temp, inactive).context(rename_error)?;

    common::history(common::HistoryData {
        path: PathBuf::new(),
        file: String::new(),
        slot: String::new(),
        action: common::Action::Switch,
    })?;

    if message {
        println!(
            "{}{}",
            append_emoji(EmojiType::Success)?,
            "Switched inventory slots!".green()
        );
    }
    Ok(())
}

// Used only on init. Creates all required directories
fn create_slots() -> Result<()> {
    let active = &common::env_config()?.active_dir;
    let inactive = &common::env_config()?.inactive_dir;

    fs::create_dir_all(active)?;
    fs::create_dir_all(inactive)?;

    println!(
        "{}{}",
        append_emoji(EmojiType::Celebrate)?,
        "Vento has been succesfully initialized!".green()
    );
    Ok(())
}