Files
keep/src/modes/common.rs
Andrew Phillips 7c1c5bd9c9 fix: resolve compilation errors by adding missing ErrorKind import and removing unused imports
Co-authored-by: aider (openai/andrew/openrouter/anthropic/claude-sonnet-4) <aider@aider.chat>
2025-08-10 11:34:44 -03:00

228 lines
6.8 KiB
Rust

use crate::Args;
use crate::compression_engine::CompressionType;
use crate::meta_plugin::MetaPluginType;
use clap::Command;
use clap::error::ErrorKind;
use log::debug;
use prettytable::format::TableFormat;
use regex::Regex;
use std::collections::HashMap;
use std::env;
use std::str::FromStr;
use strum::IntoEnumIterator;
use serde::{Deserialize, Serialize};
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 {
const UNITS: &[&str] = &["", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei"];
const THRESHOLD: u64 = 1024;
if size == 0 {
return "0".to_string();
}
let mut size_f = size as f64;
let mut unit_index = 0;
while size_f >= THRESHOLD as f64 && unit_index < UNITS.len() - 1 {
size_f /= THRESHOLD as f64;
unit_index += 1;
}
if unit_index == 0 {
format!("{}", size)
} else if size_f.fract() == 0.0 {
format!("{}{}", size_f as u64, UNITS[unit_index])
} else {
format!("{:.1}{}", size_f, UNITS[unit_index])
}
}
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 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()
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum OutputFormat {
Table,
Json,
Yaml,
}
impl FromStr for OutputFormat {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"table" => Ok(OutputFormat::Table),
"json" => Ok(OutputFormat::Json),
"yaml" => Ok(OutputFormat::Yaml),
_ => Err(anyhow::anyhow!("Invalid output format. Supported formats: table, json, yaml")),
}
}
}
pub fn get_output_format(args: &Args) -> OutputFormat {
args.options.output_format
.as_ref()
.and_then(|s| OutputFormat::from_str(s).ok())
.unwrap_or(OutputFormat::Table)
}
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
}