use crate::Args; use crate::compression_engine::CompressionType; use crate::db::Item; use crate::db::Meta; use crate::db::store_meta; use crate::meta_plugin::MetaPluginType; use anyhow::{anyhow, Result}; use clap::Command; use clap::error::ErrorKind; use humansize::{BINARY, FormatSizeOptions}; use log::debug; use prettytable::format::TableFormat; use regex::Regex; use rusqlite::Connection; use std::collections::HashMap; use std::env; use std::str::FromStr; use strum::IntoEnumIterator; pub fn get_meta_from_env() -> HashMap { debug!("COMMON: Getting meta from KEEP_META_*"); let re = Regex::new(r"^KEEP_META_(.+)$").unwrap(); let mut meta_env: HashMap = HashMap::new(); for (key, value) in env::vars() { if let Some(meta_name_caps) = re.captures(key.as_str()) { let name = String::from(meta_name_caps.get(1).unwrap().as_str()); // Ignore KEEP_META_PLUGINS if name != "PLUGINS" { debug!("COMMON: Found meta: {}={}", name.clone(), value.clone()); meta_env.insert(name, value.clone()); } } } meta_env } pub fn format_size_human_readable(size: u64) -> String { 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(), } } pub fn string_column(s: String, column_width: usize) -> String { if column_width > 0 { match s.char_indices().nth(column_width) { None => s.to_string(), Some((idx, _)) => s[..idx].to_string(), } } else { s.to_string() } } pub fn size_column(size: u64, human_readable: bool, column_width: usize) -> String { string_column(format_size(size, human_readable), column_width) } #[derive(Debug, Eq, PartialEq, Clone, strum::EnumIter, strum::Display, strum::EnumString)] #[strum(ascii_case_insensitive)] pub enum ColumnType { Id, Time, Size, Compression, FileSize, FilePath, Tags, Meta, } impl ColumnType { /// Returns a Result with error message if the string is not a valid ColumnType pub fn from_str(s: &str) -> anyhow::Result { Ok(Self::try_from(s)?) } } // impl TryFrom<&str> for ColumnType is already implemented by strum_macros // so we remove this conflicting implementation pub fn get_format_box_chars_no_border_line_separator() -> TableFormat { prettytable::format::FormatBuilder::new() .column_separator('│') .borders('│') .separators( &[prettytable::format::LinePosition::Top], prettytable::format::LineSeparator::new('─', '┬', '┌', '┐'), ) .separators( &[prettytable::format::LinePosition::Title], prettytable::format::LineSeparator::new('─', '┼', '├', '┤'), ) .separators( &[prettytable::format::LinePosition::Bottom], prettytable::format::LineSeparator::new('─', '┴', '└', '┘'), ) .padding(1, 1) .build() } pub fn get_digest_type_meta(digest_type: MetaPluginType) -> String { format!("digest_{}", digest_type.to_string().to_lowercase()) } pub fn store_item_meta_value( conn: &mut Connection, item: Item, meta_name: String, meta_value: String, ) -> Result<(), anyhow::Error> { // Save digest to meta let meta = Meta { id: item.id.ok_or_else(|| anyhow!("Item missing ID"))?, name: meta_name, value: meta_value, }; store_meta(conn, meta)?; Ok(()) } pub fn store_item_digest_value( conn: &mut Connection, item: Item, digest_type: MetaPluginType, digest_value: String, ) -> Result<(), anyhow::Error> { // Save digest to meta let digest_meta_name = get_digest_type_meta(digest_type); let digest_meta = Meta { id: item.id.unwrap(), name: digest_meta_name, value: digest_value, }; store_meta(conn, digest_meta)?; Ok(()) } pub fn cmd_args_digest_type(cmd: &mut Command, args: &Args) -> MetaPluginType { let digest_name = args .item .digest .clone() .unwrap_or(MetaPluginType::DigestSha256.to_string()); let digest_type_opt = MetaPluginType::from_str(&digest_name); if digest_type_opt.is_err() { cmd.error( ErrorKind::InvalidValue, format!("Invalid digest algorithm '{}'. Use 'sha256' or 'md5'", digest_name), ) .exit(); } digest_type_opt.unwrap() } pub fn cmd_args_compression_type(cmd: &mut Command, args: &Args) -> CompressionType { let compression_name = args .item .compression .clone() .unwrap_or(CompressionType::LZ4.to_string()); let compression_type_opt = CompressionType::from_str(&compression_name); if compression_type_opt.is_err() { cmd.error( ErrorKind::InvalidValue, format!("Invalid compression algorithm '{}'. Supported algorithms: lz4, gzip, xz, zstd", compression_name), ) .exit(); } compression_type_opt.unwrap() } pub fn cmd_args_meta_plugin_types(cmd: &mut Command, args: &Args) -> Vec { let mut meta_plugin_types = Vec::new(); // Handle comma-separated values in each meta_plugins argument for meta_plugin_names_str in &args.item.meta_plugins { let meta_plugin_names: Vec<&str> = meta_plugin_names_str.split(',').collect(); for name in meta_plugin_names { let trimmed_name = name.trim(); if trimmed_name.is_empty() { continue; } // Try to find the MetaPluginType by meta name let mut found = false; for meta_plugin_type in MetaPluginType::iter() { let mut meta_plugin = crate::meta_plugin::get_meta_plugin(meta_plugin_type.clone()); if meta_plugin.meta_name() == trimmed_name { meta_plugin_types.push(meta_plugin_type); found = true; break; } } if !found { cmd.error( ErrorKind::InvalidValue, format!("Unknown meta plugin type: {}", trimmed_name), ) .exit(); } } } meta_plugin_types }