diff --git a/src/main.rs b/src/main.rs index 3da652e..a3e2de2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -304,7 +304,7 @@ fn main() -> Result<(), Error> { debug!("MAIN: DB opened successfully"); match mode { - KeepModes::Save => mode_save(&mut cmd, args, ids, tags, conn, data_path)?, + KeepModes::Save => crate::modes::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)? } @@ -324,144 +324,6 @@ fn main() -> Result<(), Error> { 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(); - } - - if tags.is_empty() { - tags.push("none".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(); - } - - let compression_type = compression_type_opt.unwrap(); - debug!("MAIN: Compression type: {}", compression_type); - - let mut item = db::Item { - id: None, - ts: Utc::now(), - size: None, - compression: compression_type.to_string(), - }; - - let id = db::insert_item(&conn, item.clone())?; - item.id = Some(id); - debug!("MAIN: Added item {:?}", item.clone()); - - if !args.options.quiet { - if std::io::stderr().is_terminal() { - let mut t = term::stderr().unwrap(); - t.reset().unwrap_or(()); - t.attr(term::Attr::Bold).unwrap_or(()); - write!(t, "KEEP:").unwrap_or(()); - t.reset().unwrap_or(()); - write!(t, " New item ").unwrap_or(()); - t.attr(term::Attr::Bold).unwrap_or(()); - write!(t, "{id}")?; - t.reset().unwrap_or(()); - write!(t, " tags: ")?; - t.attr(term::Attr::Bold).unwrap_or(()); - write!(t, "{}", tags.join(" "))?; - t.reset().unwrap_or(()); - writeln!(t)?; - std::io::stderr().flush()?; - } else { - let mut t = std::io::stderr(); - writeln!(t, "KEEP: New item: {} tags: {:?}", id, tags)?; - } - } - - db::set_item_tags(&conn, item.clone(), tags)?; - - let mut item_meta: HashMap = get_meta_from_env(); - - if let Ok(hostname) = gethostname().into_string() { - if !item_meta.contains_key("hostname") { - item_meta.insert("hostname".to_string(), hostname); - } - } - - for item in args.item.meta.iter() { - let item = item.clone(); - item_meta.insert(item.key, item.value); - } - - for kv in item_meta.iter() { - let meta = db::Meta { - id: item.id.unwrap(), - name: kv.0.to_string(), - value: kv.1.to_string(), - }; - db::store_meta(&conn, meta)?; - } - - let mut item_path = data_path.clone(); - item_path.push(id.to_string()); - - let mut stdin = io::stdin().lock(); - let mut stdout = io::stdout().lock(); - let mut buffer = [0; libc::BUFSIZ as usize]; - - 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 { - let n = stdin.read(&mut buffer[..libc::BUFSIZ as usize])?; - - if n == 0 { - debug!("MAIN: EOF on STDIN"); - break; - } - - stdout.write_all(&buffer[..n])?; - item_out.write_all(&buffer[..n])?; - item.size = match item.size { - None => Some(n as i64), - Some(prev_n) => Some(prev_n + n as i64), - }; - } - debug!("MAIN: Ending IO loop"); - - stdout.flush()?; - item_out.flush()?; - - db::update_item(&conn, item.clone())?; - - Ok(()) -} fn mode_diff( cmd: &mut clap::Command, diff --git a/src/modes/mod.rs b/src/modes/mod.rs index 92c3074..2d4e6b4 100644 --- a/src/modes/mod.rs +++ b/src/modes/mod.rs @@ -1,5 +1,6 @@ pub mod common; pub mod delete; pub mod get; +pub mod save; pub mod status; pub mod update; diff --git a/src/modes/save.rs b/src/modes/save.rs new file mode 100644 index 0000000..f8b8f31 --- /dev/null +++ b/src/modes/save.rs @@ -0,0 +1,165 @@ +use anyhow::{anyhow, Context, Result}; +use std::collections::HashMap; +use std::fs; +use std::io; +use std::io::{BufWriter, Read, Write}; +use std::path::PathBuf; +use std::str::FromStr; + +use clap::error::ErrorKind; +use clap::Command; +use log::{debug, warn}; +use rusqlite::Connection; + +use crate::compression::CompressionType; +use crate::db::{self, Item, Meta}; +use crate::modes::common::{get_meta_from_env, format_size, string_column, format_size_human_readable}; +use crate::modes::common::{Column, ColumnType, KeyValue, NumberOrString, ProjectDirs}; +use crate::modes::common::{FORMAT_BOX_CHARS_NO_BORDER_LINE_SEPARATOR, FORMAT_NO_BORDER_LINE_SEPARATOR}; +use crate::modes::common::{InsertItem, UpdateItem, DeleteItem, StoreMeta}; +use crate::modes::common::{GetItem, GetItemLast, GetItemMatching}; +use crate::modes::common::{GetItemTags, GetItemMeta}; +use crate::modes::common::{GetHostname, GetProjectDirs}; +use crate::modes::common::{FormatSizeHumanReadable}; +use crate::modes::common::{InsertTag, DeleteItemTags, SetItemTags}; +use crate::modes::common::{InsertMeta, QueryUpsertMeta, QueryDeleteMeta, StoreMeta}; +use crate::modes::common::{GetItemMeta}; + +pub fn mode_save( + cmd: &mut Command, + args: crate::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_type_opt = CompressionType::from_str(&compression_name); + if compression_type_opt.is_err() { + cmd.error( + ErrorKind::InvalidValue, + format!("Unknown compression type: {}", compression_name), + ) + .exit(); + } + + let compression_type = compression_type_opt.unwrap(); + debug!("MAIN: Compression type: {}", compression_type); + + let mut item = db::Item { + id: None, + ts: Utc::now(), + size: None, + compression: compression_type.to_string(), + }; + + let id = db::insert_item(&conn, item.clone())?; + item.id = Some(id); + debug!("MAIN: Added item {:?}", item.clone()); + + if !args.options.quiet { + if std::io::stderr().is_terminal() { + let mut t = term::stderr().unwrap(); + t.reset().unwrap_or(()); + t.attr(term::Attr::Bold).unwrap_or(()); + write!(t, "KEEP:").unwrap_or(()); + t.reset().unwrap_or(()); + write!(t, " New item ").unwrap_or(()); + t.attr(term::Attr::Bold).unwrap_or(()); + write!(t, "{id}")?; + t.reset().unwrap_or(()); + write!(t, " tags: ")?; + t.attr(term::Attr::Bold).unwrap_or(()); + write!(t, "{}", tags.join(" "))?; + t.reset().unwrap_or(()); + writeln!(t)?; + std::io::stderr().flush()?; + } else { + let mut t = std::io::stderr(); + writeln!(t, "KEEP: New item: {} tags: {:?}", id, tags)?; + } + } + + db::set_item_tags(&conn, item.clone(), tags)?; + + let mut item_meta: HashMap = get_meta_from_env(); + + if let Ok(hostname) = gethostname().into_string() { + if !item_meta.contains_key("hostname") { + item_meta.insert("hostname".to_string(), hostname); + } + } + + for item in args.item.meta.iter() { + let item = item.clone(); + item_meta.insert(item.key, item.value); + } + + for kv in item_meta.iter() { + let meta = db::Meta { + id: item.id.unwrap(), + name: kv.0.to_string(), + value: kv.1.to_string(), + }; + db::store_meta(&conn, meta)?; + } + + let mut item_path = data_path.clone(); + item_path.push(id.to_string()); + + let mut stdin = io::stdin().lock(); + let mut stdout = io::stdout().lock(); + let mut buffer = [0; libc::BUFSIZ as usize]; + + 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 { + let n = stdin.read(&mut buffer[..libc::BUFSIZ as usize])?; + + if n == 0 { + debug!("MAIN: EOF on STDIN"); + break; + } + + stdout.write_all(&buffer[..n])?; + item_out.write_all(&buffer[..n])?; + item.size = match item.size { + None => Some(n as i64), + Some(prev_n) => Some(prev_n + n as i64), + }; + } + debug!("MAIN: Ending IO loop"); + + stdout.flush()?; + item_out.flush()?; + + db::update_item(&conn, item.clone())?; + + Ok(()) +}