mirror of
https://git.sr.ht/~nixgoat/vento
synced 2025-07-28 08:50:53 +00:00
Compare commits
No commits in common. "master" and "v1.1.1" have entirely different histories.
979
Cargo.lock
generated
979
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
23
Cargo.toml
23
Cargo.toml
|
@ -1,12 +1,12 @@
|
|||
[package]
|
||||
name = "vento"
|
||||
version = "1.4.0-alpha"
|
||||
version = "1.1.1"
|
||||
edition = "2021"
|
||||
readme = "README.md"
|
||||
|
||||
description = "A CLI inventory for your files"
|
||||
authors = ["Lux Aliaga <lux@nixgoat.me>"]
|
||||
repository = "https://git.sr.ht/~nixgoat/vento"
|
||||
authors = ["Lux Aliaga <they@mint.lgbt>"]
|
||||
repository = "https://codeberg.org/nixgoat/vento"
|
||||
|
||||
license = "GPL-3.0-or-later"
|
||||
keywords = ["utility", "file-manager", "inventory"]
|
||||
|
@ -16,19 +16,12 @@ build = "build.rs"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
dirs = "5.0"
|
||||
colored = "2"
|
||||
fs_extra = "1.3"
|
||||
anyhow = "1.0"
|
||||
dirs = "4.0.0"
|
||||
colored = "2.0.0"
|
||||
fs_extra = "1.2.0"
|
||||
anyhow = "1.0.65"
|
||||
size_format = "1.0.2"
|
||||
config = "0.14"
|
||||
xz2 = "0.1"
|
||||
tar = "0.4"
|
||||
clap = { version = "4.3.23", features = ["derive"] }
|
||||
serde = "1.0"
|
||||
rusqlite = { version = "0.31.0", features = ["bundled"] }
|
||||
chrono = "0.4"
|
||||
termion = "3.0.0"
|
||||
config = "0.13.1"
|
||||
|
||||
[build-dependencies]
|
||||
man = "0.3.0"
|
||||
|
|
16
README.md
16
README.md
|
@ -1,8 +1,8 @@
|
|||

|
||||

