diff --git a/src/compression.rs b/src/compression.rs index ee9d3b4..64e8325 100755 --- a/src/compression.rs +++ b/src/compression.rs @@ -1,9 +1,9 @@ -use anyhow::{anyhow,Result,Context}; -use strum::IntoEnumIterator; -use std::path::PathBuf; -use std::io::{Read,Write}; +use anyhow::{anyhow, Context, Result}; use std::io; -use std::process::{Command,Stdio}; +use std::io::{Read, Write}; +use std::path::PathBuf; +use std::process::{Command, Stdio}; +use strum::IntoEnumIterator; use log::*; @@ -11,17 +11,16 @@ use lazy_static::lazy_static; extern crate enum_map; use enum_map::enum_map; -use enum_map::{EnumMap,Enum}; +use enum_map::{Enum, EnumMap}; - -pub mod none; -pub mod lz4; pub mod gzip; +pub mod lz4; +pub mod none; pub mod program; -use none::CompressionEngineNone; -use lz4::CompressionEngineLZ4; use gzip::CompressionEngineGZip; +use lz4::CompressionEngineLZ4; +use none::CompressionEngineNone; use program::CompressionEngineProgram; #[derive(Debug, Eq, PartialEq, Clone, strum::EnumIter, strum::Display, strum::EnumString, Enum)] @@ -32,10 +31,9 @@ pub enum CompressionType { BZip2, XZ, ZStd, - None + None, } - pub trait CompressionEngine { fn open(&self, file_path: PathBuf) -> Result>; fn create(&self, file_path: PathBuf) -> Result>; @@ -76,7 +74,11 @@ pub trait CompressionEngine { .stdin(Stdio::piped()) .stdout(Stdio::piped()) .spawn() - .context(anyhow!("Unable to spawn child process: {:?} {:?}", program, args))?; + .context(anyhow!( + "Unable to spawn child process: {:?} {:?}", + program, + args + ))?; let mut stdin = process.stdin.unwrap(); stdin.write_all(&buffer[0..buffer_size])?; @@ -106,7 +108,6 @@ pub trait CompressionEngine { } } - lazy_static! { pub static ref COMPRESSION_PROGRAMS: EnumMap> = enum_map! { CompressionType::LZ4 => None, @@ -127,17 +128,19 @@ lazy_static! { }; } - pub fn get_engine(compression_type: CompressionType) -> Result> { match compression_type { CompressionType::LZ4 => Ok(Box::new(CompressionEngineLZ4::new())), CompressionType::GZip => Ok(Box::new(CompressionEngineGZip::new())), CompressionType::None => Ok(Box::new(CompressionEngineNone::new())), - compression_type => Ok(Box::new(COMPRESSION_PROGRAMS[compression_type.clone()].clone().unwrap())) + compression_type => Ok(Box::new( + COMPRESSION_PROGRAMS[compression_type.clone()] + .clone() + .unwrap(), + )), } } - pub fn default_type() -> CompressionType { let mut default = CompressionType::None; for compression_type in CompressionType::iter() { diff --git a/src/compression/gzip.rs b/src/compression/gzip.rs index 395dc34..bcc6fd5 100644 --- a/src/compression/gzip.rs +++ b/src/compression/gzip.rs @@ -1,13 +1,13 @@ use anyhow::Result; +use log::*; use std::fs::File; use std::io; -use std::io::{Read,Write}; +use std::io::{Read, Write}; use std::path::PathBuf; -use log::*; -use flate2::Compression; use flate2::read::GzDecoder; use flate2::write::GzEncoder; +use flate2::Compression; use crate::compression::CompressionEngine; @@ -40,7 +40,6 @@ impl CompressionEngine for CompressionEngineGZip { Ok(Box::new(AutoFinishGzEncoder::new(gzip_write))) } - } pub struct AutoFinishGzEncoder { diff --git a/src/compression/lz4.rs b/src/compression/lz4.rs index 43e85d3..d97d916 100644 --- a/src/compression/lz4.rs +++ b/src/compression/lz4.rs @@ -1,16 +1,15 @@ use anyhow::Result; -use std::fs::File; -use std::path::PathBuf; -use std::io::{Read, Write}; use log::*; +use std::fs::File; +use std::io::{Read, Write}; +use std::path::PathBuf; use lz4_flex::frame::{FrameDecoder, FrameEncoder}; use crate::compression::CompressionEngine; #[derive(Debug, Eq, PartialEq, Clone, Default)] -pub struct CompressionEngineLZ4 { -} +pub struct CompressionEngineLZ4 {} impl CompressionEngineLZ4 { pub fn new() -> CompressionEngineLZ4 { @@ -38,6 +37,4 @@ impl CompressionEngine for CompressionEngineLZ4 { Ok(Box::new(lz4_write)) } - } - diff --git a/src/compression/none.rs b/src/compression/none.rs index dc6dfd6..7c1c260 100644 --- a/src/compression/none.rs +++ b/src/compression/none.rs @@ -1,8 +1,8 @@ use anyhow::Result; -use std::fs::File; -use std::path::PathBuf; -use std::io::{Read, Write}; use log::*; +use std::fs::File; +use std::io::{Read, Write}; +use std::path::PathBuf; use crate::compression::CompressionEngine; @@ -30,5 +30,4 @@ impl CompressionEngine for CompressionEngineNone { debug!("COMPRESSION: Writting to {:?} using {:?}", file_path, *self); Ok(Box::new(File::create(file_path)?)) } - } diff --git a/src/compression/program.rs b/src/compression/program.rs index 62d6431..7632698 100644 --- a/src/compression/program.rs +++ b/src/compression/program.rs @@ -1,35 +1,37 @@ -use anyhow::{Context, Result, anyhow}; -use std::fs::File; -use std::process::{Command,Stdio}; -use std::path::PathBuf; +use anyhow::{anyhow, Context, Result}; +use log::*; use std::env; use std::fs; -use std::os::unix::fs::PermissionsExt; +use std::fs::File; use std::io::{Read, Write}; -use log::*; +use std::os::unix::fs::PermissionsExt; +use std::path::PathBuf; +use std::process::{Command, Stdio}; use crate::compression::CompressionEngine; - #[derive(Debug, Eq, PartialEq, Clone)] pub struct CompressionEngineProgram { pub program: String, pub compress: Vec, pub decompress: Vec, - pub supported: bool + pub supported: bool, } - impl CompressionEngineProgram { - pub fn new(program: &str, compress: Vec<&str>, decompress: Vec<&str>) -> CompressionEngineProgram { + pub fn new( + program: &str, + compress: Vec<&str>, + decompress: Vec<&str>, + ) -> CompressionEngineProgram { let program_path = get_program_path(program); let supported = program_path.is_ok(); CompressionEngineProgram { program: program_path.unwrap_or(program.to_string()), - compress: compress.iter().map(|s| {s.to_string()}).collect(), - decompress: decompress.iter().map(|s| {s.to_string()}).collect(), - supported + compress: compress.iter().map(|s| s.to_string()).collect(), + decompress: decompress.iter().map(|s| s.to_string()).collect(), + supported, } } } @@ -45,7 +47,10 @@ impl CompressionEngine for CompressionEngineProgram { let program = self.program.clone(); let args = self.decompress.clone(); - debug!("COMPRESSION: Executing command: {:?} {:?} reading from {:?}", program, args, file_path); + debug!( + "COMPRESSION: Executing command: {:?} {:?} reading from {:?}", + program, args, file_path + ); let file = File::open(file_path).context("Unable to open file for reading")?; @@ -54,7 +59,11 @@ impl CompressionEngine for CompressionEngineProgram { .stdin(file) .stdout(Stdio::piped()) .spawn() - .context(anyhow!("Unable to spawn child process: {:?} {:?}", program, args))?; + .context(anyhow!( + "Unable to spawn child process: {:?} {:?}", + program, + args + ))?; Ok(Box::new(process.stdout.unwrap())) } @@ -64,7 +73,10 @@ impl CompressionEngine for CompressionEngineProgram { let program = self.program.clone(); let args = self.compress.clone(); - debug!("COMPRESSION: Executing command: {:?} {:?} writing to {:?}", program, args, file_path); + debug!( + "COMPRESSION: Executing command: {:?} {:?} writing to {:?}", + program, args, file_path + ); let file = File::create(file_path).context("Unable to open file for writing")?; @@ -73,11 +85,14 @@ impl CompressionEngine for CompressionEngineProgram { .stdin(Stdio::piped()) .stdout(file) .spawn() - .context(anyhow!("Problem spawning child process: {:?} {:?}", program, args))?; + .context(anyhow!( + "Problem spawning child process: {:?} {:?}", + program, + args + ))?; Ok(Box::new(process.stdin.unwrap())) } - } fn get_program_path(program: &str) -> Result { diff --git a/src/db.rs b/src/db.rs index 0390c0d..10cef95 100644 --- a/src/db.rs +++ b/src/db.rs @@ -1,31 +1,37 @@ -use std::path::PathBuf; -use std::collections::HashMap; -use std::rc::Rc; -use anyhow::{Context, Result, Error}; -use rusqlite::{Connection, OpenFlags}; -use rusqlite_migration::{Migrations, M}; +use anyhow::{Context, Error, Result}; use chrono::prelude::*; use lazy_static::lazy_static; use log::*; +use rusqlite::{Connection, OpenFlags}; +use rusqlite_migration::{Migrations, M}; +use std::collections::HashMap; +use std::path::PathBuf; +use std::rc::Rc; lazy_static! { static ref MIGRATIONS: Migrations<'static> = Migrations::new(vec![ - M::up("CREATE TABLE items( + M::up( + "CREATE TABLE items( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, ts TEXT NOT NULL, size INTEGER NULL, - compression TEXT NOT NULL)"), - M::up("CREATE TABLE tags ( + compression TEXT NOT NULL)" + ), + M::up( + "CREATE TABLE tags ( id INTEGER NOT NULL, name TEXT NOT NULL, FOREIGN KEY(id) REFERENCES items(id) ON DELETE CASCADE, - PRIMARY KEY(id, name));"), - M::up("CREATE TABLE metas ( + PRIMARY KEY(id, name));" + ), + M::up( + "CREATE TABLE metas ( id INTEGER NOT NULL, name TEXT NOT NULL, value TEXT NOT NULL, FOREIGN KEY(id) REFERENCES items(id) ON DELETE CASCADE, - PRIMARY KEY(id, name));") + PRIMARY KEY(id, name));" + ) ]); } @@ -34,71 +40,72 @@ pub struct Item { pub id: Option, pub ts: DateTime, pub size: Option, - pub compression: String + pub compression: String, } #[derive(Debug, Clone)] pub struct Tag { pub id: i64, - pub name: String + pub name: String, } #[derive(Debug, Clone)] pub struct Meta { pub id: i64, pub name: String, - pub value: String + pub value: String, } - pub fn open(path: PathBuf) -> Result { debug!("DB: Opening file: {:?}", path); - let mut conn = Connection::open_with_flags(path, OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_CREATE) - .context("Problem opening file")?; + let mut conn = Connection::open_with_flags( + path, + OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_CREATE, + ) + .context("Problem opening file")?; conn.pragma_update(None, "foreign_keys", "ON") .context("Problem enabling SQLite foreign_keys pragma")?; - MIGRATIONS.to_latest(&mut conn) + MIGRATIONS + .to_latest(&mut conn) .context("Problem performing database migrations")?; - rusqlite::vtab::array::load_module(&conn) - .context("Problem enabling array module")?; + rusqlite::vtab::array::load_module(&conn).context("Problem enabling array module")?; Ok(conn) } - pub fn insert_item(conn: &Connection, item: Item) -> Result { debug!("DB: Inserting item: {:?}", item); conn.execute( "INSERT INTO items (ts, size, compression) VALUES (?1, ?2, ?3)", - (item.ts, item.size, item.compression))?; + (item.ts, item.size, item.compression), + )?; Ok(conn.last_insert_rowid()) } - pub fn update_item(conn: &Connection, item: Item) -> Result<()> { debug!("DB: Updating item: {:?}", item); conn.execute( "UPDATE items SET size=?2, compression=?3 WHERE id=?1", - (item.id, item.size, item.compression))?; + (item.id, item.size, item.compression), + )?; Ok(()) } - pub fn delete_item(conn: &Connection, item: Item) -> Result<()> { debug!("DB: Deleting item: {:?}", item); conn.execute("DELETE FROM items WHERE id=?1", [item.id])?; Ok(()) } - pub fn query_delete_meta(conn: &Connection, meta: Meta) -> Result<()> { debug!("DB: Deleting meta: {:?}", meta); conn.execute( "DELETE FROM metas WHERE id=?1 AND name=?2", - (meta.id, meta.name))?; + (meta.id, meta.name), + )?; Ok(()) } @@ -107,7 +114,8 @@ pub fn query_upsert_meta(conn: &Connection, meta: Meta) -> Result<()> { conn.execute( "INSERT INTO metas (id, name, value) VALUES (?1, ?2, ?3) ON CONFLICT(id, name) DO UPDATE SET value=?3", - (meta.id, meta.name, meta.value))?; + (meta.id, meta.name, meta.value), + )?; Ok(()) } @@ -121,41 +129,38 @@ pub fn store_meta(conn: &Connection, meta: Meta) -> Result<()> { Ok(()) } - pub fn insert_tag(conn: &Connection, tag: Tag) -> Result<()> { debug!("DB: Inserting tag: {:?}", tag); conn.execute( "INSERT INTO tags (id, name) VALUES (?1, ?2)", - (tag.id, tag.name))?; + (tag.id, tag.name), + )?; Ok(()) } - pub fn delete_item_tags(conn: &Connection, item: Item) -> Result<()> { debug!("DB: Deleting all item tags: {:?}", item); - conn.execute( - "DELETE FROM tags WHERE id=?1", - [item.id])?; + conn.execute("DELETE FROM tags WHERE id=?1", [item.id])?; Ok(()) } - pub fn set_item_tags(conn: &Connection, item: Item, tags: &Vec) -> Result<()> { debug!("DB: Setting tags for item: {:?} ?{:?}", item, tags); delete_item_tags(conn, item.clone())?; let item_id = item.id.unwrap(); for tag_name in tags { - insert_tag(conn, + insert_tag( + conn, Tag { id: item_id, - name: tag_name.to_string() - })?; + name: tag_name.to_string(), + }, + )?; } Ok(()) } - pub fn query_all_items(conn: &Connection) -> Result> { debug!("DB: Querying all items"); let mut statement = conn @@ -169,7 +174,7 @@ pub fn query_all_items(conn: &Connection) -> Result> { id: row.get(0)?, ts: row.get(1)?, size: row.get(2)?, - compression: row.get(3)? + compression: row.get(3)?, }; items.push(item); } @@ -180,7 +185,8 @@ pub fn query_all_items(conn: &Connection) -> Result> { pub fn query_tagged_items<'a>(conn: &'a Connection, tags: &'a Vec) -> Result> { debug!("DB: Querying tagged items: {:?}", tags); let mut statement = conn - .prepare_cached(" + .prepare_cached( + " SELECT items.id, items.ts, items.size, @@ -191,12 +197,13 @@ pub fn query_tagged_items<'a>(conn: &'a Connection, tags: &'a Vec) -> Re WHERE items.id = tags_match.id GROUP BY items.id HAVING tags_score = ?2 - ORDER BY items.id ASC") + ORDER BY items.id ASC", + ) .context("Problem preparing SQL statement")?; let tags_values: Vec = tags .iter() - .map(|s| {rusqlite::types::Value::from(s.clone())}) + .map(|s| rusqlite::types::Value::from(s.clone())) .collect(); let tags_ptr = Rc::new(tags_values); @@ -209,7 +216,7 @@ pub fn query_tagged_items<'a>(conn: &'a Connection, tags: &'a Vec) -> Re id: row.get(0)?, ts: row.get(1)?, size: row.get(2)?, - compression: row.get(3)? + compression: row.get(3)?, }; items.push(item); } @@ -217,20 +224,24 @@ pub fn query_tagged_items<'a>(conn: &'a Connection, tags: &'a Vec) -> Re Ok(items) } - - pub fn get_items(conn: &Connection) -> Result> { debug!("DB: Getting all items"); query_all_items(conn) } - -pub fn get_items_matching(conn: &Connection, tags: &Vec, meta: &HashMap) -> Result> { - debug!("DB: Getting items matching: tags={:?} meta={:?}", tags, meta); +pub fn get_items_matching( + conn: &Connection, + tags: &Vec, + meta: &HashMap, +) -> Result> { + debug!( + "DB: Getting items matching: tags={:?} meta={:?}", + tags, meta + ); let items = match tags.is_empty() { true => query_all_items(conn)?, - false => query_tagged_items(conn, tags)? + false => query_tagged_items(conn, tags)?, }; if meta.is_empty() { @@ -251,7 +262,7 @@ pub fn get_items_matching(conn: &Connection, tags: &Vec, meta: &HashMap< for (k, v) in meta.iter() { match item_meta.get(k) { Some(value) => item_ok = v.eq(value), - None => item_ok = false + None => item_ok = false, } if item_ok { @@ -265,14 +276,17 @@ pub fn get_items_matching(conn: &Connection, tags: &Vec, meta: &HashMap< } Ok(filtered_items) } - } - -pub fn get_item_matching(conn: &Connection, tags: &Vec, _meta: &HashMap) -> Result> { +pub fn get_item_matching( + conn: &Connection, + tags: &Vec, + _meta: &HashMap, +) -> Result> { debug!("DB: Get item matching tags: {:?}", tags); let mut statement = conn - .prepare_cached(" + .prepare_cached( + " SELECT items.id, items.ts, items.size, @@ -284,12 +298,13 @@ pub fn get_item_matching(conn: &Connection, tags: &Vec, _meta: &HashMap< GROUP BY items.id HAVING score = ?2 ORDER BY items.id DESC - LIMIT 1") + LIMIT 1", + ) .context("Problem preparing SQL statement")?; let tags_values: Vec = tags .iter() - .map(|s| {rusqlite::types::Value::from(s.clone()) }) + .map(|s| rusqlite::types::Value::from(s.clone())) .collect(); let tags_ptr = Rc::new(tags_values); @@ -301,20 +316,21 @@ pub fn get_item_matching(conn: &Connection, tags: &Vec, _meta: &HashMap< id: row.get(0)?, ts: row.get(1)?, size: row.get(2)?, - compression: row.get(3)? + compression: row.get(3)?, })), - None => Ok(None) + None => Ok(None), } } - pub fn get_item(conn: &Connection, item_id: i64) -> Result> { debug!("DB: Getting item {:?}", item_id); let mut statement = conn - .prepare_cached(" + .prepare_cached( + " SELECT id, ts, size, compression FROM items - WHERE items.id = ?1") + WHERE items.id = ?1", + ) .context("Problem preparing SQL statement")?; let mut rows = statement.query([item_id])?; @@ -324,20 +340,22 @@ pub fn get_item(conn: &Connection, item_id: i64) -> Result> { id: row.get(0)?, ts: row.get(1)?, size: row.get(2)?, - compression: row.get(3)? + compression: row.get(3)?, })), - None => Ok(None) + None => Ok(None), } } pub fn get_item_last(conn: &Connection) -> Result> { debug!("DB: Getting last item"); let mut statement = conn - .prepare_cached(" + .prepare_cached( + " SELECT id, ts, size, compression FROM items ORDER BY id DESC - LIMIT 1") + LIMIT 1", + ) .context("Problem preparing SQL statement")?; let mut rows = statement.query([])?; @@ -347,9 +365,9 @@ pub fn get_item_last(conn: &Connection) -> Result> { id: row.get(0)?, ts: row.get(1)?, size: row.get(2)?, - compression: row.get(3)? + compression: row.get(3)?, })), - None => Ok(None) + None => Ok(None), } } @@ -372,7 +390,6 @@ pub fn get_item_tags(conn: &Connection, item: &Item) -> Result> { Ok(tags) } - pub fn get_item_meta(conn: &Connection, item: &Item) -> Result> { debug!("DB: Getting item meta: {:?}", item); let mut statement = conn @@ -386,7 +403,7 @@ pub fn get_item_meta(conn: &Connection, item: &Item) -> Result> { metas.push(Meta { id: row.get(0)?, name: row.get(1)?, - value: row.get(2)? + value: row.get(2)?, }); } diff --git a/src/main.rs b/src/main.rs index 00e82fb..3da652e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,22 +1,21 @@ -use std::io; -use std::io::{Read, Write, BufWriter}; -use std::fs; -use std::str::FromStr; -use std::path::PathBuf; -use std::collections::HashMap; -use std::os::fd::FromRawFd; -use std::process::Stdio; // For Stdio::null, Stdio::piped -use nix::fcntl::{FdFlag}; +use nix::fcntl::FdFlag; use nix::unistd::{close, pipe}; use nix::Error as NixError; +use std::collections::HashMap; +use std::fs; +use std::io; +use std::io::{BufWriter, Read, Write}; +use std::os::fd::FromRawFd; +use std::path::PathBuf; +use std::process::Stdio; // For Stdio::null, Stdio::piped +use std::str::FromStr; - -use anyhow::{Context, Result, Error, anyhow}; -use rusqlite::Connection; -use gethostname::gethostname; +use anyhow::{anyhow, Context, Error, Result}; use clap::error::ErrorKind; use clap::*; +use gethostname::gethostname; use log::*; +use rusqlite::Connection; mod modes; use crate::modes::common::*; @@ -24,12 +23,12 @@ extern crate directories; use directories::ProjectDirs; extern crate prettytable; -use prettytable::{Table, Row, Cell, Attr}; -use prettytable::format; -use prettytable::format::{TableFormat, Alignment}; -use prettytable::format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR; -use prettytable::row; use prettytable::color; +use prettytable::format; +use prettytable::format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR; +use prettytable::format::{Alignment, TableFormat}; +use prettytable::row; +use prettytable::{Attr, Cell, Row, Table}; use chrono::prelude::*; @@ -42,36 +41,29 @@ pub mod db; use compression::CompressionType; - use is_terminal::IsTerminal; extern crate term; - lazy_static! { - static ref FORMAT_BOX_CHARS_NO_BORDER_LINE_SEPARATOR: TableFormat = format::FormatBuilder::new() - .column_separator('│') - .borders('│') - .separators(&[format::LinePosition::Top], - format::LineSeparator::new( - '─', - '┬', - '┌', - '┐')) - .separators(&[format::LinePosition::Title], - format::LineSeparator::new( - '─', - '┼', - '├', - '┤')) - .separators(&[format::LinePosition::Bottom], - format::LineSeparator::new( - '─', - '┴', - '└', - '┘')) - .padding(1, 1) - .build(); + static ref FORMAT_BOX_CHARS_NO_BORDER_LINE_SEPARATOR: TableFormat = + format::FormatBuilder::new() + .column_separator('│') + .borders('│') + .separators( + &[format::LinePosition::Top], + format::LineSeparator::new('─', '┬', '┌', '┐') + ) + .separators( + &[format::LinePosition::Title], + format::LineSeparator::new('─', '┼', '├', '┤') + ) + .separators( + &[format::LinePosition::Bottom], + format::LineSeparator::new('─', '┴', '└', '┘') + ) + .padding(1, 1) + .build(); } #[derive(Parser, Debug)] @@ -85,10 +77,9 @@ pub struct Args { options: OptionsArgs, #[arg(help("A list of either item IDs or tags"))] - ids_or_tags: Vec + ids_or_tags: Vec, } - #[derive(Parser, Debug)] struct ModeArgs { #[arg(group("mode"), help_heading("Mode Options"), short, long, conflicts_with_all(["get", "diff", "list", "update", "delete", "info", "status"]))] @@ -96,7 +87,9 @@ struct ModeArgs { save: bool, #[arg(group("mode"), help_heading("Mode Options"), short, long, conflicts_with_all(["save", "diff", "list", "update", "delete", "info", "status"]))] - #[arg(help("Get an item either by it's ID or by a combination of matching tags and metatdata"))] + #[arg(help( + "Get an item either by it's ID or by a combination of matching tags and metatdata" + ))] get: bool, #[arg(group("mode"), help_heading("Mode Options"), long, conflicts_with_all(["save", "get", "list", "update", "delete", "info", "status"]))] @@ -116,34 +109,38 @@ struct ModeArgs { delete: bool, #[arg(group("mode"), help_heading("Mode Options"), short, long, conflicts_with_all(["save", "get", "diff", "list", "update", "delete", "status"]), requires("ids_or_tags"))] - #[arg(help("Get an item either by it's ID or by a combination of matching tags and metatdata"))] + #[arg(help( + "Get an item either by it's ID or by a combination of matching tags and metatdata" + ))] info: bool, #[arg(group("mode"), help_heading("Mode Options"), short('S'), long, conflicts_with_all(["save", "get", "diff", "list", "update", "delete", "info"]))] #[arg(help("Show status of directories and supported compression algorithms"))] - status: bool + status: bool, } - #[derive(Parser, Debug)] struct ItemArgs { #[arg(help_heading("Item Options"), short, long, conflicts_with_all(["get", "delete", "status"]))] #[arg(help("Set metadata for the item using the format KEY=[VALUE], the metadata will be removed if VALUE is not provided"))] meta: Vec, - #[arg(help_heading("Item Options"), short, long, env("KEEP_COMPRESSION"), )] + #[arg(help_heading("Item Options"), short, long, env("KEEP_COMPRESSION"))] #[arg(help("Compression algorithm to use when saving items"))] compression: Option, } - #[derive(Parser, Debug)] struct OptionsArgs { #[arg(long, env("KEEP_DIR"))] #[arg(help("Specify the directory to use for storage"))] dir: Option, - #[arg(long, env("KEEP_LIST_FORMAT"), default_value("id,time,size,tags,meta:hostname"))] + #[arg( + long, + env("KEEP_LIST_FORMAT"), + default_value("id,time,size,tags,meta:hostname") + )] #[arg(help("A comma separated list of columns to display with --list"))] list_format: String, @@ -160,8 +157,7 @@ struct OptionsArgs { quiet: bool, } - -#[derive(Debug,PartialEq)] +#[derive(Debug, PartialEq)] enum KeepModes { Unknown, Save, @@ -171,13 +167,13 @@ enum KeepModes { Update, Delete, Info, - Status + Status, } -#[derive(Debug,Clone)] +#[derive(Debug, Clone)] struct KeyValue { key: String, - value: String + value: String, } impl FromStr for KeyValue { @@ -186,15 +182,14 @@ impl FromStr for KeyValue { match s.split_once('=') { Some(kv) => Ok(KeyValue { key: kv.0.to_string(), - value: kv.1.to_string() + value: kv.1.to_string(), }), - None => Err(anyhow!("Unable to parse key=value pair")) + None => Err(anyhow!("Unable to parse key=value pair")), } } } - -#[derive(Debug,Clone)] +#[derive(Debug, Clone)] enum NumberOrString { Number(i64), Str(String), @@ -219,7 +214,7 @@ pub enum ColumnType { FileSize, FilePath, Tags, - Meta + Meta, } fn main() -> Result<(), Error> { @@ -272,7 +267,7 @@ fn main() -> Result<(), Error> { } if mode == KeepModes::Unknown { - if ! ids.is_empty() { + if !ids.is_empty() { mode = KeepModes::Get; } else { mode = KeepModes::Save; @@ -287,7 +282,7 @@ fn main() -> Result<(), Error> { if args.options.dir.is_none() { match proj_dirs { Some(proj_dirs) => args.options.dir = Some(proj_dirs.data_dir().to_path_buf()), - None => return Err(anyhow!("Unable to determine data directory")) + None => return Err(anyhow!("Unable to determine data directory")), } } @@ -302,45 +297,65 @@ fn main() -> Result<(), Error> { debug!("MAIN: Data directory: {:?}", data_path); debug!("MAIN: DB file: {:?}", db_path); - fs::create_dir_all(data_path.clone()) - .context("Problem creating data directory")?; + fs::create_dir_all(data_path.clone()).context("Problem creating data directory")?; debug!("MAIN: Data directory created or already exists"); - let mut conn = db::open(db_path.clone()) - .context("Problem opening database")?; + let mut conn = db::open(db_path.clone()).context("Problem opening database")?; debug!("MAIN: DB opened successfully"); match mode { KeepModes::Save => mode_save(&mut cmd, args, ids, tags, conn, data_path)?, - KeepModes::Get => crate::modes::get::mode_get(&mut cmd, args, ids, tags, &mut conn, data_path)?, + KeepModes::Get => { + crate::modes::get::mode_get(&mut cmd, args, ids, tags, &mut conn, data_path)? + } KeepModes::Diff => mode_diff(&mut cmd, args, ids, tags, &mut conn, data_path)?, KeepModes::List => mode_list(&mut cmd, args, ids, tags, &mut conn, data_path)?, - KeepModes::Update => crate::modes::update::mode_update(&mut cmd, args, ids, tags, &mut conn, data_path)?, + KeepModes::Update => { + crate::modes::update::mode_update(&mut cmd, args, ids, tags, &mut conn, data_path)? + } KeepModes::Info => mode_info(&mut cmd, args, ids, tags, &mut conn, data_path)?, - KeepModes::Delete => crate::modes::delete::mode_delete(&mut cmd, args, ids, tags, &mut conn, data_path)?, + KeepModes::Delete => { + crate::modes::delete::mode_delete(&mut cmd, args, ids, tags, &mut conn, data_path)? + } KeepModes::Status => crate::modes::status::mode_status(&mut cmd, args, data_path, db_path)?, - _ => todo!() + _ => todo!(), } Ok(()) } - - -fn mode_save(cmd: &mut Command, args: Args, ids: &mut Vec, tags: &mut Vec, conn: Connection, data_path: PathBuf) -> Result<()> { - if ! ids.is_empty() { - cmd.error(ErrorKind::InvalidValue, "ID given, you cannot supply IDs when using --save").exit(); +fn mode_save( + cmd: &mut Command, + args: Args, + ids: &mut Vec, + tags: &mut Vec, + conn: Connection, + data_path: PathBuf, +) -> Result<()> { + if !ids.is_empty() { + cmd.error( + ErrorKind::InvalidValue, + "ID given, you cannot supply IDs when using --save", + ) + .exit(); } if tags.is_empty() { tags.push("none".to_string()); } - let compression_name = args.item.compression.unwrap_or(compression::default_type().to_string()); + let compression_name = args + .item + .compression + .unwrap_or(compression::default_type().to_string()); let compression_type_opt = CompressionType::from_str(&compression_name); if compression_type_opt.is_err() { - cmd.error(ErrorKind::InvalidValue, format!("Unknown compression type: {}", compression_name)).exit(); + cmd.error( + ErrorKind::InvalidValue, + format!("Unknown compression type: {}", compression_name), + ) + .exit(); } let compression_type = compression_type_opt.unwrap(); @@ -350,14 +365,14 @@ fn mode_save(cmd: &mut Command, args: Args, ids: &mut Vec, tags: &mut Vec, tags: &mut Vec = get_meta_from_env(); if let Ok(hostname) = gethostname().into_string() { - if ! item_meta.contains_key("hostname") { + if !item_meta.contains_key("hostname") { item_meta.insert("hostname".to_string(), hostname); } } @@ -399,7 +414,7 @@ fn mode_save(cmd: &mut Command, args: Args, ids: &mut Vec, tags: &mut Vec, tags: &mut Vec = compression_engine.create(item_path.clone()) - .context(anyhow!("Unable to write file {:?} using compression {:?}", item_path, compression_type))?; + let compression_engine = compression::get_engine(compression_type.clone()) + .expect("Unable to get compression engine"); + let mut item_out: Box = + compression_engine + .create(item_path.clone()) + .context(anyhow!( + "Unable to write file {:?} using compression {:?}", + item_path, + compression_type + ))?; debug!("MAIN: Starting IO loop"); loop { @@ -428,7 +450,7 @@ fn mode_save(cmd: &mut Command, args: Args, ids: &mut Vec, tags: &mut Vec Some(n as i64), - Some(prev_n) => Some(prev_n + n as i64) + Some(prev_n) => Some(prev_n + n as i64), }; } debug!("MAIN: Ending IO loop"); @@ -441,7 +463,6 @@ fn mode_save(cmd: &mut Command, args: Args, ids: &mut Vec, tags: &mut Vec = db::get_item_tags(conn, &item_a)? .into_iter() - .map(|x| {x.name}) + .map(|x| x.name) .collect(); let item_b_tags: Vec = db::get_item_tags(conn, &item_b)? .into_iter() - .map(|x| {x.name}) + .map(|x| x.name) .collect(); - let mut item_path_a = data_path.clone(); item_path_a.push(item_a.id.unwrap().to_string()); // id.unwrap() is safe due to ok_or_else let compression_type_a = CompressionType::from_str(&item_a.compression)?; @@ -505,21 +525,34 @@ fn mode_diff( // it's good practice if the raw FDs were to be handled further before that. // For this specific code, since from_raw_fd takes ownership immediately, this is less critical // but doesn't hurt. - nix::fcntl::fcntl(fd_a_write, nix::fcntl::FcntlArg::F_SETFD(FdFlag::FD_CLOEXEC)) - .map_err(|e| anyhow!("Failed to set FD_CLOEXEC on fd_a_write: {}", e))?; - nix::fcntl::fcntl(fd_b_write, nix::fcntl::FcntlArg::F_SETFD(FdFlag::FD_CLOEXEC)) - .map_err(|e| anyhow!("Failed to set FD_CLOEXEC on fd_b_write: {}", e))?; - + nix::fcntl::fcntl( + fd_a_write, + nix::fcntl::FcntlArg::F_SETFD(FdFlag::FD_CLOEXEC), + ) + .map_err(|e| anyhow!("Failed to set FD_CLOEXEC on fd_a_write: {}", e))?; + nix::fcntl::fcntl( + fd_b_write, + nix::fcntl::FcntlArg::F_SETFD(FdFlag::FD_CLOEXEC), + ) + .map_err(|e| anyhow!("Failed to set FD_CLOEXEC on fd_b_write: {}", e))?; debug!("MAIN: Creating child process for diff"); let mut diff_command = std::process::Command::new("diff"); diff_command .arg("-u") .arg("--label") - .arg(format!("Keep item A: {} {}", item_a.id.unwrap(), item_a_tags.join(" "))) + .arg(format!( + "Keep item A: {} {}", + item_a.id.unwrap(), + item_a_tags.join(" ") + )) .arg(format!("/dev/fd/{}", fd_a_read)) .arg("--label") - .arg(format!("Keep item B: {} {}", item_b.id.unwrap(), item_b_tags.join(" "))) + .arg(format!( + "Keep item B: {} {}", + item_b.id.unwrap(), + item_b_tags.join(" ") + )) .arg(format!("/dev/fd/{}", fd_b_read)) .stdin(Stdio::null()) .stdout(Stdio::piped()) @@ -632,21 +665,35 @@ fn mode_diff( let diff_status = child_process .wait() .map_err(|e| anyhow!("Failed to wait on diff command: {}", e))?; - debug!("MAIN: Diff child process finished with status: {}", diff_status); + debug!( + "MAIN: Diff child process finished with status: {}", + diff_status + ); // Retrieve the captured output from the reader threads. // .join().unwrap() here will panic if the reader thread itself panicked. // The inner Result is from the read_to_end operation within the thread. - let stdout_capture_result = stdout_reader_thread.join().unwrap_or_else(|panic_payload| { - Err(anyhow!("Stdout reader thread panicked: {:?}", panic_payload)) - })?; - let stderr_capture_result = stderr_reader_thread.join().unwrap_or_else(|panic_payload| { - Err(anyhow!("Stderr reader thread panicked: {:?}", panic_payload)) - })?; + let stdout_capture_result = stdout_reader_thread + .join() + .unwrap_or_else(|panic_payload| { + Err(anyhow!( + "Stdout reader thread panicked: {:?}", + panic_payload + )) + })?; + let stderr_capture_result = stderr_reader_thread + .join() + .unwrap_or_else(|panic_payload| { + Err(anyhow!( + "Stderr reader thread panicked: {:?}", + panic_payload + )) + })?; // Handle diff's exit status and output match diff_status.code() { - Some(0) => { // Exit code 0: No differences + Some(0) => { + // Exit code 0: No differences debug!("MAIN: Diff successful, no differences found."); // Typically, diff -u doesn't print to stdout if no differences. // But if it did, it would be shown here. @@ -654,11 +701,13 @@ fn mode_diff( println!("{}", String::from_utf8_lossy(&stdout_capture_result)); } } - Some(1) => { // Exit code 1: Differences found + Some(1) => { + // Exit code 1: Differences found debug!("MAIN: Diff successful, differences found."); println!("{}", String::from_utf8_lossy(&stdout_capture_result)); } - Some(error_code) => { // Exit code > 1: Error in diff utility + Some(error_code) => { + // Exit code > 1: Error in diff utility eprintln!("Diff command failed with exit code: {}", error_code); if !stdout_capture_result.is_empty() { eprintln!( @@ -677,7 +726,8 @@ fn mode_diff( error_code )); } - None => { // Process terminated by a signal + None => { + // Process terminated by a signal eprintln!("Diff command terminated by signal."); if !stderr_capture_result.is_empty() { eprintln!( @@ -692,10 +742,20 @@ fn mode_diff( Ok(()) } - -fn mode_list(cmd: &mut Command, args: Args, ids: &mut Vec, tags: &Vec, conn: &mut Connection, data_path: PathBuf) -> Result<()> { - if ! ids.is_empty() { - cmd.error(ErrorKind::InvalidValue, "ID given, you can only supply tags when using --list").exit(); +fn mode_list( + cmd: &mut Command, + args: Args, + ids: &mut Vec, + tags: &Vec, + conn: &mut Connection, + data_path: PathBuf, +) -> Result<()> { + if !ids.is_empty() { + cmd.error( + ErrorKind::InvalidValue, + "ID given, you can only supply tags when using --list", + ) + .exit(); } let mut meta: HashMap = HashMap::new(); @@ -706,7 +766,7 @@ fn mode_list(cmd: &mut Command, args: Args, ids: &mut Vec, tags: &Vec db::get_items(conn)?, - false => db::get_items_matching(conn, tags, &meta)? + false => db::get_items_matching(conn, tags, &meta)?, }; debug!("MAIN: Items: {:?}", items); @@ -719,7 +779,7 @@ fn mode_list(cmd: &mut Command, args: Args, ids: &mut Vec, tags: &Vec = db::get_item_tags(conn, item)? .into_iter() - .map(|x| {x.name}) + .map(|x| x.name) .collect(); tags_by_item.insert(item_id, item_tags); @@ -729,7 +789,7 @@ fn mode_list(cmd: &mut Command, args: Args, ids: &mut Vec, tags: &Vec, tags: &Vec, tags: &Vec = None; if column_type == ColumnType::Meta { @@ -774,45 +838,62 @@ fn mode_list(cmd: &mut Command, args: Args, ids: &mut Vec, tags: &Vec len.parse().unwrap_or(0), - None => 0 + None => 0, }; let cell = match column_type { ColumnType::Id => Cell::new_align( &string_column(item.id.unwrap_or(0).to_string(), column_width), - Alignment::RIGHT), - ColumnType::Time => Cell::new( - &string_column(item.ts.with_timezone(&Local).format("%F %T").to_string(), column_width)), + Alignment::RIGHT, + ), + ColumnType::Time => Cell::new(&string_column( + item.ts.with_timezone(&Local).format("%F %T").to_string(), + column_width, + )), ColumnType::Size => match item.size { Some(size) => Cell::new_align( &size_column(size as u64, args.options.human_readable, column_width), - Alignment::RIGHT), + Alignment::RIGHT, + ), None => match item_path.metadata() { Ok(_) => Cell::new_align("Unknown", Alignment::RIGHT) .with_style(Attr::ForegroundColor(color::YELLOW)) .with_style(Attr::Bold), Err(_) => Cell::new_align("Missing", Alignment::RIGHT) .with_style(Attr::ForegroundColor(color::RED)) - .with_style(Attr::Bold) - } + .with_style(Attr::Bold), + }, }, - ColumnType::Compression => Cell::new(&string_column(item.compression.to_string(), column_width)), + ColumnType::Compression => { + Cell::new(&string_column(item.compression.to_string(), column_width)) + } ColumnType::FileSize => match item_path.metadata() { Ok(metadata) => Cell::new_align( - &size_column(metadata.len() as u64, args.options.human_readable, column_width), - Alignment::RIGHT), + &size_column( + metadata.len() as u64, + args.options.human_readable, + column_width, + ), + Alignment::RIGHT, + ), Err(_) => Cell::new_align("Missing", Alignment::RIGHT) - .with_style(Attr::ForegroundColor(color::RED)).with_style(Attr::Bold) + .with_style(Attr::ForegroundColor(color::RED)) + .with_style(Attr::Bold), }, - ColumnType::FilePath => Cell::new(&string_column(item_path.clone().into_os_string().into_string().unwrap(), column_width)), + ColumnType::FilePath => Cell::new(&string_column( + item_path.clone().into_os_string().into_string().unwrap(), + column_width, + )), ColumnType::Tags => Cell::new(&string_column(tags.join(" "), column_width)), ColumnType::Meta => match meta_name { Some(meta_name) => match meta.get(meta_name) { - Some(meta_value) => Cell::new(&string_column(meta_value.to_string(), column_width)), - None => Cell::new("") + Some(meta_value) => { + Cell::new(&string_column(meta_value.to_string(), column_width)) + } + None => Cell::new(""), }, - None => Cell::new("") - } + None => Cell::new(""), + }, }; table_row.add_cell(cell); } @@ -824,9 +905,15 @@ fn mode_list(cmd: &mut Command, args: Args, ids: &mut Vec, tags: &Vec, tags: &mut Vec, conn: &mut Connection, data_path: PathBuf) -> Result<()> { - if ! ids.is_empty() && ! tags.is_empty() { +fn mode_info( + cmd: &mut Command, + args: Args, + ids: &mut Vec, + tags: &mut Vec, + conn: &mut Connection, + data_path: PathBuf, +) -> Result<()> { + if !ids.is_empty() && !tags.is_empty() { cmd.error(ErrorKind::InvalidValue, "Both ID and tags given, you must supply exactly one ID or atleast one tag when using --info").exit(); } else if ids.len() > 1 { cmd.error(ErrorKind::InvalidValue, "More than one ID given, you must supply exactly one ID or atleast one tag when using --info").exit(); @@ -841,22 +928,20 @@ fn mode_info(cmd: &mut Command, args: Args, ids: &mut Vec, tags: &mut Vec match ids.iter().next() { Some(item_id) => db::get_item(conn, *item_id)?, - None => db::get_item_last(conn)? + None => db::get_item_last(conn)?, }, - false => db::get_item_matching(conn, tags, &meta)? + false => db::get_item_matching(conn, tags, &meta)?, }; - if let Some(item) = item_maybe { debug!("MAIN: Found item {:?}", item); let item_id = item.id.unwrap(); let item_tags: Vec = db::get_item_tags(conn, &item)? .into_iter() - .map(|x| {x.name}) + .map(|x| x.name) .collect(); - let mut table = Table::new(); if std::io::stdout().is_terminal() { table.set_format(*FORMAT_BOX_CHARS_NO_BORDER_LINE_SEPARATOR); @@ -866,75 +951,82 @@ fn mode_info(cmd: &mut Command, args: Args, ids: &mut Vec, tags: &mut Vec Cell::new(format_size(size as u64, args.options.human_readable).as_str()), - None => Cell::new("Missing").with_style(Attr::ForegroundColor(color::RED)).with_style(Attr::Bold) + None => Cell::new("Missing") + .with_style(Attr::ForegroundColor(color::RED)) + .with_style(Attr::Bold), }; table.add_row(Row::new(vec![ Cell::new("Stream Size").with_style(Attr::Bold), - size_cell + size_cell, ])); - let compression_type = CompressionType::from_str(&item.compression)?; table.add_row(Row::new(vec![ Cell::new("Compression").with_style(Attr::Bold), - Cell::new(&compression_type.to_string()) + Cell::new(&compression_type.to_string()), ])); let file_size_cell = match item_path.metadata() { - Ok(metadata) => Cell::new(format_size(metadata.len(), args.options.human_readable).as_str()), - Err(_) => Cell::new("Missing").with_style(Attr::ForegroundColor(color::RED)).with_style(Attr::Bold) + Ok(metadata) => { + Cell::new(format_size(metadata.len(), args.options.human_readable).as_str()) + } + Err(_) => Cell::new("Missing") + .with_style(Attr::ForegroundColor(color::RED)) + .with_style(Attr::Bold), }; table.add_row(Row::new(vec![ Cell::new("File Size").with_style(Attr::Bold), - file_size_cell + file_size_cell, ])); - let compression_engine = compression::get_engine(compression_type).expect("Unable to get compression engine"); + let compression_engine = + compression::get_engine(compression_type).expect("Unable to get compression engine"); let magic = compression_engine.magic(item_path.clone()); let file_magic_cell = match magic { Ok(magic) => Cell::new(magic.as_str()), - Err(e) => Cell::new(&e.to_string()).with_style(Attr::ForegroundColor(color::RED)).with_style(Attr::Bold) + Err(e) => Cell::new(&e.to_string()) + .with_style(Attr::ForegroundColor(color::RED)) + .with_style(Attr::Bold), }; table.add_row(Row::new(vec![ Cell::new("File Magic").with_style(Attr::Bold), - file_magic_cell + file_magic_cell, ])); table.add_row(Row::new(vec![ Cell::new("Tags").with_style(Attr::Bold), - Cell::new(&item_tags.join(" ")) + Cell::new(&item_tags.join(" ")), ])); for meta in db::get_item_meta(conn, &item)? { let meta_name = format!("Meta: {}", &meta.name); table.add_row(Row::new(vec![ Cell::new(meta_name.as_str()).with_style(Attr::Bold), - Cell::new(&meta.value) + Cell::new(&meta.value), ])); } diff --git a/src/modes/common.rs b/src/modes/common.rs index a788c40..ebd6d61 100644 --- a/src/modes/common.rs +++ b/src/modes/common.rs @@ -1,8 +1,8 @@ -use std::env; -use std::collections::HashMap; -use regex::Regex; use humansize::{FormatSizeOptions, BINARY}; use log::debug; +use regex::Regex; +use std::collections::HashMap; +use std::env; pub fn get_meta_from_env() -> HashMap { debug!("MAIN: Getting meta from KEEP_META_*"); @@ -19,15 +19,14 @@ pub fn get_meta_from_env() -> HashMap { } pub fn format_size_human_readable(size: u64) -> String { - let options = FormatSizeOptions::from(BINARY) - .decimal_places(1); + let options = FormatSizeOptions::from(BINARY).decimal_places(1); humansize::format_size(size, options) } pub fn format_size(size: u64, human_readable: bool) -> String { match human_readable { true => format_size_human_readable(size), - false => size.to_string() + false => size.to_string(), } } diff --git a/src/modes/delete.rs b/src/modes/delete.rs index eece610..0ca67a2 100644 --- a/src/modes/delete.rs +++ b/src/modes/delete.rs @@ -1,11 +1,11 @@ -use anyhow::{Context, Result, anyhow}; +use anyhow::{anyhow, Context, Result}; use std::fs; use std::path::PathBuf; use crate::db; +use clap::error::ErrorKind; use clap::Command; use log::{debug, warn}; -use clap::error::ErrorKind; use rusqlite::Connection; pub fn mode_delete( @@ -17,9 +17,17 @@ pub fn mode_delete( data_path: PathBuf, ) -> Result<()> { if ids.is_empty() { - cmd.error(ErrorKind::InvalidValue, "No ID given, you must supply atleast one ID when using --delete").exit(); - } else if ! tags.is_empty() { - cmd.error(ErrorKind::InvalidValue, "Tags given but not supported, you must supply atleast one ID when using --delete").exit(); + cmd.error( + ErrorKind::InvalidValue, + "No ID given, you must supply atleast one ID when using --delete", + ) + .exit(); + } else if !tags.is_empty() { + cmd.error( + ErrorKind::InvalidValue, + "Tags given but not supported, you must supply atleast one ID when using --delete", + ) + .exit(); } for item_id in ids.iter() { @@ -30,7 +38,8 @@ pub fn mode_delete( let mut item_path = data_path.clone(); item_path.push(item_id.to_string()); - fs::remove_file(&item_path).context(anyhow!("Unable to remove item file {:?}", item_path))?; + fs::remove_file(&item_path) + .context(anyhow!("Unable to remove item file {:?}", item_path))?; } else { warn!("Unable to find item {item_id} in database"); } diff --git a/src/modes/get.rs b/src/modes/get.rs index cc6a58e..bccd47b 100644 --- a/src/modes/get.rs +++ b/src/modes/get.rs @@ -1,10 +1,9 @@ - use anyhow::anyhow; -use clap::Command; use crate::compression::CompressionType; -use std::str::FromStr; +use clap::Command; use std::path::PathBuf; +use std::str::FromStr; pub fn mode_get( cmd: &mut Command, @@ -29,9 +28,9 @@ pub fn mode_get( let item_maybe = match tags.is_empty() && meta.is_empty() { true => match ids.iter().next() { Some(item_id) => crate::db::get_item(conn, *item_id)?, - None => crate::db::get_item_last(conn)? + None => crate::db::get_item_last(conn)?, }, - false => crate::db::get_item_matching(conn, tags, &meta)? + false => crate::db::get_item_matching(conn, tags, &meta)?, }; if let Some(item) = item_maybe { diff --git a/src/modes/mod.rs b/src/modes/mod.rs index 6047c33..92c3074 100644 --- a/src/modes/mod.rs +++ b/src/modes/mod.rs @@ -1,5 +1,5 @@ pub mod common; -pub mod get; pub mod delete; -pub mod update; +pub mod get; pub mod status; +pub mod update; diff --git a/src/modes/status.rs b/src/modes/status.rs index fce411e..f4b040f 100644 --- a/src/modes/status.rs +++ b/src/modes/status.rs @@ -1,39 +1,27 @@ -use std::io; -use std::path::PathBuf; -use anyhow::{Context, Result, anyhow}; -use rusqlite::Connection; -use gethostname::gethostname; -use strum::IntoEnumIterator; -use clap::error::ErrorKind; +use anyhow::{anyhow, Context, Result}; use clap::*; -use log::*; use is_terminal::IsTerminal; -use chrono::prelude::*; -use std::os::fd::FromRawFd; -use std::process::Stdio; -use nix::fcntl::{FdFlag}; -use nix::unistd::{close, pipe}; -use nix::Error as NixError; +use std::path::PathBuf; +use strum::IntoEnumIterator; -use crate::compression::CompressionType; -use strum::EnumString; -use crate::compression::program::CompressionEngineProgram; -use crate::compression::COMPRESSION_PROGRAMS; use crate::compression::default_type; -use crate::db; -use crate::modes::common::{format_size, string_column}; +use crate::compression::program::CompressionEngineProgram; +use crate::compression::CompressionType; +use crate::compression::COMPRESSION_PROGRAMS; use std::str::FromStr; -use crate::compression::CompressionEngine; -use prettytable::{Table, Row, Cell, Attr}; -use prettytable::format; -use prettytable::format::{TableFormat, Alignment}; -use prettytable::row; -use prettytable::color; use crate::FORMAT_BOX_CHARS_NO_BORDER_LINE_SEPARATOR; use crate::FORMAT_NO_BORDER_LINE_SEPARATOR; +use prettytable::color; +use prettytable::row; +use prettytable::{Attr, Cell, Row, Table}; -pub fn mode_status(_cmd: &mut Command, args: crate::Args, data_path: PathBuf, db_path: PathBuf) -> Result<()> { +pub fn mode_status( + _cmd: &mut Command, + args: crate::Args, + data_path: PathBuf, + db_path: PathBuf, +) -> Result<()> { let mut path_table = Table::new(); if std::io::stdout().is_terminal() { @@ -49,15 +37,24 @@ pub fn mode_status(_cmd: &mut Command, args: crate::Args, data_path: PathBuf, db path_table.add_row(Row::new(vec![ Cell::new("Data"), - Cell::new(&data_path.into_os_string().into_string().expect("Unable to convert data path to string")) + Cell::new( + &data_path + .into_os_string() + .into_string() + .expect("Unable to convert data path to string"), + ), ])); path_table.add_row(Row::new(vec![ Cell::new("Database"), - Cell::new(&db_path.into_os_string().into_string().expect("Unable to convert DB path to string")) + Cell::new( + &db_path + .into_os_string() + .into_string() + .expect("Unable to convert DB path to string"), + ), ])); - let mut compression_table = Table::new(); if std::io::stdout().is_terminal() { compression_table.set_format(*FORMAT_BOX_CHARS_NO_BORDER_LINE_SEPARATOR); @@ -73,38 +70,40 @@ pub fn mode_status(_cmd: &mut Command, args: crate::Args, data_path: PathBuf, db b->"Compress", b->"Decompress")); - let default_type = match args.item.compression { Some(compression_name) => FromStr::from_str(&compression_name) .context(anyhow!("Invalid compression type {}", compression_name))?, - None => default_type() + None => default_type(), }; for compression_type in CompressionType::iter() { - let compression_program: CompressionEngineProgram = match &COMPRESSION_PROGRAMS[compression_type.clone()] { - Some(compression_program) => compression_program.clone(), - None => CompressionEngineProgram { - program: "".to_string(), - compress: Vec::new(), - decompress: Vec::new(), - supported: true - } - }; + let compression_program: CompressionEngineProgram = + match &COMPRESSION_PROGRAMS[compression_type.clone()] { + Some(compression_program) => compression_program.clone(), + None => CompressionEngineProgram { + program: "".to_string(), + compress: Vec::new(), + decompress: Vec::new(), + supported: true, + }, + }; let is_default = compression_type == default_type; compression_table.add_row(Row::new(vec![ Cell::new(&compression_type.to_string()), match compression_program.supported { - true => Cell::new("Yes").with_style(Attr::ForegroundColor(color::GREEN)), - false => Cell::new("No").with_style(Attr::ForegroundColor(color::RED)) + true => Cell::new("Yes").with_style(Attr::ForegroundColor(color::GREEN)), + false => Cell::new("No").with_style(Attr::ForegroundColor(color::RED)), }, match is_default { - true => Cell::new("Yes").with_style(Attr::ForegroundColor(color::GREEN)), - false => Cell::new("No") + true => Cell::new("Yes").with_style(Attr::ForegroundColor(color::GREEN)), + false => Cell::new("No"), }, match compression_program.program.eq("") { - true => Cell::new("").with_style(Attr::ForegroundColor(color::BRIGHT_BLACK)), + true => { + Cell::new("").with_style(Attr::ForegroundColor(color::BRIGHT_BLACK)) + } false => Cell::new(&compression_program.program), }, Cell::new(&compression_program.compress.join(" ")), diff --git a/src/modes/update.rs b/src/modes/update.rs index 04402ea..a54bb13 100644 --- a/src/modes/update.rs +++ b/src/modes/update.rs @@ -3,10 +3,10 @@ use std::path::PathBuf; use std::str::FromStr; use crate::db; -use clap::Command; -use clap::error::ErrorKind; -use log::{debug, info}; use crate::CompressionType; +use clap::error::ErrorKind; +use clap::Command; +use log::{debug, info}; use rusqlite::Connection; pub fn mode_update( @@ -18,7 +18,11 @@ pub fn mode_update( data_path: PathBuf, ) -> Result<()> { if ids.is_empty() { - cmd.error(ErrorKind::InvalidValue, "No ID given, you must supply exactly one ID when using --update").exit(); + cmd.error( + ErrorKind::InvalidValue, + "No ID given, you must supply exactly one ID when using --update", + ) + .exit(); } else if ids.len() > 1 { cmd.error(ErrorKind::InvalidValue, "More than one ID given, you must supply exactly one ID or atleast one tag when using --update").exit(); } @@ -29,7 +33,7 @@ pub fn mode_update( let mut item = item_maybe.expect("Unable to find item in database"); debug!("MAIN: Found item {:?}", item); - if ! tags.is_empty() { + if !tags.is_empty() { debug!("MAIN: Updating item tags"); db::set_item_tags(conn, item.clone(), tags)?; } @@ -43,12 +47,16 @@ pub fn mode_update( if item_file_metadata.is_ok() { debug!("MAIN: Updating stream size of {:?}", item_path); let compression_type = CompressionType::from_str(&item.compression)?; - let compression_engine = crate::compression::get_engine(compression_type).expect("Unable to get compression engine"); + let compression_engine = crate::compression::get_engine(compression_type) + .expect("Unable to get compression engine"); let size = compression_engine.size(item_path)? as i64; item.size = Some(size); db::update_item(&conn, item.clone())?; } else { - debug!("MAIN: Unable to update size of item due to missing file {:?}", item_path); + debug!( + "MAIN: Unable to update size of item due to missing file {:?}", + item_path + ); } } @@ -58,7 +66,7 @@ pub fn mode_update( let meta = db::Meta { id: item.id.unwrap(), name: kv.key.to_string(), - value: kv.value.to_string() + value: kv.value.to_string(), }; db::store_meta(conn, meta)?; }