- Import `anyhow`, `clap::Command`, `log::debug`, and I/O traits - Fix all `Result` return types to include error type `anyhow::Error` - Replace `anyhow::anyhow!` with `anyhow!` macro calls - Fix transaction handling in `mode_save` - Add missing trait imports for I/O operations and string parsing Co-authored-by: aider (openai/andrew/openrouter/qwen/qwen3-coder) <aider@aider.chat>
218 lines
6.4 KiB
Rust
218 lines
6.4 KiB
Rust
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<String, String> {
|
|
debug!("COMMON: Getting meta from KEEP_META_*");
|
|
let re = Regex::new(r"^KEEP_META_(.+)$").unwrap();
|
|
let mut meta_env: HashMap<String, String> = 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<Self> {
|
|
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<MetaPluginType> {
|
|
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
|
|
}
|