1
0
Fork 0
mirror of https://git.sr.ht/~nixgoat/vento synced 2025-12-07 05:01:43 +00:00

history: Implement new database system

The new database system uses SQLite3, and allows users to move across
a linear history of their actions, including moving forward from a
previous action. Whenever a user runs a new action, the actions after
the current position within the history will be removed.
This commit is contained in:
Lux Aliaga 2024-02-17 01:25:50 -03:00
parent 92b2942471
commit 600c3e4f3a
Signed by: lux
GPG key ID: 5A400026ED2F57FD
10 changed files with 474 additions and 125 deletions

81
Cargo.lock generated
View file

@ -108,9 +108,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]] [[package]]
name = "base64" name = "base64"
version = "0.13.0" version = "0.21.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
@ -123,6 +123,9 @@ name = "bitflags"
version = "2.4.2" version = "2.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "block-buffer" name = "block-buffer"
@ -223,11 +226,12 @@ dependencies = [
[[package]] [[package]]
name = "config" name = "config"
version = "0.13.2" version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11f1667b8320afa80d69d8bbe40830df2c8a06003d86f73d8e003b2c48df416d" checksum = "7328b20597b53c2454f0b1919720c25c7339051c02b72b7e05409e00b14132be"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"convert_case",
"json5", "json5",
"lazy_static", "lazy_static",
"nom", "nom",
@ -284,6 +288,12 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "crunchy"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
[[package]] [[package]]
name = "crypto-common" name = "crypto-common"
version = "0.1.6" version = "0.1.6"
@ -327,9 +337,18 @@ dependencies = [
[[package]] [[package]]
name = "dlv-list" name = "dlv-list"
version = "0.3.0" version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257" checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f"
dependencies = [
"const-random",
]
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]] [[package]]
name = "errno" name = "errno"
@ -403,7 +422,7 @@ dependencies = [
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.12.3" version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
@ -679,9 +698,9 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]] [[package]]
name = "ordered-multimap" name = "ordered-multimap"
version = "0.4.3" version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a" checksum = "4ed8acf08e98e744e5384c8bc63ceb0364e68a6854187221c18df61c4797690e"
dependencies = [ dependencies = [
"dlv-list", "dlv-list",
"hashbrown 0.13.2", "hashbrown 0.13.2",
@ -796,12 +815,11 @@ checksum = "e33e4fb37ba46888052c763e4ec2acfedd8f00f62897b630cadb6298b833675e"
[[package]] [[package]]
name = "ron" name = "ron"
version = "0.7.1" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88073939a61e5b7680558e6be56b419e208420c2adb92be54921fa6b72283f1a" checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94"
dependencies = [ dependencies = [
"base64", "base64",
"bitflags 1.3.2",
"bitflags 2.4.2", "bitflags 2.4.2",
"serde", "serde",
"serde_derive", "serde_derive",
@ -823,9 +841,9 @@ dependencies = [
[[package]] [[package]]
name = "rust-ini" name = "rust-ini"
version = "0.18.0" version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df" checksum = "7e2a3bcec1f113553ef1c88aae6c020a369d03d55b58de9869a0908930385091"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"ordered-multimap", "ordered-multimap",
@ -988,11 +1006,36 @@ dependencies = [
[[package]] [[package]]
name = "toml" name = "toml"
version = "0.5.9" version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" checksum = "9a9aad4a3066010876e8dcf5a8a06e70a558751117a145c6ce2b82c2e2054290"
dependencies = [ dependencies = [
"serde", "serde",
"serde_spanned",
"toml_datetime",
"toml_edit",
]
[[package]]
name = "toml_datetime"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.22.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c1b5fd4128cc8d3e0cb74d4ed9a9cc7c7284becd4df68f5f940e1ad123606f6"
dependencies = [
"indexmap",
"serde",
"serde_spanned",
"toml_datetime",
"winnow",
] ]
[[package]] [[package]]
@ -1013,6 +1056,12 @@ version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "unicode-segmentation"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
[[package]] [[package]]
name = "utf8parse" name = "utf8parse"
version = "0.2.1" version = "0.2.1"

View file

@ -21,7 +21,7 @@ colored = "2"
fs_extra = "1.3" fs_extra = "1.3"
anyhow = "1.0" anyhow = "1.0"
size_format = "1.0.2" size_format = "1.0.2"
config = "0.13" config = "0.14"
xz2 = "0.1" xz2 = "0.1"
tar = "0.4" tar = "0.4"
clap = { version = "4.3.23", features = ["derive"] } clap = { version = "4.3.23", features = ["derive"] }

View file

@ -44,7 +44,8 @@ fn main() -> Result<()> {
let slot = unwrapped_slot.as_str(); let slot = unwrapped_slot.as_str();
let out = cli.output.unwrap_or(get_current_dir()?); let out = cli.output.unwrap_or(get_current_dir()?);
item::drop(&cli.file, slot, out, true, cli.slot.is_some())?; item::drop(&cli.file, slot, out, true, cli.slot.is_some(), true)?;
Ok(()) Ok(())
} }

View file

@ -40,6 +40,6 @@ fn main() -> Result<()> {
let cli = Cli::parse(); let cli = Cli::parse();
let slot = cli.slot.clone().unwrap_or(String::from("active")); let slot = cli.slot.clone().unwrap_or(String::from("active"));
item::take(&cli.file, &slot, true, cli.slot.is_some())?; item::take(&cli.file, &slot, true, cli.slot.is_some(), true)?;
Ok(()) Ok(())
} }

View file

@ -39,9 +39,13 @@ struct Cli {
#[arg(short = 'c', long)] #[arg(short = 'c', long)]
switch: bool, switch: bool,
/// Undo the last action /// Undo actions by a certain amount of steps
#[arg(short, long)] #[arg(short, long, value_name="STEPS", default_missing_value = "1", num_args = ..=1)]
undo: bool, 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>,
/// Export an inventory /// Export an inventory
#[arg(short, long, value_names = &["SLOT", "ARCHIVE"], num_args = ..=2)] #[arg(short, long, value_names = &["SLOT", "ARCHIVE"], num_args = ..=2)]
@ -73,11 +77,19 @@ fn main() -> Result<()> {
let dir = unwrapped_dir.as_str(); let dir = unwrapped_dir.as_str();
if cli.switch { if cli.switch {
inv::switch(true)? inv::switch(true, true)?
} else if cli.undo {
history::undo()?
} else if cli.init { } else if cli.init {
inv::init()? inv::init()?
} else if cli.undo.is_some() {
history::undo(match cli.undo {
Some(x) => x,
None => 1,
})?
} else if cli.redo.is_some() {
history::redo(match cli.redo {
Some(x) => x,
None => 1,
})?
} else if cli.export_inv.is_some() { } else if cli.export_inv.is_some() {
let unwrapped_export_inv = cli.export_inv.unwrap(); let unwrapped_export_inv = cli.export_inv.unwrap();
let export_inv_values = match unwrapped_export_inv.len() { let export_inv_values = match unwrapped_export_inv.len() {

View file

@ -21,11 +21,11 @@ use crate::message::{throw_error, ErrorType};
use anyhow::Result; use anyhow::Result;
use colored::control::set_override; use colored::control::set_override;
use config::Config; use config::Config;
use rusqlite::Connection;
use serde::Deserialize; use serde::Deserialize;
use std::env::current_dir; use std::env::current_dir;
use std::fs::File;
use std::io::Write;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
pub struct Settings { pub struct Settings {
pub vento_dir: PathBuf, pub vento_dir: PathBuf,
@ -33,11 +33,15 @@ pub struct Settings {
pub inactive_dir: PathBuf, pub inactive_dir: PathBuf,
} }
#[derive(Debug)]
pub struct HistoryData { pub struct HistoryData {
pub path: PathBuf, pub id: i32,
pub file: String, pub path: Option<PathBuf>,
pub slot: String, pub file: Option<String>,
pub slot: Option<String>,
pub action: Action, pub action: Action,
pub time: i64,
pub current: i32,
} }
pub struct DeserializedConfig { pub struct DeserializedConfig {
@ -60,6 +64,7 @@ struct History {
display_dir: bool, display_dir: bool,
} }
#[derive(Debug)]
pub enum Action { pub enum Action {
Take, Take,
Drop, Drop,
@ -139,26 +144,48 @@ pub fn parse_config() -> Result<DeserializedConfig> {
}) })
} }
/// Writes an action into the history file /// Writes an action into the history database
pub fn history(data: HistoryData) -> Result<()> { pub fn history(data: HistoryData) -> Result<()> {
let mut last_path = env_config()?.vento_dir; let mut path = env_config()?.vento_dir;
last_path.push("last"); path.push("history.db3");
let mut last_file = File::create(last_path)?; let db = Connection::open(path)?;
write!( // Create table if it doesn't exist.
&mut last_file, db.execute(
"{} "CREATE TABLE IF NOT EXISTS history (
{} id INTEGER PRIMARY KEY,
{} path TEXT,
{}", file TEXT,
data.path.to_str().unwrap(), slot TEXT,
data.file, action TEXT NOT NULL,
data.slot, time INTEGER NOT NULL,
match data.action { current INTEGER NOT NULL)",
Action::Take => "take", (),
Action::Drop => "drop", )?;
Action::Switch => "switch",
} // Remove future actions
let mut current = db.prepare("SELECT id FROM history WHERE current = 1")?;
let actions = current.query_map([], |row| Ok(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(PathBuf::new()).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(),
),
)?; )?;
Ok(()) Ok(())

View file

@ -18,98 +18,171 @@
*/ */
use crate::{ use crate::{
common::{env_config, parse_config}, common::{env_config, parse_config, Action, HistoryData},
inv, item, inv, item,
message::{append_emoji, throw_error, EmojiType, ErrorType}, message::{append_emoji, throw_error, EmojiType, ErrorType},
}; };
use anyhow::Result; use anyhow::Result;
use chrono::prelude::*;
use colored::Colorize; use colored::Colorize;
use std::fs; use rusqlite::Connection;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
/// Undoes the last action made by Vento using the history file located on the Vento directory /// Undoes actions made by Vento using the history database located on the Vento directory
pub fn undo() -> Result<()> { pub fn undo(steps: usize) -> Result<()> {
let lastpath: PathBuf = [env_config()?.vento_dir, Path::new("last").to_path_buf()] let path: PathBuf = [
.iter() env_config()?.vento_dir,
.collect(); Path::new("history.db3").to_path_buf(),
]
.iter()
.collect();
let db = Connection::open(path)?;
let lastfile = fs::read_to_string(lastpath)?; // 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| Ok(row.get(0)?))?;
let last_action: usize = actions.last().unwrap_or(Ok(0))?;
let mut contents = vec![]; if last_action <= steps {
throw_error(ErrorType::InvalidStepsLength)?;
for line in lastfile.lines() {
contents.push(line);
} }
if contents.len() != 4 { let final_dest = last_action - steps;
throw_error(ErrorType::InvalidHistoryLength)?;
// 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 = vec![
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],
)?;
} }
match contents[3] { // Prepares to display details of the final position
"take" => { let mut final_transaction = db.prepare("SELECT * FROM history WHERE current = 1")?;
let destpath = Path::new(contents[0]).to_path_buf(); let final_action_iter = final_transaction.query_map([], |row| {
item::drop( Ok(HistoryData {
&String::from(contents[1]), id: row.get(0)?,
contents[2], path: Some(PathBuf::from(row.get::<_, String>(1)?)),
destpath, file: row.get(2)?,
false, slot: row.get(3)?,
false, action: match row.get::<_, String>(4)?.as_str() {
)?; "take" => Action::Take,
} "drop" => Action::Drop,
"drop" => { "switch" => Action::Switch,
let path = vec![contents[0], contents[1]].join("/"); _ => unreachable!(),
item::take(&path, contents[2], false, false)?; },
} time: row.get(5)?,
"switch" => { current: row.get::<_, i32>(5)?,
inv::switch(false)?; })
} })?;
_ => throw_error(ErrorType::IllegalAction)?,
} 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!( println!(
"{}{}{}{}", "{}{}{}{}{}{}",
append_emoji(EmojiType::Success)?, append_emoji(EmojiType::Success)?,
match contents[3] { "Rolled back to ".green(),
"take" => "Take", match final_action.action {
"drop" => "Drop", Action::Take => "Take",
"switch" => "Switch", Action::Drop => "Drop",
_ => "Unknown", Action::Switch => "Switch",
} }
.bold(), .bold(),
" action undone".green(), " action, on ".green(),
match contents[3] { newdate,
"take" => format!( match final_action.action {
Action::Take => format!(
"{}{}{}{}{}{}{}", "{}{}{}{}{}{}{}",
" (".green(), " (".green(),
contents[1].bold(), final_action.file.unwrap().bold(),
", ".green(), ", ".green(),
match parse_config()?.history_display_dir { match parse_config()?.history_display_dir {
true => format!("{} {} ", "from".green(), contents[0],), true => format!(
"{} {} ",
"from".green(),
final_action.path.unwrap().to_str().unwrap(),
),
_ => String::new(), _ => String::new(),
}, },
"to ".green(), "to ".green(),
match contents[2] { match final_action.slot.clone().unwrap().as_str() {
"active" => contents[2].green(), "active" => final_action.slot.unwrap().green(),
"inactive" => contents[2].blue(), "inactive" => final_action.slot.unwrap().blue(),
_ => contents[2].red(), _ => final_action.slot.unwrap().red(),
} }
.bold(), .bold(),
" slot)".green(), " slot)".green(),
), ),
"drop" => format!( Action::Drop => format!(
"{}{}{}{}{}{}{}", "{}{}{}{}{}{}{}",
" (".green(), " (".green(),
contents[1].bold(), final_action.file.unwrap().bold(),
", from ".green(), ", from ".green(),
match contents[2] { match final_action.slot.clone().unwrap().as_str() {
"active" => contents[2].green(), "active" => final_action.slot.unwrap().green(),
"inactive" => contents[2].blue(), "inactive" => final_action.slot.unwrap().blue(),
_ => contents[2].red(), _ => final_action.slot.unwrap().red(),
} }
.bold(), .bold(),
" slot".green(), " slot".green(),
match parse_config()?.history_display_dir { match parse_config()?.history_display_dir {
true => format!(" {} {}", "to".green(), contents[0],), true => format!(
" {} {}",
"to".green(),
final_action.path.unwrap().to_str().unwrap(),
),
false => String::new(), false => String::new(),
}, },
")".green(), ")".green(),
@ -120,3 +193,172 @@ pub fn undo() -> Result<()> {
Ok(()) 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| Ok(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| Ok(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 = vec![
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(())
}

View file

@ -178,7 +178,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 /// Switches inevntory slots between each other, making the currently active inventory inactive and viceversa
pub fn switch(message: bool) -> Result<()> { pub fn switch(message: bool, save_history: bool) -> Result<()> {
let ventodir = &common::env_config()?.vento_dir; let ventodir = &common::env_config()?.vento_dir;
let active = &common::env_config()?.active_dir; let active = &common::env_config()?.active_dir;
let inactive = &common::env_config()?.inactive_dir; let inactive = &common::env_config()?.inactive_dir;
@ -192,12 +192,17 @@ pub fn switch(message: bool) -> Result<()> {
fs::rename(inactive, active).context(rename_error)?; fs::rename(inactive, active).context(rename_error)?;
fs::rename(&temp, inactive).context(rename_error)?; fs::rename(&temp, inactive).context(rename_error)?;
common::history(common::HistoryData { if save_history {
path: PathBuf::new(), common::history(common::HistoryData {
file: String::new(), id: 0,
slot: String::new(), path: None,
action: common::Action::Switch, file: None,
})?; slot: None,
action: common::Action::Switch,
current: 1,
time: 0,
})?;
}
if message { if message {
println!( println!(

View file

@ -28,7 +28,7 @@ use std::fs;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
/// Takes a file or directory and stores it in an inventory slot /// Takes a file or directory and stores it in an inventory slot
pub fn take(file: &String, slot: &str, message: bool, display_slot: bool) -> Result<()> { pub fn take(file: &String, slot: &str, message: bool, display_slot: bool, save_history: bool) -> Result<()> {
let ventodir = &env_config()?.vento_dir; let ventodir = &env_config()?.vento_dir;
if !ventodir.is_dir() { if !ventodir.is_dir() {
@ -78,12 +78,17 @@ pub fn take(file: &String, slot: &str, message: bool, display_slot: bool) -> Res
throw_error(ErrorType::NoFileOrDir)?; throw_error(ErrorType::NoFileOrDir)?;
} }
history(HistoryData { if save_history {
path: sourcelocation.clone(), history(HistoryData {
file: String::from(filename), id: 0,
slot: String::from(slot), path: Some(sourcelocation.clone()),
action: Action::Take, file: Some(String::from(filename)),
})?; slot: Some(String::from(slot)),
action: Action::Take,
current: 1,
time: 0,
})?;
}
if message { if message {
println!( println!(
@ -125,6 +130,7 @@ pub fn drop(
dest: PathBuf, dest: PathBuf,
message: bool, message: bool,
display_slot: bool, display_slot: bool,
save_history: bool
) -> Result<()> { ) -> Result<()> {
// Drops a file or directory // Drops a file or directory
let ventodir = &env_config()?.vento_dir; let ventodir = &env_config()?.vento_dir;
@ -180,12 +186,17 @@ pub fn drop(
destpath.pop(); destpath.pop();
history(HistoryData { if save_history {
path: destpath.clone(), history(HistoryData {
file: String::from(file), id: 0,
slot: String::from(slot), path: Some(destpath.clone()),
action: Action::Drop, file: Some(String::from(file)),
})?; slot: Some(String::from(slot)),
action: Action::Drop,
current: 1,
time: 0,
})?;
}
if message { if message {
println!( println!(

View file

@ -28,6 +28,7 @@ pub enum ErrorType {
NoCurrentDirectory, NoCurrentDirectory,
NoHomeDirectory, NoHomeDirectory,
InvalidHistoryLength, InvalidHistoryLength,
InvalidStepsLength,
IllegalAction, IllegalAction,
NotInitialized, NotInitialized,
NoAccessParent, NoAccessParent,
@ -68,7 +69,8 @@ pub fn throw_error(error: ErrorType) -> Result<()> {
ErrorType::SpecifySlot => "You need to specify a slot", 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::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::NoHomeDirectory => "Vento was unable to detect your home directory. Have you configured your environment correctly?",
ErrorType::InvalidHistoryLength => "Invalid history length", ErrorType::InvalidHistoryLength => "Invalid history length",
ErrorType::InvalidStepsLength => "Invalid steps length",
ErrorType::IllegalAction => "Illegal action", ErrorType::IllegalAction => "Illegal action",
ErrorType::NotInitialized => "Vento not initialized. Run \"vento -i\" to initialize Vento", ErrorType::NotInitialized => "Vento not initialized. Run \"vento -i\" to initialize Vento",
ErrorType::NoAccessParent => "Cannot access parent", ErrorType::NoAccessParent => "Cannot access parent",