|
||||
|
||||
[](https://crates.io/crates/vento)
|
||||
[](https://crates.io/crates/vento)
|
||||
[](https://git.sr.ht/~nixgoat/vento/tree/master/item/LICENSE.md)
|
||||
[](https://codeberg.org/nixgoat/vento/src/branch/master/LICENSE.md)
|
||||
|
||||
Vento is a utility which allows you to manage your files as if you're playing an old text adventure. It's made in Rust and originally inspired by [Chesapeake's Inventory](https://github.com/mothdotmonster/inventory).
|
||||
|
||||
|
@ -23,13 +23,7 @@ $ cargo install vento
|
|||
Clone the repository using `git`.
|
||||
|
||||
```
|
||||
$ git clone https://git.sr.ht/~nixgoat/vento && cd vento
|
||||
```
|
||||
|
||||
Check out to the latest stable release.
|
||||
|
||||
```
|
||||
$ git checkout v1.3
|
||||
$ git clone https://codeberg.org/nixgoat/vento.git && cd vento
|
||||
```
|
||||
|
||||
### 2.a) cargo-make
|
||||
|
@ -66,7 +60,7 @@ $ vento
|
|||
$ vento -c
|
||||
|
||||
// undoing last action
|
||||
$ vento -u
|
||||
$ vento -c
|
||||
|
||||
// taking a file or directory
|
||||
$ take <file|directory>
|
||||
|
@ -90,4 +84,4 @@ $ man (command)
|
|||
## Credits
|
||||
|
||||
- [Chesapeake](https://moth.monster/) for the original concept
|
||||
- [jo!](https://sr.ht/~j0lol/) for helping me with Rust concepts!
|
||||
- [jo!](https://codeberg.org/j0) for helping me with Rust concepts!
|
||||
|
|
58
build.rs
58
build.rs
|
@ -48,7 +48,7 @@ fn main() -> Result<()> {
|
|||
fn vento() -> Result<Page> {
|
||||
let content = Manual::new("vento")
|
||||
.about("a CLI inventory for your files")
|
||||
.author(Author::new("Lux Aliaga").email("lux@nixgoat.me"))
|
||||
.author(Author::new("Lux Aliaga").email("they@mint.lgbt"))
|
||||
.description("List files and directories in the currently active inventory, the files in SLOT, the files in DIRECTORY or the files in DIRECTORY in SLOT.")
|
||||
.flag(
|
||||
Flag::new()
|
||||
|
@ -60,49 +60,7 @@ fn vento() -> Result<Page> {
|
|||
Flag::new()
|
||||
.short("-u")
|
||||
.long("--undo")
|
||||
.help("Undoes actions by a certain amount of steps"),
|
||||
)
|
||||
.flag(
|
||||
Flag::new()
|
||||
.short("-r")
|
||||
.long("--redo")
|
||||
.help("Redoes actions by a certain amount of steps"),
|
||||
)
|
||||
.flag(
|
||||
Flag::new()
|
||||
.short("-v")
|
||||
.long("--view")
|
||||
.help("Shows log of actions"),
|
||||
)
|
||||
.flag(
|
||||
Flag::new()
|
||||
.short("-m")
|
||||
.long("--migrate")
|
||||
.help("Migrates history file to database"),
|
||||
)
|
||||
.flag(
|
||||
Flag::new()
|
||||
.short("-e")
|
||||
.long("--export-inv")
|
||||
.help("Exports an inventory"),
|
||||
)
|
||||
.flag(
|
||||
Flag::new()
|
||||
.short("-E")
|
||||
.long("--export-dir")
|
||||
.help("Exports the Vento directory"),
|
||||
)
|
||||
.flag(
|
||||
Flag::new()
|
||||
.short("-g")
|
||||
.long("--import-inv")
|
||||
.help("Imports an inventory archive"),
|
||||
)
|
||||
.flag(
|
||||
Flag::new()
|
||||
.short("-G")
|
||||
.long("--import-dir")
|
||||
.help("Imports a Vento directory archive"),
|
||||
.help("Undoes the last action"),
|
||||
)
|
||||
.flag(
|
||||
Flag::new()
|
||||
|
@ -138,7 +96,7 @@ fn vento() -> Result<Page> {
|
|||
fn take() -> Result<Page> {
|
||||
let content = Manual::new("take")
|
||||
.about("a file grabber for Vento")
|
||||
.author(Author::new("Lux Aliaga").email("lux@nixgoat.me"))
|
||||
.author(Author::new("Lux Aliaga").email("they@mint.lgbt"))
|
||||
.description("Take FILE and put it in the inventory.")
|
||||
.option(
|
||||
Opt::new("slot")
|
||||
|
@ -158,7 +116,7 @@ fn take() -> Result<Page> {
|
|||
fn drop() -> Result<Page> {
|
||||
let content = Manual::new("drop")
|
||||
.about("a file dropper for Vento")
|
||||
.author(Author::new("Lux Aliaga").email("lux@nixgoat.me"))
|
||||
.author(Author::new("Lux Aliaga").email("they@mint.lgbt"))
|
||||
.description("Take FILE off the inventory and drop it in DESTINATION.")
|
||||
.option(
|
||||
Opt::new("slot")
|
||||
|
@ -179,15 +137,11 @@ fn drop() -> Result<Page> {
|
|||
fn ventotoml() -> Result<Page> {
|
||||
let content = Manual::new("vento.toml")
|
||||
.about("configuration file for Vento")
|
||||
.author(Author::new("Lux Aliaga").email("lux@nixgoat.me"))
|
||||
.description("This is the configuration file for the vento(1), take(1) and drop(1) utilities. Its presence and all its directives are optional. Directives prefixed with \"name.directive\" indicate a separate section in config file, denoted by brackets.")
|
||||
.author(Author::new("Lux Aliaga").email("they@mint.lgbt"))
|
||||
.description("This is the configuration file for the vento(1), take(1) and drop(1) utilities. Its presence and all its directives are optional.")
|
||||
.custom (
|
||||
Section::new("supported directives")
|
||||
.paragraph("directory = \"PATH\": Changes the path in which Vento's inventories are saved in.")
|
||||
.paragraph("display_emoji = (true | false): Sets whether emojis will be prefixed on messages or not.")
|
||||
.paragraph("display_colors = (true | false): Sets whether messages will be colored.")
|
||||
.paragraph("item.display_dir = (true | false): Sets whether item actions will show the paths involved in the operation.")
|
||||
.paragraph("history.display_dir = (true | false): Sets whether history actions will show the paths involved in the operation.")
|
||||
)
|
||||
.custom (
|
||||
Section::new("files")
|
||||
|
|
133
src/archive.rs
133
src/archive.rs
|
@ -1,133 +0,0 @@
|
|||
/*
|
||||
* Vento, a CLI inventory for your files.
|
||||
* Copyright (C) 2023 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 crate::{
|
||||
common,
|
||||
message::{append_emoji, EmojiType},
|
||||
};
|
||||
use anyhow::Result;
|
||||
use colored::Colorize;
|
||||
use std::{fs::File, path::PathBuf};
|
||||
use tar::Archive;
|
||||
use xz2::read::XzDecoder;
|
||||
use xz2::write::XzEncoder;
|
||||
|
||||
/// Exports an inventory slot into an xz tarball
|
||||
pub fn export_inv(slot: &str, output: PathBuf, message: bool) -> Result<()> {
|
||||
let slotdir: PathBuf = match slot {
|
||||
"active" | "a" => common::env_config()?.active_dir,
|
||||
"inactive" | "i" => common::env_config()?.inactive_dir,
|
||||
_ => PathBuf::new(),
|
||||
};
|
||||
|
||||
let archive = File::create(&output)?;
|
||||
let enc = XzEncoder::new(archive, 9);
|
||||
let mut tar = tar::Builder::new(enc);
|
||||
tar.append_dir_all("", slotdir)?;
|
||||
|
||||
if message {
|
||||
println!(
|
||||
"{}{} {} {} {}",
|
||||
append_emoji(EmojiType::Success)?,
|
||||
"Exported".green(),
|
||||
match slot {
|
||||
"a" | "active" => "active".green(),
|
||||
"i" | "inactive" => "inactive".blue(),
|
||||
_ => slot.red(),
|
||||
}
|
||||
.bold(),
|
||||
"slot into".green(),
|
||||
&output.to_str().unwrap()
|
||||
);
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Exports the Vento directory into an xz tarball
|
||||
pub fn export_dir(output: PathBuf, message: bool) -> Result<()> {
|
||||
let dir: PathBuf = common::env_config()?.vento_dir;
|
||||
|
||||
let archive = File::create(&output)?;
|
||||
let enc = XzEncoder::new(archive, 9);
|
||||
let mut tar = tar::Builder::new(enc);
|
||||
tar.append_dir_all("", dir)?;
|
||||
|
||||
if message {
|
||||
println!(
|
||||
"{}{} {}",
|
||||
append_emoji(EmojiType::Success)?,
|
||||
"Exported Vento directory into".green(),
|
||||
&output.to_str().unwrap()
|
||||
);
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Imports an xz tarball into an inventory slot
|
||||
pub fn import_inv(input: PathBuf, slot: &str, message: bool) -> Result<()> {
|
||||
let slotdir: PathBuf = match slot {
|
||||
"active" | "a" => common::env_config()?.active_dir,
|
||||
"inactive" | "i" => common::env_config()?.inactive_dir,
|
||||
_ => PathBuf::new(),
|
||||
};
|
||||
|
||||
let tar_xz = File::open(&input)?;
|
||||
let tar = XzDecoder::new(tar_xz);
|
||||
let mut archive = Archive::new(tar);
|
||||
archive.unpack(&slotdir)?;
|
||||
|
||||
if message {
|
||||
println!(
|
||||
"{}{} {} {} {} {}",
|
||||
append_emoji(EmojiType::Success)?,
|
||||
"Imported".green(),
|
||||
&input.to_str().unwrap(),
|
||||
"into".green(),
|
||||
match slot {
|
||||
"a" | "active" => "active".green(),
|
||||
"i" | "inactive" => "inactive".blue(),
|
||||
_ => slot.red(),
|
||||
}
|
||||
.bold(),
|
||||
"slot".green()
|
||||
);
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Imports an xz tarball into the Vento directory
|
||||
pub fn import_dir(input: PathBuf, message: bool) -> Result<()> {
|
||||
let dir: PathBuf = common::env_config()?.vento_dir;
|
||||
|
||||
let tar_xz = File::open(&input)?;
|
||||
let tar = XzDecoder::new(tar_xz);
|
||||
let mut archive = Archive::new(tar);
|
||||
archive.unpack(&dir)?;
|
||||
|
||||
if message {
|
||||
println!(
|
||||
"{}{} {} {}",
|
||||
append_emoji(EmojiType::Success)?,
|
||||
"Imported".green(),
|
||||
&input.to_str().unwrap(),
|
||||
"into Vento directory".green(),
|
||||
);
|
||||
};
|
||||
Ok(())
|
||||
}
|
|
@ -17,34 +17,53 @@
|
|||
*
|
||||
*/
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::Parser;
|
||||
use std::path::PathBuf;
|
||||
use vento::{common::get_current_dir, item};
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(name = "Drop")]
|
||||
#[command(about = "A file dropper for Vento", long_about = None)]
|
||||
#[command(author, version)]
|
||||
struct Cli {
|
||||
/// Pick a slot to drop the file from
|
||||
#[arg(short, long)]
|
||||
slot: Option<String>,
|
||||
|
||||
/// File to drop from inventory
|
||||
file: String,
|
||||
/// Location to drop file onto
|
||||
output: Option<PathBuf>,
|
||||
}
|
||||
use anyhow::{bail, Result};
|
||||
use colored::Colorize;
|
||||
use std::env;
|
||||
use std::path::Path;
|
||||
use vento::{help, item};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// Handles args in Drop
|
||||
let cli = Cli::parse();
|
||||
let unwrapped_slot = cli.slot.clone().unwrap_or(String::from("active"));
|
||||
let slot = unwrapped_slot.as_str();
|
||||
let out = cli.output.unwrap_or(get_current_dir()?);
|
||||
|
||||
item::drop(&cli.file, slot, out, true, cli.slot.is_some(), true)?;
|
||||
|
||||
let args: Vec<String> = env::args().collect();
|
||||
if args.len() >= 2 {
|
||||
if args[1].contains("--slot=") {
|
||||
// Checks if the user has provided the long argument "--slot="
|
||||
match args.len() {
|
||||
4 => item::drop(&args[2], &args[1].as_str().replace("--slot=", ""), Path::new(&args[4]).to_path_buf(), true)?,
|
||||
3 => item::drop(&args[2], &args[1].as_str().replace("--slot=", ""), match env::current_dir() {
|
||||
Ok(dir) => dir,
|
||||
Err(_) => bail!("{}", "Vento was unable to detect your current directory. Have you configured your environment correctly?".red())
|
||||
}, true)?,
|
||||
2 => bail!("{}", "You need to specify a file".red()),
|
||||
_ => bail!("{}", "Too many arguments".red()),
|
||||
};
|
||||
} else {
|
||||
match args[1].as_str() {
|
||||
"--help" | "-h" => help::drop()?,
|
||||
"-s" => match args.len() {
|
||||
5 => item::drop(&args[3], &args[2], Path::new(&args[4]).to_path_buf(), true)?,
|
||||
4 => item::drop(&args[3], &args[2], match env::current_dir() {
|
||||
Ok(dir) => dir,
|
||||
Err(_) => bail!("{}", "Vento was unable to detect your current directory. Have you configured your environment correctly?".red())
|
||||
}, true)?,
|
||||
3 => bail!("{}", "You need to specify a file".red()),
|
||||
2 => bail!("{}", "You need to specify a slot".red()),
|
||||
_ => bail!("{}", "Too many arguments".red()),
|
||||
},
|
||||
_ => match args.len() {
|
||||
3 => item::drop(&args[1], &String::from("active"), Path::new(&args[2]).to_path_buf(), true)?,
|
||||
2 => item::drop(&args[1], &String::from("active"), match env::current_dir() {
|
||||
Ok(dir) => dir,
|
||||
Err(_) => bail!("{}", "Vento was unable to detect your current directory. Have you configured your environment correctly?".red())
|
||||
}, true)?,
|
||||
_ => bail!("{}", "Too many arguments".red()),
|
||||
},
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If the user provides no arguments, Drop will display the help message.
|
||||
help::drop()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -17,29 +17,40 @@
|
|||
*
|
||||
*/
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::Parser;
|
||||
use vento::{common::override_color, item};
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(name = "Take")]
|
||||
#[command(about = "A file grabber for Vento", long_about = None)]
|
||||
#[command(author, version)]
|
||||
struct Cli {
|
||||
/// Pick a slot to take the file into
|
||||
#[arg(short, long)]
|
||||
slot: Option<String>,
|
||||
|
||||
/// File to take
|
||||
file: String,
|
||||
}
|
||||
use anyhow::{bail, Result};
|
||||
use colored::Colorize;
|
||||
use std::env;
|
||||
use vento::{help, item};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// Handles args in Vento
|
||||
override_color()?;
|
||||
let cli = Cli::parse();
|
||||
let slot = cli.slot.clone().unwrap_or(String::from("active"));
|
||||
|
||||
item::take(&cli.file, &slot, true, cli.slot.is_some(), true)?;
|
||||
let args: Vec<String> = env::args().collect();
|
||||
if args.len() >= 2 {
|
||||
if args[1].contains("--slot=") {
|
||||
// Checks if the user has provided the long argument "--slot="
|
||||
match args.len() {
|
||||
3 => item::take(&args[2], &args[1].replace("--slot=", ""), true)?,
|
||||
2 => bail!("{}", "You need to specify a file".red()),
|
||||
_ => bail!("{}", "Too many arguments".red()),
|
||||
};
|
||||
} else {
|
||||
match args[1].as_str() {
|
||||
"--help" | "-h" => help::take()?,
|
||||
"-s" => match args.len() {
|
||||
4 => item::take(&args[3], &args[2], true)?,
|
||||
3 => bail!("{}", "You need to specify a file".red()),
|
||||
2 => bail!("{}", "You need to specify a slot".red()),
|
||||
_ => bail!("{}", "Too many arguments".red()),
|
||||
},
|
||||
_ => match args.len() {
|
||||
2 => item::take(&args[1], &String::from("active"), true)?,
|
||||
_ => bail!("{}", "Too many arguments".red()),
|
||||
},
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If the user provides no arguments, Take will display the help message.
|
||||
help::take()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
154
src/bin/vento.rs
154
src/bin/vento.rs
|
@ -17,133 +17,41 @@
|
|||
*
|
||||
*/
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::Parser;
|
||||
use std::path::PathBuf;
|
||||
use vento::{
|
||||
archive,
|
||||
common::override_color,
|
||||
history, inv,
|
||||
message::{throw_error, ErrorType},
|
||||
};
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(name = "Vento")]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
struct Cli {
|
||||
/// Pick slot to list
|
||||
#[arg(short, long)]
|
||||
slot: Option<String>,
|
||||
|
||||
/// Switch slots
|
||||
#[arg(short = 'c', long)]
|
||||
switch: bool,
|
||||
|
||||
/// Undo actions by a certain amount of steps
|
||||
#[arg(short, long, value_name="STEPS", default_missing_value = "1", num_args = ..=1)]
|
||||
undo: Option<usize>,
|
||||
|
||||
/// Redo actions by a certain amount of steps
|
||||
#[arg(short, long, value_name="STEPS", default_missing_value = "1", num_args = ..=1)]
|
||||
redo: Option<usize>,
|
||||
|
||||
/// View log of actions
|
||||
#[arg(short = 'v', long, value_name="LENGTH", default_missing_value = "2", num_args = ..=1)]
|
||||
view: Option<isize>,
|
||||
|
||||
/// Migrate history file to database
|
||||
#[arg(short, long)]
|
||||
migrate: bool,
|
||||
|
||||
/// Export an inventory
|
||||
#[arg(short, long, value_names = &["SLOT", "ARCHIVE"], num_args = ..=2)]
|
||||
export_inv: Option<Vec<String>>,
|
||||
|
||||
/// Export the Vento directory
|
||||
#[arg(short = 'E', long, default_missing_value = "vento.tar.xz", value_name = "ARCHIVE", num_args = ..=1)]
|
||||
export_dir: Option<PathBuf>,
|
||||
|
||||
/// Import an inventory archive
|
||||
#[arg(short = 'g', long, num_args = 1..=2, value_names = &["ARCHIVE", "SLOT"])]
|
||||
import_inv: Option<Vec<String>>,
|
||||
|
||||
/// Import a Vento directory archive
|
||||
#[arg(short = 'G', long, value_name = "ARCHIVE")]
|
||||
import_dir: Option<PathBuf>,
|
||||
|
||||
/// Initialize Vento
|
||||
#[arg(short, long)]
|
||||
init: bool,
|
||||
|
||||
directory: Option<String>,
|
||||
}
|
||||
use anyhow::{bail, Result};
|
||||
use colored::Colorize;
|
||||
use std::env;
|
||||
use vento::{help, history, inv};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
override_color()?;
|
||||
let cli = Cli::parse();
|
||||
let unwrapped_dir = cli.directory.unwrap_or(String::new());
|
||||
let dir = unwrapped_dir.as_str();
|
||||
|
||||
if cli.switch {
|
||||
inv::switch(true, true)?
|
||||
} else if cli.init {
|
||||
inv::init()?
|
||||
} else if cli.undo.is_some() {
|
||||
history::undo(cli.undo.unwrap_or(1))?
|
||||
} else if cli.redo.is_some() {
|
||||
history::redo(cli.redo.unwrap_or(1))?
|
||||
} else if cli.view.is_some() {
|
||||
history::view(cli.view.unwrap_or(2))?
|
||||
} else if cli.migrate {
|
||||
history::migrate()?;
|
||||
} else if cli.export_inv.is_some() {
|
||||
let unwrapped_export_inv = cli.export_inv.unwrap();
|
||||
let export_inv_values = match unwrapped_export_inv.len() {
|
||||
0 => vec![String::from("active"), String::from("active.tar.xz")],
|
||||
_ => unwrapped_export_inv,
|
||||
};
|
||||
|
||||
archive::export_inv(
|
||||
match export_inv_values[0].as_str() {
|
||||
"active" | "inactive" | "a" | "i" => export_inv_values[0].as_str(),
|
||||
_ => "active",
|
||||
},
|
||||
PathBuf::from(match export_inv_values[0].as_str() {
|
||||
"active" | "inactive" | "a" | "i" => export_inv_values[1].as_str(),
|
||||
_ => export_inv_values[0].as_str(),
|
||||
}),
|
||||
true,
|
||||
)?
|
||||
} else if cli.export_dir.is_some() {
|
||||
archive::export_dir(cli.export_dir.unwrap(), true)?
|
||||
} else if cli.import_inv.is_some() {
|
||||
let import_inv_values = &cli
|
||||
.import_inv
|
||||
.unwrap_or(vec![String::new(), String::from("active")]);
|
||||
|
||||
match import_inv_values[0].as_str() {
|
||||
"" | "active" | "inactive" | "a" | "i" => throw_error(ErrorType::SpecifyFile)?,
|
||||
_ => archive::import_inv(
|
||||
PathBuf::from(&import_inv_values[0]),
|
||||
match import_inv_values.len() {
|
||||
2 => match import_inv_values[1].as_str() {
|
||||
"active" | "inactive" | "a" | "i" => import_inv_values[1].as_str(),
|
||||
_ => "active",
|
||||
},
|
||||
_ => "active",
|
||||
// Handles args in Vento
|
||||
let args: Vec<String> = env::args().collect();
|
||||
if args.len() >= 2 {
|
||||
// If the vector for the arguments the command is taking is larger than 2, it most likely means the user has provided an argument
|
||||
if args[1].contains("--slot=") {
|
||||
// Checks if the user has provided the long argument "--slot="
|
||||
match args.len() {
|
||||
3 => inv::list(&args[1].replace("--slot=", ""), &args[2])?,
|
||||
2 => inv::list(&args[1].replace("--slot=", ""), "")?,
|
||||
_ => bail!("{}", "Too many arguments".red()),
|
||||
};
|
||||
} else {
|
||||
match args[1].as_str() {
|
||||
"-h" | "--help" => help::vento()?,
|
||||
"-i" | "--init" => inv::init()?,
|
||||
"-c" | "--switch" => inv::switch(true)?,
|
||||
"-u" | "--undo" => history::undo()?,
|
||||
"-s" => match args.len() {
|
||||
4 => inv::list(&args[2], &args[3])?,
|
||||
3 => inv::list(&args[2], "")?,
|
||||
2 => bail!("{}", "You need to specify a slot".red()),
|
||||
_ => bail!("{}", "Too many arguments".red()),
|
||||
},
|
||||
true,
|
||||
)?,
|
||||
};
|
||||
} else if cli.import_dir.is_some() {
|
||||
archive::import_dir(cli.import_dir.unwrap(), true)?
|
||||
_ => inv::list("active", args[1].as_str())?,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
inv::list(
|
||||
cli.slot.clone().unwrap_or(String::from("active")).as_str(),
|
||||
dir,
|
||||
cli.slot.is_some(),
|
||||
)?
|
||||
// If the user provides no arguments, Vento will display the files in the active slot.
|
||||
inv::list("active", "")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
152
src/common.rs
152
src/common.rs
|
@ -17,15 +17,12 @@
|
|||
*
|
||||
*/
|
||||
|
||||
use crate::message::{throw_error, ErrorType};
|
||||
use anyhow::Result;
|
||||
use colored::control::set_override;
|
||||
use anyhow::{bail, Result};
|
||||
use colored::Colorize;
|
||||
use config::Config;
|
||||
use rusqlite::Connection;
|
||||
use serde::Deserialize;
|
||||
use std::env::current_dir;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
|
||||
pub struct Settings {
|
||||
pub vento_dir: PathBuf,
|
||||
|
@ -33,38 +30,13 @@ pub struct Settings {
|
|||
pub inactive_dir: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct HistoryData {
|
||||
pub id: i32,
|
||||
pub path: Option<PathBuf>,
|
||||
pub file: Option<String>,
|
||||
pub slot: Option<String>,
|
||||
pub path: PathBuf,
|
||||
pub file: String,
|
||||
pub slot: String,
|
||||
pub action: Action,
|
||||
pub time: i64,
|
||||
pub current: i32,
|
||||
}
|
||||
|
||||
pub struct DeserializedConfig {
|
||||
pub directory: String,
|
||||
pub display_dir: bool,
|
||||
pub history_display_dir: bool,
|
||||
pub display_emoji: bool,
|
||||
pub display_colors: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[allow(unused)]
|
||||
struct Item {
|
||||
display_dir: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[allow(unused)]
|
||||
struct History {
|
||||
display_dir: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Action {
|
||||
Take,
|
||||
Drop,
|
||||
|
@ -78,9 +50,9 @@ pub fn env_config() -> Result<Settings> {
|
|||
_ => PathBuf::new(),
|
||||
};
|
||||
if home == PathBuf::new() {
|
||||
throw_error(ErrorType::NoHomeDirectory)?;
|
||||
bail!("{}", "Vento was unable to detect your home folder. Have you configured your environment correctly?".red());
|
||||
};
|
||||
let custom_dir = Path::new(&parse_config()?.directory).to_path_buf();
|
||||
let custom_dir = Path::new(&dir_config()?).to_path_buf();
|
||||
let vento_dir: PathBuf = if custom_dir != PathBuf::new() {
|
||||
Path::new(&custom_dir).to_path_buf()
|
||||
} else {
|
||||
|
@ -101,13 +73,9 @@ pub fn env_config() -> Result<Settings> {
|
|||
})
|
||||
}
|
||||
|
||||
/// Handles reading the config file or variables for Vento.
|
||||
pub fn parse_config() -> Result<DeserializedConfig> {
|
||||
let mut directory = String::new();
|
||||
let mut display_dir = true;
|
||||
let mut history_display_dir = true;
|
||||
let mut display_emoji = true;
|
||||
let mut display_colors = true;
|
||||
fn dir_config() -> Result<String> {
|
||||
// Handles reading the config file or variables for Vento.
|
||||
let mut result = String::new();
|
||||
let mut config = match dirs::config_dir() {
|
||||
Option::Some(dir) => dir,
|
||||
_ => PathBuf::new(),
|
||||
|
@ -123,93 +91,37 @@ pub fn parse_config() -> Result<DeserializedConfig> {
|
|||
.add_source(config::Environment::with_prefix("VENTO"))
|
||||
.build()?;
|
||||
|
||||
directory = match settings.get_string("directory") {
|
||||
result = match settings.get_string("directory") {
|
||||
Ok(value) => value,
|
||||
Err(_) => String::new(),
|
||||
};
|
||||
|
||||
display_dir = settings.get_bool("item.display_dir").unwrap_or(true);
|
||||
history_display_dir = settings.get_bool("history.display_dir").unwrap_or(true);
|
||||
display_emoji = settings.get_bool("display_emoji").unwrap_or(true);
|
||||
display_colors = settings.get_bool("display_colors").unwrap_or(true);
|
||||
}
|
||||
};
|
||||
|
||||
Ok(DeserializedConfig {
|
||||
directory,
|
||||
display_dir,
|
||||
history_display_dir,
|
||||
display_emoji,
|
||||
display_colors,
|
||||
})
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Writes an action into the history database
|
||||
/// Writes an action into the history file
|
||||
pub fn history(data: HistoryData) -> Result<()> {
|
||||
let mut path = env_config()?.vento_dir;
|
||||
path.push("history.db3");
|
||||
let db = Connection::open(path)?;
|
||||
let mut last_path = env_config()?.vento_dir;
|
||||
last_path.push("last");
|
||||
let mut last_file = File::create(last_path)?;
|
||||
|
||||
// Create table if it doesn't exist.
|
||||
db.execute(
|
||||
"CREATE TABLE IF NOT EXISTS history (
|
||||
id INTEGER PRIMARY KEY,
|
||||
path TEXT,
|
||||
file TEXT,
|
||||
slot TEXT,
|
||||
action TEXT NOT NULL,
|
||||
time INTEGER NOT NULL,
|
||||
current INTEGER NOT NULL)",
|
||||
(),
|
||||
)?;
|
||||
|
||||
// Remove future actions
|
||||
let mut current = db.prepare("SELECT id FROM history WHERE current = 1")?;
|
||||
let actions = current.query_map([], |row| row.get(0))?;
|
||||
let lastaction: i64 = actions.last().unwrap_or(Ok(0))?;
|
||||
db.execute("DELETE FROM history WHERE id > ?1", [lastaction])?;
|
||||
|
||||
// Unset current actions
|
||||
db.execute("UPDATE history SET current = 0 WHERE current = 1", ())?;
|
||||
|
||||
// Insert action into table
|
||||
db.execute(
|
||||
"INSERT INTO history (path, file, slot, action, time, current) VALUES (?1, ?2, ?3, ?4, ?5, 1)",
|
||||
(
|
||||
data.path.unwrap_or_default().to_str(),
|
||||
data.file,
|
||||
data.slot,
|
||||
match data.action {
|
||||
Action::Take => "take",
|
||||
Action::Drop => "drop",
|
||||
Action::Switch => "switch",
|
||||
},
|
||||
SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or(Duration::new(0, 0)).as_secs(),
|
||||
),
|
||||
write!(
|
||||
&mut last_file,
|
||||
"{}
|
||||
{}
|
||||
{}
|
||||
{}",
|
||||
data.path.to_str().unwrap(),
|
||||
data.file,
|
||||
data.slot,
|
||||
match data.action {
|
||||
Action::Take => "take",
|
||||
Action::Drop => "drop",
|
||||
Action::Switch => "switch",
|
||||
}
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Gets current directory for commands
|
||||
pub fn get_current_dir() -> Result<PathBuf> {
|
||||
let currentdir = match current_dir() {
|
||||
Ok(dir) => dir,
|
||||
Err(_) => PathBuf::new(),
|
||||
};
|
||||
|
||||
if currentdir == PathBuf::new() {
|
||||
throw_error(ErrorType::NoCurrentDirectory)?;
|
||||
}
|
||||
|
||||
Ok(currentdir)
|
||||
}
|
||||
|
||||
/// Sets color override if display_colors is disabled
|
||||
pub fn override_color() -> Result<()> {
|
||||
if !parse_config()?.display_colors {
|
||||
set_override(false)
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
84
src/help.rs
Normal file
84
src/help.rs
Normal file
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* 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 anyhow::Result;
|
||||
use colored::Colorize;
|
||||
|
||||
/// Displays the help message for the vento command
|
||||
pub fn vento() -> Result<()> {
|
||||
println!(
|
||||
"{}, a CLI inventory for your files
|
||||
© 2022 Lux Aliaga. Licensed under GPLv3
|
||||
|
||||
{}
|
||||
- {}: Lists files in selected inventory
|
||||
- {}: Switches slots
|
||||
- {}: Undoes the last action
|
||||
- {}: Initializes Vento
|
||||
- {}: Displays this message",
|
||||
"Vento".bold().blue(),
|
||||
"Usage:".bold(),
|
||||
"vento [ -s slot | --slot=slot ] [ directory ]"
|
||||
.bold()
|
||||
.green(),
|
||||
"vento ( -c | --switch )".bold().green(),
|
||||
"vento ( -u | --undo )".bold().green(),
|
||||
"vento ( -i | --init )".bold().green(),
|
||||
"vento ( -h | --help )".bold().green()
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Displays the help message for the take command
|
||||
pub fn take() -> Result<()> {
|
||||
println!(
|
||||
"{}, a file grabber for Vento
|
||||
© 2022 Lux Aliaga. Licensed under GPLv3
|
||||
|
||||
{}
|
||||
- {}: Takes a file and saves it in the inventory
|
||||
- {}: Displays this message",
|
||||
"Take".bold().blue(),
|
||||
"Usage:".bold(),
|
||||
"take [ -s slot | --slot=slot ] file | directory"
|
||||
.bold()
|
||||
.green(),
|
||||
"take ( -h | --help )".bold().green()
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Displays the help message for the drop command
|
||||
pub fn drop() -> Result<()> {
|
||||
println!(
|
||||
"{}, a file dropper for Vento
|
||||
© 2022 Lux Aliaga. Licensed under GPLv3
|
||||
|
||||
{}
|
||||
- {}: Takes a file off the inventory and drops it
|
||||
- {}: Displays this message",
|
||||
"Drop".bold().blue(),
|
||||
"Usage:".bold(),
|
||||
"drop [ -s slot | --slot=slot ] file | directory [destination]"
|
||||
.bold()
|
||||
.green(),
|
||||
"drop ( -h | --help )".bold().green()
|
||||
);
|
||||
Ok(())
|
||||
}
|
677
src/history.rs
677
src/history.rs
|
@ -17,629 +17,94 @@
|
|||
*
|
||||
*/
|
||||
|
||||
use crate::{
|
||||
common::{self, env_config, parse_config, Action, HistoryData},
|
||||
inv, item,
|
||||
message::{append_emoji, throw_error, EmojiType, ErrorType},
|
||||
};
|
||||
use anyhow::Result;
|
||||
use chrono::prelude::*;
|
||||
use crate::{common, inv, item};
|
||||
use anyhow::{bail, Result};
|
||||
use colored::Colorize;
|
||||
use rusqlite::Connection;
|
||||
use std::{
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
/// Undoes actions made by Vento using the history database located on the Vento directory
|
||||
pub fn undo(steps: usize) -> Result<()> {
|
||||
let path: PathBuf = [
|
||||
env_config()?.vento_dir,
|
||||
Path::new("history.db3").to_path_buf(),
|
||||
/// Undoes the last action made by Vento using the history file located on the Vento directory
|
||||
pub fn undo() -> Result<()> {
|
||||
let lastpath: PathBuf = [
|
||||
common::env_config()?.vento_dir,
|
||||
Path::new("last").to_path_buf(),
|
||||
]
|
||||
.iter()
|
||||
.collect();
|
||||
let db = Connection::open(path)?;
|
||||
|
||||
// Determine if step amount is greater than the position of the action
|
||||
let mut current = db.prepare("SELECT id FROM history WHERE current = 1")?;
|
||||
let actions = current.query_map([], |row| row.get(0))?;
|
||||
let last_action: usize = actions.last().unwrap_or(Ok(0))?;
|
||||
|
||||
if last_action <= steps {
|
||||
throw_error(ErrorType::InvalidStepsLength)?;
|
||||
}
|
||||
|
||||
let final_dest = last_action - steps;
|
||||
|
||||
// Calculates how many actions need to be undone
|
||||
let mut undo_queue_transaction = db.prepare(
|
||||
"SELECT id, path, file, slot, action FROM history WHERE id > ?2 AND id <= ?1 ORDER BY id DESC",
|
||||
)?;
|
||||
let undo_queue = undo_queue_transaction.query_map([last_action, final_dest], |row| {
|
||||
Ok(HistoryData {
|
||||
id: row.get(0)?,
|
||||
path: Some(PathBuf::from(row.get::<_, String>(1)?)),
|
||||
file: row.get(2)?,
|
||||
slot: row.get(3)?,
|
||||
action: match row.get::<_, String>(4)?.as_str() {
|
||||
"take" => Action::Take,
|
||||
"drop" => Action::Drop,
|
||||
"switch" => Action::Switch,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
time: 0,
|
||||
current: 0,
|
||||
})
|
||||
})?;
|
||||
|
||||
// Undoes actions for each step
|
||||
for raw_step in undo_queue {
|
||||
let step = raw_step?;
|
||||
|
||||
match step.action {
|
||||
Action::Take => {
|
||||
item::drop(
|
||||
&step.file.unwrap(),
|
||||
&step.slot.unwrap(),
|
||||
step.path.unwrap(),
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
)?;
|
||||
}
|
||||
Action::Drop => {
|
||||
let path: String = [
|
||||
String::from(step.path.unwrap().to_str().unwrap()),
|
||||
step.file.unwrap(),
|
||||
]
|
||||
.join("/");
|
||||
item::take(&path, step.slot.unwrap().as_str(), false, false, false)?;
|
||||
}
|
||||
Action::Switch => inv::switch(false, false)?,
|
||||
}
|
||||
|
||||
db.execute("UPDATE history SET current = 0 WHERE current = 1", ())?;
|
||||
db.execute(
|
||||
"UPDATE history SET current = 1 WHERE id = ?1",
|
||||
[step.id - 1],
|
||||
)?;
|
||||
}
|
||||
|
||||
// Prepares to display details of the final position
|
||||
let mut final_transaction = db.prepare("SELECT * FROM history WHERE current = 1")?;
|
||||
let final_action_iter = final_transaction.query_map([], |row| {
|
||||
Ok(HistoryData {
|
||||
id: row.get(0)?,
|
||||
path: Some(PathBuf::from(row.get::<_, String>(1)?)),
|
||||
file: row.get(2)?,
|
||||
slot: row.get(3)?,
|
||||
action: match row.get::<_, String>(4)?.as_str() {
|
||||
"take" => Action::Take,
|
||||
"drop" => Action::Drop,
|
||||
"switch" => Action::Switch,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
time: row.get(5)?,
|
||||
current: row.get::<_, i32>(5)?,
|
||||
})
|
||||
})?;
|
||||
|
||||
let final_action = final_action_iter.last().unwrap()?;
|
||||
|
||||
// Formats the current action's timestamp to readable, local time
|
||||
let timestamp = final_action.time;
|
||||
let naive = NaiveDateTime::from_timestamp_opt(timestamp, 0);
|
||||
let datetime = TimeZone::from_utc_datetime(&Local, &naive.unwrap());
|
||||
let newdate = datetime.format("%Y-%m-%d, %H:%M:%S");
|
||||
|
||||
println!(
|
||||
"{}{}{}{}{}{}",
|
||||
append_emoji(EmojiType::Success)?,
|
||||
"Rolled back to ".green(),
|
||||
match final_action.action {
|
||||
Action::Take => "Take",
|
||||
Action::Drop => "Drop",
|
||||
Action::Switch => "Switch",
|
||||
}
|
||||
.bold(),
|
||||
" action, on ".green(),
|
||||
newdate,
|
||||
match final_action.action {
|
||||
Action::Take => format!(
|
||||
"{}{}{}{}{}{}{}",
|
||||
" (".green(),
|
||||
final_action.file.unwrap().bold(),
|
||||
", ".green(),
|
||||
match parse_config()?.history_display_dir {
|
||||
true => format!(
|
||||
"{} {} ",
|
||||
"from".green(),
|
||||
final_action.path.unwrap().to_str().unwrap(),
|
||||
),
|
||||
_ => String::new(),
|
||||
},
|
||||
"to ".green(),
|
||||
match final_action.slot.clone().unwrap().as_str() {
|
||||
"active" => final_action.slot.unwrap().green(),
|
||||
"inactive" => final_action.slot.unwrap().blue(),
|
||||
_ => final_action.slot.unwrap().red(),
|
||||
}
|
||||
.bold(),
|
||||
" slot)".green(),
|
||||
),
|
||||
Action::Drop => format!(
|
||||
"{}{}{}{}{}{}{}",
|
||||
" (".green(),
|
||||
final_action.file.unwrap().bold(),
|
||||
", from ".green(),
|
||||
match final_action.slot.clone().unwrap().as_str() {
|
||||
"active" => final_action.slot.unwrap().green(),
|
||||
"inactive" => final_action.slot.unwrap().blue(),
|
||||
_ => final_action.slot.unwrap().red(),
|
||||
}
|
||||
.bold(),
|
||||
" slot".green(),
|
||||
match parse_config()?.history_display_dir {
|
||||
true => format!(
|
||||
" {} {}",
|
||||
"to".green(),
|
||||
final_action.path.unwrap().to_str().unwrap(),
|
||||
),
|
||||
false => String::new(),
|
||||
},
|
||||
")".green(),
|
||||
),
|
||||
_ => String::from(""),
|
||||
}
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Redoes actions made by Vento using the history database located on the Vento directory
|
||||
pub fn redo(steps: usize) -> Result<()> {
|
||||
let path: PathBuf = [
|
||||
env_config()?.vento_dir,
|
||||
Path::new("history.db3").to_path_buf(),
|
||||
]
|
||||
.iter()
|
||||
.collect();
|
||||
let db = Connection::open(path)?;
|
||||
|
||||
// Determine if step amount is greater than the position of the action
|
||||
let mut current = db.prepare("SELECT id FROM history WHERE current = 1")?;
|
||||
let actions = current.query_map([], |row| row.get(0))?;
|
||||
let last_action: usize = actions.last().unwrap_or(Ok(0))?;
|
||||
|
||||
// Determine table size
|
||||
let mut size_transaction = db.prepare("SELECT id FROM history")?;
|
||||
let size_actions = size_transaction.query_map([], |row| row.get(0))?;
|
||||
let size: usize = size_actions.last().unwrap_or(Ok(0))?;
|
||||
|
||||
if size - last_action < steps {
|
||||
throw_error(ErrorType::InvalidStepsLength)?;
|
||||
}
|
||||
|
||||
let final_dest = last_action + steps;
|
||||
|
||||
// Calculates how many actions need to be redone
|
||||
let mut redo_queue_transaction = db.prepare(
|
||||
"SELECT id, path, file, slot, action FROM history WHERE id > ?1 AND id <= ?2 ORDER BY id ASC",
|
||||
)?;
|
||||
let redo_queue = redo_queue_transaction.query_map([last_action, final_dest], |row| {
|
||||
Ok(HistoryData {
|
||||
id: row.get(0)?,
|
||||
path: Some(PathBuf::from(row.get::<_, String>(1)?)),
|
||||
file: row.get(2)?,
|
||||
slot: row.get(3)?,
|
||||
action: match row.get::<_, String>(4)?.as_str() {
|
||||
"take" => Action::Take,
|
||||
"drop" => Action::Drop,
|
||||
"switch" => Action::Switch,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
time: 0,
|
||||
current: 0,
|
||||
})
|
||||
})?;
|
||||
|
||||
// Redoes actions for each step
|
||||
for raw_step in redo_queue {
|
||||
let step = raw_step?;
|
||||
|
||||
match step.action {
|
||||
Action::Take => {
|
||||
let path: String = [
|
||||
String::from(step.path.unwrap().to_str().unwrap()),
|
||||
step.file.unwrap(),
|
||||
]
|
||||
.join("/");
|
||||
item::take(&path, step.slot.unwrap().as_str(), false, false, false)?;
|
||||
}
|
||||
Action::Drop => {
|
||||
item::drop(
|
||||
&step.file.unwrap(),
|
||||
&step.slot.unwrap(),
|
||||
step.path.unwrap(),
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
)?;
|
||||
}
|
||||
Action::Switch => inv::switch(false, false)?,
|
||||
}
|
||||
|
||||
db.execute("UPDATE history SET current = 0 WHERE current = 1", ())?;
|
||||
db.execute("UPDATE history SET current = 1 WHERE id = ?1", [step.id])?;
|
||||
}
|
||||
|
||||
// Prepares to display details of the final position
|
||||
let mut final_transaction = db.prepare("SELECT * FROM history WHERE current = 1")?;
|
||||
let final_action_iter = final_transaction.query_map([], |row| {
|
||||
Ok(HistoryData {
|
||||
id: row.get(0)?,
|
||||
path: Some(PathBuf::from(row.get::<_, String>(1)?)),
|
||||
file: row.get(2)?,
|
||||
slot: row.get(3)?,
|
||||
action: match row.get::<_, String>(4)?.as_str() {
|
||||
"take" => Action::Take,
|
||||
"drop" => Action::Drop,
|
||||
"switch" => Action::Switch,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
time: row.get(5)?,
|
||||
current: row.get::<_, i32>(5)?,
|
||||
})
|
||||
})?;
|
||||
|
||||
let final_action = final_action_iter.last().unwrap()?;
|
||||
|
||||
// Formats the current action's timestamp to readable, local time
|
||||
let timestamp = final_action.time;
|
||||
let naive = NaiveDateTime::from_timestamp_opt(timestamp, 0);
|
||||
let datetime = TimeZone::from_utc_datetime(&Local, &naive.unwrap());
|
||||
let newdate = datetime.format("%Y-%m-%d, %H:%M:%S");
|
||||
|
||||
// Prints transaction result
|
||||
println!(
|
||||
"{}{}{}{}{}{}",
|
||||
append_emoji(EmojiType::Success)?,
|
||||
"Returned to ".green(),
|
||||
match final_action.action {
|
||||
Action::Take => "Take",
|
||||
Action::Drop => "Drop",
|
||||
Action::Switch => "Switch",
|
||||
}
|
||||
.bold(),
|
||||
" action, on ".green(),
|
||||
newdate,
|
||||
match final_action.action {
|
||||
Action::Take => format!(
|
||||
"{}{}{}{}{}{}{}",
|
||||
" (".green(),
|
||||
final_action.file.unwrap().bold(),
|
||||
", ".green(),
|
||||
match parse_config()?.history_display_dir {
|
||||
true => format!(
|
||||
"{} {} ",
|
||||
"from".green(),
|
||||
final_action.path.unwrap().to_str().unwrap(),
|
||||
),
|
||||
_ => String::new(),
|
||||
},
|
||||
"to ".green(),
|
||||
match final_action.slot.clone().unwrap().as_str() {
|
||||
"active" => final_action.slot.unwrap().green(),
|
||||
"inactive" => final_action.slot.unwrap().blue(),
|
||||
_ => final_action.slot.unwrap().red(),
|
||||
}
|
||||
.bold(),
|
||||
" slot)".green(),
|
||||
),
|
||||
Action::Drop => format!(
|
||||
"{}{}{}{}{}{}{}",
|
||||
" (".green(),
|
||||
final_action.file.unwrap().bold(),
|
||||
", from ".green(),
|
||||
match final_action.slot.clone().unwrap().as_str() {
|
||||
"active" => final_action.slot.unwrap().green(),
|
||||
"inactive" => final_action.slot.unwrap().blue(),
|
||||
_ => final_action.slot.unwrap().red(),
|
||||
}
|
||||
.bold(),
|
||||
" slot".green(),
|
||||
match parse_config()?.history_display_dir {
|
||||
true => format!(
|
||||
" {} {}",
|
||||
"to".green(),
|
||||
final_action.path.unwrap().to_str().unwrap(),
|
||||
),
|
||||
false => String::new(),
|
||||
},
|
||||
")".green(),
|
||||
),
|
||||
_ => String::from(""),
|
||||
}
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Displays n actions before and after the current action
|
||||
pub fn view(length: isize) -> Result<()> {
|
||||
let path: PathBuf = [
|
||||
env_config()?.vento_dir,
|
||||
Path::new("history.db3").to_path_buf(),
|
||||
]
|
||||
.iter()
|
||||
.collect();
|
||||
let db = Connection::open(path)?;
|
||||
|
||||
// Determine table size
|
||||
let mut size_transaction = db.prepare("SELECT id FROM history")?;
|
||||
let size_actions = size_transaction.query_map([], |row| row.get(0))?;
|
||||
let size: isize = size_actions.last().unwrap_or(Ok(0))?;
|
||||
|
||||
let (x, _) = termion::terminal_size().unwrap();
|
||||
|
||||
// If there's no history, don't print the table
|
||||
if size == 0 {
|
||||
println!(
|
||||
"{}{}",
|
||||
append_emoji(EmojiType::Success)?,
|
||||
"No data to show".green()
|
||||
);
|
||||
}
|
||||
|
||||
// Find last action
|
||||
let mut current = db.prepare("SELECT id FROM history WHERE current = 1")?;
|
||||
let actions = current.query_map([], |row| row.get(0))?;
|
||||
let last_action: isize = actions.last().unwrap_or(Ok(0))?;
|
||||
|
||||
let mut forward: isize = last_action + length;
|
||||
let mut backward: isize = last_action - length;
|
||||
let total_range: isize = length * 2;
|
||||
|
||||
// Changes ranges in case they exceed the table margins
|
||||
if forward >= size {
|
||||
forward = size;
|
||||
backward = size - total_range;
|
||||
} else if backward < 1 {
|
||||
backward = 1;
|
||||
forward = total_range + 1;
|
||||
}
|
||||
|
||||
// Read from table
|
||||
let mut history_transaction =
|
||||
db.prepare("SELECT * FROM history WHERE id >= ?1 AND id <= ?2")?;
|
||||
let history = history_transaction.query_map([backward, forward], |row| {
|
||||
Ok(HistoryData {
|
||||
id: row.get(0)?,
|
||||
path: Some(PathBuf::from(row.get::<_, String>(1)?)),
|
||||
file: row.get(2)?,
|
||||
slot: row.get(3)?,
|
||||
action: match row.get::<_, String>(4)?.as_str() {
|
||||
"take" => Action::Take,
|
||||
"drop" => Action::Drop,
|
||||
"switch" => Action::Switch,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
time: row.get(5)?,
|
||||
current: row.get(6)?,
|
||||
})
|
||||
})?;
|
||||
|
||||
// Terminal needs to be at least 83 columns wide
|
||||
if x < 83 {
|
||||
throw_error(ErrorType::SmallTerminal)?;
|
||||
}
|
||||
|
||||
let mut space_left: usize = (x - 83).into();
|
||||
|
||||
// Append separators to ID
|
||||
let mut id_separators = String::new();
|
||||
if size.to_string().len() > 2 {
|
||||
for _ in 0..size.to_string().len() - 2 {
|
||||
id_separators.insert(id_separators.len(), '-')
|
||||
}
|
||||
space_left = space_left - size.to_string().len() + 2;
|
||||
}
|
||||
|
||||
// Append separators to path column
|
||||
let mut path_separators = String::new();
|
||||
let mut file_separators = String::new();
|
||||
|
||||
// Calculate spaces left to add padding to the path and file separators
|
||||
space_left /= 3;
|
||||
for _ in 0..space_left * 2 {
|
||||
path_separators.insert(path_separators.len(), '-')
|
||||
}
|
||||
for _ in 0..space_left {
|
||||
file_separators.insert(file_separators.len(), '-')
|
||||
}
|
||||
|
||||
let separator = format!(
|
||||
"+----{}+---------------------+--------+------------------{}+----------{}+----------+---+",
|
||||
id_separators, path_separators, file_separators
|
||||
);
|
||||
|
||||
// Render the first column names
|
||||
println!("{}", separator);
|
||||
print!("| ");
|
||||
if size.to_string().len() > 2 {
|
||||
for _ in 0..size.to_string().len() - 2 {
|
||||
print!(" ")
|
||||
}
|
||||
}
|
||||
print!("ID | Date | Action | Path ");
|
||||
for _ in 0..space_left * 2 {
|
||||
print!(" ")
|
||||
}
|
||||
print!("| File ");
|
||||
for _ in 0..space_left {
|
||||
print!(" ")
|
||||
}
|
||||
println!("| Slot | C |\n{}", separator);
|
||||
|
||||
// Print the rows
|
||||
for raw_step in history {
|
||||
let step = raw_step?;
|
||||
|
||||
// Format timestamp on row
|
||||
let timestamp = step.time;
|
||||
let naive = NaiveDateTime::from_timestamp_opt(timestamp, 0);
|
||||
let datetime = TimeZone::from_utc_datetime(&Local, &naive.unwrap());
|
||||
let fdate = datetime.format("%Y-%m-%d %H:%M:%S");
|
||||
|
||||
// Add spacing for ID column
|
||||
let mut id_pad = String::new();
|
||||
let id = step.id.to_string().len();
|
||||
|
||||
if size.to_string().len() >= 2 {
|
||||
let id_pad_len = size.to_string().len();
|
||||
for x in 0..id_pad_len - id {
|
||||
id_pad.insert(x, ' ');
|
||||
}
|
||||
} else {
|
||||
id_pad.insert(0, ' ');
|
||||
}
|
||||
|
||||
// Add spacing to fit inside the file column
|
||||
let file_len = match &step.file {
|
||||
Some(x) => x.len(),
|
||||
None => 0,
|
||||
};
|
||||
let mut file_pad = String::new();
|
||||
let mut file = step.file.unwrap_or(String::from(""));
|
||||
let file_column_len;
|
||||
if file_len > space_left + 8 {
|
||||
file_column_len = 0;
|
||||
let mut reversed: String = file.chars().rev().collect();
|
||||
for _ in 0..file_len - space_left - 5 {
|
||||
reversed.pop();
|
||||
}
|
||||
file = reversed.chars().rev().collect();
|
||||
for x in 0..3 {
|
||||
file.insert(x, '.');
|
||||
}
|
||||
} else {
|
||||
file_column_len = space_left + 8 - file_len
|
||||
}
|
||||
|
||||
for x in 0..file_column_len {
|
||||
file_pad.insert(x, ' ');
|
||||
}
|
||||
|
||||
// Add spacing to fit inside the path column
|
||||
let mut path_pad = String::new();
|
||||
let mut path = step
|
||||
.path
|
||||
.unwrap_or(PathBuf::new())
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
let path_len = path.len();
|
||||
let path_column_len;
|
||||
if path_len > space_left * 2 + 16 {
|
||||
path_column_len = 0;
|
||||
let mut reversed: String = path.chars().rev().collect();
|
||||
for _ in 0..path_len - space_left * 2 - 13 {
|
||||
reversed.pop();
|
||||
}
|
||||
path = reversed.chars().rev().collect();
|
||||
for x in 0..3 {
|
||||
path.insert(x, '.');
|
||||
}
|
||||
} else {
|
||||
path_column_len = space_left * 2 + 16 - path_len;
|
||||
}
|
||||
|
||||
for _ in 0..path_column_len {
|
||||
path_pad.insert(path_pad.len(), ' ');
|
||||
}
|
||||
|
||||
// Add spacing on slot column
|
||||
let mut slot = step.slot.unwrap_or(String::from(""));
|
||||
if slot == "active" {
|
||||
slot = String::from("active ");
|
||||
} else if slot == "inactive" {
|
||||
slot = String::from("inactive");
|
||||
} else {
|
||||
slot = String::from(" ")
|
||||
}
|
||||
|
||||
println!(
|
||||
"| {}{} | {} | {} | {}{} | {}{} | {} | {} |",
|
||||
id_pad,
|
||||
step.id,
|
||||
fdate,
|
||||
match step.action {
|
||||
Action::Take => "Take ",
|
||||
Action::Drop => "Drop ",
|
||||
Action::Switch => "Switch",
|
||||
},
|
||||
path,
|
||||
path_pad,
|
||||
file,
|
||||
file_pad,
|
||||
slot,
|
||||
match step.current {
|
||||
0 => " ",
|
||||
1 => "*",
|
||||
_ => " ",
|
||||
}
|
||||
);
|
||||
}
|
||||
println!("{}", separator);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Migrate old "last" file into the history database
|
||||
pub fn migrate() -> Result<()> {
|
||||
// Get last file from previous location
|
||||
let last_path: PathBuf = [env_config()?.vento_dir, Path::new("last").to_path_buf()]
|
||||
.iter()
|
||||
.collect();
|
||||
|
||||
if !last_path.is_file() {
|
||||
throw_error(ErrorType::NoFileOrDir)?;
|
||||
}
|
||||
|
||||
let last_file = fs::read_to_string(&last_path)?;
|
||||
let lastfile = fs::read_to_string(lastpath)?;
|
||||
|
||||
let mut contents = vec![];
|
||||
|
||||
for line in last_file.lines() {
|
||||
for line in lastfile.lines() {
|
||||
contents.push(line);
|
||||
}
|
||||
|
||||
if contents.len() != 4 {
|
||||
throw_error(ErrorType::InvalidHistoryLength)?;
|
||||
bail!("Invalid history length".red());
|
||||
}
|
||||
|
||||
// Write contents of file into history database
|
||||
common::history(HistoryData {
|
||||
id: 0,
|
||||
path: Some(Path::new(contents[0]).to_path_buf()),
|
||||
file: Some(String::from(contents[1])),
|
||||
slot: Some(String::from(contents[2])),
|
||||
action: match contents[3] {
|
||||
"take" => Action::Take,
|
||||
"drop" => Action::Drop,
|
||||
"switch" => Action::Switch,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
time: 0,
|
||||
current: 1,
|
||||
})?;
|
||||
|
||||
fs::remove_file(last_path)?;
|
||||
match contents[3] {
|
||||
"take" => {
|
||||
let destpath = Path::new(contents[0]).to_path_buf();
|
||||
item::drop(&String::from(contents[1]), contents[2], destpath, false)?;
|
||||
}
|
||||
"drop" => {
|
||||
let path = vec![contents[0], contents[1]].join("/");
|
||||
item::take(&path, contents[2], false)?;
|
||||
}
|
||||
"switch" => {
|
||||
inv::switch(false)?;
|
||||
}
|
||||
_ => bail!("Illegal action".red()),
|
||||
}
|
||||
|
||||
println!(
|
||||
"{}{}",
|
||||
append_emoji(EmojiType::Success)?,
|
||||
"Migrated history file to database".green()
|
||||
"✅ {}",
|
||||
format!(
|
||||
"{}{}{}",
|
||||
match contents[3] {
|
||||
"take" => "Take",
|
||||
"drop" => "Drop",
|
||||
"switch" => "Switch",
|
||||
_ => "Unknown",
|
||||
}
|
||||
.bold(),
|
||||
" action undone".green(),
|
||||
match contents[3] {
|
||||
"take" => format!(
|
||||
"{}{}{}{}{}{}{}",
|
||||
" (".green(),
|
||||
contents[1].bold(),
|
||||
", from ".green(),
|
||||
contents[0],
|
||||
" to ".green(),
|
||||
match contents[2] {
|
||||
"active" => contents[2].green(),
|
||||
"inactive" => contents[2].blue(),
|
||||
_ => contents[2].red(),
|
||||
}
|
||||
.bold(),
|
||||
" slot)".green(),
|
||||
),
|
||||
"drop" => format!(
|
||||
"{}{}{}{}{}{}{}",
|
||||
" (".green(),
|
||||
contents[1].bold(),
|
||||
", from ".green(),
|
||||
match contents[2] {
|
||||
"active" => contents[2].green(),
|
||||
"inactive" => contents[2].blue(),
|
||||
_ => contents[2].red(),
|
||||
}
|
||||
.bold(),
|
||||
" slot to ".green(),
|
||||
contents[0],
|
||||
")".green(),
|
||||
),
|
||||
_ => String::from(""),
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
Ok(())
|
||||
|
|
86
src/inv.rs
86
src/inv.rs
|
@ -17,10 +17,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
use super::{
|
||||
common,
|
||||
message::{append_emoji, throw_error, EmojiType, ErrorType},
|
||||
};
|
||||
use super::common;
|
||||
use anyhow::{bail, Context, Result};
|
||||
use colored::Colorize;
|
||||
use size_format::SizeFormatterBinary;
|
||||
|
@ -35,11 +32,11 @@ pub fn init() -> Result<()> {
|
|||
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());
|
||||
print!("⚠️ {} Vento has already been initialized. Reinitializing will delete all files on the directory for Vento. Do you wish to proceed? (y/N) ", "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)?,
|
||||
"y" | "Y" => fs::remove_dir_all(&ventodir)?,
|
||||
_ => process::exit(0),
|
||||
};
|
||||
};
|
||||
|
@ -49,12 +46,15 @@ pub fn init() -> Result<()> {
|
|||
}
|
||||
|
||||
/// Lists files in the provided slot and/or directory
|
||||
pub fn list(slot: &str, dir: &str, display_slot: bool) -> Result<()> {
|
||||
pub fn list(slot: &str, dir: &str) -> 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)?;
|
||||
bail!(
|
||||
"{}",
|
||||
"Vento not initialized. Run \"vento -i\" to initialize Vento".red()
|
||||
);
|
||||
}
|
||||
|
||||
let mut slotdir: PathBuf = match slot {
|
||||
|
@ -70,7 +70,7 @@ pub fn list(slot: &str, dir: &str, display_slot: bool) -> Result<()> {
|
|||
|
||||
if dir.to_string().contains("..") {
|
||||
// Basically preventing from listing anything out of bounds. ls and dir exist for that
|
||||
throw_error(ErrorType::NoAccessParent)?;
|
||||
bail!("{}", "Cannot access parent".red());
|
||||
}
|
||||
|
||||
if !slotdir.is_dir() {
|
||||
|
@ -89,17 +89,12 @@ pub fn list(slot: &str, dir: &str, display_slot: bool) -> Result<()> {
|
|||
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()
|
||||
match slot {
|
||||
"active" => slot.bold(),
|
||||
_ => slot.blue().bold(),
|
||||
},
|
||||
if !dir.is_empty() {
|
||||
if cfg!(windows) {
|
||||
|
@ -115,20 +110,12 @@ pub fn list(slot: &str, dir: &str, display_slot: bool) -> Result<()> {
|
|||
);
|
||||
} 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()
|
||||
"Files in {}{} ({}):",
|
||||
match slot {
|
||||
"active" => slot.bold(),
|
||||
_ => slot.blue().bold(),
|
||||
},
|
||||
if !dir.is_empty() {
|
||||
if cfg!(windows) {
|
||||
|
@ -178,7 +165,7 @@ pub fn list(slot: &str, dir: &str, display_slot: bool) -> Result<()> {
|
|||
}
|
||||
|
||||
/// Switches inevntory slots between each other, making the currently active inventory inactive and viceversa
|
||||
pub fn switch(message: bool, save_history: bool) -> Result<()> {
|
||||
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;
|
||||
|
@ -188,28 +175,19 @@ pub fn switch(message: bool, save_history: bool) -> Result<()> {
|
|||
|
||||
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)?;
|
||||
fs::rename(&active, &temp).context(rename_error)?;
|
||||
fs::rename(&inactive, &active).context(rename_error)?;
|
||||
fs::rename(&temp, &inactive).context(rename_error)?;
|
||||
|
||||
if save_history {
|
||||
common::history(common::HistoryData {
|
||||
id: 0,
|
||||
path: None,
|
||||
file: None,
|
||||
slot: None,
|
||||
action: common::Action::Switch,
|
||||
current: 1,
|
||||
time: 0,
|
||||
})?;
|
||||
}
|
||||
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()
|
||||
);
|
||||
println!("✅ {}", "Switched inventory slots!".green());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -222,10 +200,6 @@ fn create_slots() -> Result<()> {
|
|||
fs::create_dir_all(active)?;
|
||||
fs::create_dir_all(inactive)?;
|
||||
|
||||
println!(
|
||||
"{}{}",
|
||||
append_emoji(EmojiType::Celebrate)?,
|
||||
"Vento has been succesfully initialized!".green()
|
||||
);
|
||||
println!("🎉 {}", "Vento has been succesfully initialized!".green());
|
||||
Ok(())
|
||||
}
|
||||
|
|
147
src/item.rs
147
src/item.rs
|
@ -17,10 +17,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
use super::{
|
||||
common::{env_config, history, parse_config, Action, HistoryData},
|
||||
message::{append_emoji, throw_error, EmojiType, ErrorType},
|
||||
};
|
||||
use super::common;
|
||||
use anyhow::{bail, Result};
|
||||
use colored::Colorize;
|
||||
use fs_extra::dir::{move_dir, CopyOptions};
|
||||
|
@ -28,22 +25,19 @@ use std::fs;
|
|||
use std::path::{Path, PathBuf};
|
||||
|
||||
/// Takes a file or directory and stores it in an inventory slot
|
||||
pub fn take(
|
||||
file: &String,
|
||||
slot: &str,
|
||||
message: bool,
|
||||
display_slot: bool,
|
||||
save_history: bool,
|
||||
) -> Result<()> {
|
||||
let ventodir = &env_config()?.vento_dir;
|
||||
pub fn take(file: &String, slot: &str, message: 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)?;
|
||||
bail!(
|
||||
"{}",
|
||||
"Vento not initialized. Run \"vento -i\" to initialize Vento".red()
|
||||
);
|
||||
};
|
||||
let slotdir: PathBuf = match slot {
|
||||
"active" | "a" => env_config()?.active_dir,
|
||||
"inactive" | "i" => env_config()?.inactive_dir,
|
||||
"active" | "a" => common::env_config()?.active_dir,
|
||||
"inactive" | "i" => common::env_config()?.inactive_dir,
|
||||
_ => PathBuf::new(),
|
||||
};
|
||||
|
||||
|
@ -70,59 +64,36 @@ pub fn take(
|
|||
|
||||
if Path::exists(&destpath) {
|
||||
// Checks if there's a file with the same name in the inventory.
|
||||
throw_error(ErrorType::ExistsInventory)?;
|
||||
bail!(
|
||||
"{}",
|
||||
"A file with the same name already exists in your inventory!".red()
|
||||
);
|
||||
}
|
||||
|
||||
if sourcepath.is_file() | sourcepath.is_symlink() {
|
||||
// Checks the path's file type
|
||||
fs::copy(file, &destpath)?;
|
||||
fs::remove_file(file)?;
|
||||
fs::copy(&file, &destpath)?;
|
||||
fs::remove_file(&file)?;
|
||||
} else if sourcepath.is_dir() {
|
||||
let options = CopyOptions::new();
|
||||
move_dir(file, &slotdir, &options)?;
|
||||
move_dir(&file, &slotdir, &options)?;
|
||||
} else {
|
||||
throw_error(ErrorType::NoFileOrDir)?;
|
||||
bail!("{}", "No such file or directory".red());
|
||||
}
|
||||
|
||||
if save_history {
|
||||
history(HistoryData {
|
||||
id: 0,
|
||||
path: Some(sourcelocation.clone()),
|
||||
file: Some(String::from(filename)),
|
||||
slot: Some(String::from(slot)),
|
||||
action: Action::Take,
|
||||
current: 1,
|
||||
time: 0,
|
||||
})?;
|
||||
}
|
||||
common::history(common::HistoryData {
|
||||
path: sourcelocation.clone(),
|
||||
file: String::from(filename),
|
||||
slot: String::from(slot),
|
||||
action: common::Action::Take,
|
||||
})?;
|
||||
|
||||
if message {
|
||||
println!(
|
||||
"{}{} {}{}{}",
|
||||
append_emoji(EmojiType::Success)?,
|
||||
"✅ {} {} {} ",
|
||||
"Took".green(),
|
||||
&filename.bold(),
|
||||
match parse_config()?.display_dir {
|
||||
true => format! {"{} {}",
|
||||
" from".green(),
|
||||
&sourcelocation.to_str().unwrap(),
|
||||
},
|
||||
_ => String::new(),
|
||||
},
|
||||
match display_slot {
|
||||
true => format!(
|
||||
"{} {} {}",
|
||||
" to".green(),
|
||||
match slot {
|
||||
"active" => slot.green(),
|
||||
"inactive" => slot.blue(),
|
||||
_ => slot.red(),
|
||||
}
|
||||
.bold(),
|
||||
"slot".green()
|
||||
),
|
||||
_ => String::new(),
|
||||
},
|
||||
format!("from {}", &sourcelocation.to_str().unwrap()).green()
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -130,25 +101,21 @@ pub fn take(
|
|||
}
|
||||
|
||||
/// Drops a file or directory and stores it in an inventory slot
|
||||
pub fn drop(
|
||||
file: &String,
|
||||
slot: &str,
|
||||
dest: PathBuf,
|
||||
message: bool,
|
||||
display_slot: bool,
|
||||
save_history: bool,
|
||||
) -> Result<()> {
|
||||
pub fn drop(file: &String, slot: &str, dest: PathBuf, message: bool) -> Result<()> {
|
||||
// Drops a file or directory
|
||||
let ventodir = &env_config()?.vento_dir;
|
||||
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)?;
|
||||
bail!(
|
||||
"{}",
|
||||
"Vento not initialized. Run \"vento -i\" to initialize Vento".red()
|
||||
);
|
||||
};
|
||||
|
||||
let slotdir: PathBuf = match slot {
|
||||
"active" | "a" => env_config()?.active_dir,
|
||||
"inactive" | "i" => env_config()?.inactive_dir,
|
||||
"active" | "a" => common::env_config()?.active_dir,
|
||||
"inactive" | "i" => common::env_config()?.inactive_dir,
|
||||
_ => PathBuf::new(),
|
||||
};
|
||||
|
||||
|
@ -175,7 +142,7 @@ pub fn drop(
|
|||
|
||||
if Path::exists(&destpath) {
|
||||
// Checks if there's a file with the same name in the destination path.
|
||||
throw_error(ErrorType::ExistsDestination)?;
|
||||
bail!("{}", "A file with the same name already exists in the destination! Try renaming it or dropping this file somewhere else".red());
|
||||
}
|
||||
|
||||
if sourcepath.is_file() | sourcepath.is_symlink() {
|
||||
|
@ -185,52 +152,26 @@ pub fn drop(
|
|||
} else if sourcepath.is_dir() {
|
||||
let destpath: PathBuf = Path::new(&dest).to_path_buf();
|
||||
let options = CopyOptions::new();
|
||||
move_dir(&sourcepath, destpath, &options)?;
|
||||
move_dir(&sourcepath, &destpath, &options)?;
|
||||
} else {
|
||||
throw_error(ErrorType::NoFileOrDir)?;
|
||||
bail!("{}", "No such file or directory".red());
|
||||
}
|
||||
|
||||
destpath.pop();
|
||||
|
||||
if save_history {
|
||||
history(HistoryData {
|
||||
id: 0,
|
||||
path: Some(destpath.clone()),
|
||||
file: Some(String::from(file)),
|
||||
slot: Some(String::from(slot)),
|
||||
action: Action::Drop,
|
||||
current: 1,
|
||||
time: 0,
|
||||
})?;
|
||||
}
|
||||
common::history(common::HistoryData {
|
||||
path: destpath.clone(),
|
||||
file: String::from(file),
|
||||
slot: String::from(slot),
|
||||
action: common::Action::Drop,
|
||||
})?;
|
||||
|
||||
if message {
|
||||
println!(
|
||||
"{}{} {}{}{}",
|
||||
append_emoji(EmojiType::Success)?,
|
||||
"✅ {} {} {} ",
|
||||
"Dropped".green(),
|
||||
&file.bold(),
|
||||
match display_slot {
|
||||
true => format!(
|
||||
"{} {} {}",
|
||||
" from".green(),
|
||||
match slot {
|
||||
"active" => slot.green(),
|
||||
"inactive" => slot.blue(),
|
||||
_ => slot.red(),
|
||||
}
|
||||
.bold(),
|
||||
"slot".green(),
|
||||
),
|
||||
false => String::new(),
|
||||
},
|
||||
match parse_config()?.display_dir {
|
||||
true => format! {"{} {} ",
|
||||
" into".green(),
|
||||
&destpath.to_str().unwrap(),
|
||||
},
|
||||
_ => String::new(),
|
||||
},
|
||||
format!("into {}", &destpath.to_str().unwrap()).green()
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -17,9 +17,8 @@
|
|||
*
|
||||
*/
|
||||
|
||||
pub mod archive;
|
||||
pub mod common;
|
||||
mod common;
|
||||
pub mod help;
|
||||
pub mod history;
|
||||
pub mod inv;
|
||||
pub mod item;
|
||||
pub mod message;
|
||||
|
|
|
@ -1,85 +0,0 @@
|
|||
/*
|
||||
* Vento, a CLI inventory for your files.
|
||||
* Copyright (C) 2023 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 crate::common::parse_config;
|
||||
use anyhow::{bail, Result};
|
||||
use colored::Colorize;
|
||||
|
||||
pub enum ErrorType {
|
||||
TooManyArgs,
|
||||
SpecifySlot,
|
||||
SpecifyFile,
|
||||
NoCurrentDirectory,
|
||||
NoHomeDirectory,
|
||||
InvalidHistoryLength,
|
||||
InvalidStepsLength,
|
||||
SmallTerminal,
|
||||
IllegalAction,
|
||||
NotInitialized,
|
||||
NoAccessParent,
|
||||
ExistsInventory,
|
||||
ExistsDestination,
|
||||
NoFileOrDir,
|
||||
}
|
||||
|
||||
pub enum EmojiType {
|
||||
Celebrate,
|
||||
Success,
|
||||
Warning,
|
||||
Inventory,
|
||||
}
|
||||
|
||||
pub fn append_emoji(message: EmojiType) -> Result<String> {
|
||||
let mut output: String = String::new();
|
||||
|
||||
if parse_config()?.display_emoji {
|
||||
match message {
|
||||
EmojiType::Celebrate => output = String::from("🎉 "),
|
||||
EmojiType::Success => output = String::from("✅ "),
|
||||
EmojiType::Inventory => output = String::from("🗃️ "),
|
||||
EmojiType::Warning => output = String::from("⚠️ "),
|
||||
};
|
||||
}
|
||||
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
/// Displays an error and exits
|
||||
pub fn throw_error(error: ErrorType) -> Result<()> {
|
||||
bail!(
|
||||
"{}",
|
||||
match error {
|
||||
ErrorType::TooManyArgs => "Too many arguments",
|
||||
ErrorType::SpecifyFile => "You need to specify a file",
|
||||
ErrorType::SpecifySlot => "You need to specify a slot",
|
||||
ErrorType::NoCurrentDirectory => "Vento was unable to detect your current directory. Have you configured your environment correctly?",
|
||||
ErrorType::NoHomeDirectory => "Vento was unable to detect your home directory. Have you configured your environment correctly?",
|
||||
ErrorType::InvalidHistoryLength => "Invalid history length",
|
||||
ErrorType::InvalidStepsLength => "Invalid steps length",
|
||||
ErrorType::SmallTerminal => "Your terminal needs to be at least 83 columns wide",
|
||||
ErrorType::IllegalAction => "Illegal action",
|
||||
ErrorType::NotInitialized => "Vento not initialized. Run \"vento -i\" to initialize Vento",
|
||||
ErrorType::NoAccessParent => "Cannot access parent",
|
||||
ErrorType::ExistsInventory => "A file with the same name already exists in your inventory!",
|
||||
ErrorType::ExistsDestination => "A file with the same name already exists in the destination! Try renaming it or dropping this file somewhere else",
|
||||
ErrorType::NoFileOrDir => "No such file or directory",
|
||||
}
|
||||
.red()
|
||||
);
|
||||
}
|
Loading…
Reference in a new issue