use crate::cli::error::{CliError, CliResult};
use crate::domain::tag::Tag;
use std::collections::HashSet;
use tracing::{debug, instrument};
pub struct ArgumentProcessor;
impl ArgumentProcessor {
#[instrument(level = "trace")]
pub fn parse_tag_string(tag_str: &Option<String>) -> Option<HashSet<Tag>> {
Tag::parse_tag_option(tag_str.as_ref().map(|s| s.as_str())).unwrap_or_else(|e| {
debug!("Failed to parse tags: {}", e);
None
})
}
#[instrument(level = "trace")]
pub fn apply_prefix_tags(
base_tags: Option<HashSet<Tag>>,
prefix_tags: Option<HashSet<Tag>>,
) -> Option<HashSet<Tag>> {
match (base_tags, prefix_tags) {
(None, None) => None,
(Some(base), None) => Some(base),
(None, Some(prefix)) => Some(prefix),
(Some(mut base), Some(prefix)) => {
base.extend(prefix);
Some(base)
}
}
}
pub fn parse_tags_with_error_handling(tag_str: &Option<String>) -> CliResult<HashSet<Tag>> {
match Tag::parse_tag_option(tag_str.as_deref()) {
Ok(Some(tags)) => Ok(tags),
Ok(None) => Ok(HashSet::new()),
Err(e) => Err(CliError::InvalidInput(format!(
"Failed to parse tags: {}",
e
))),
}
}
#[instrument(level = "trace")]
pub fn process_search_tag_parameters(
tags_exact: &Option<String>,
tags_exact_prefix: &Option<String>,
tags_all: &Option<String>,
tags_all_prefix: &Option<String>,
tags_all_not: &Option<String>,
tags_all_not_prefix: &Option<String>,
tags_any: &Option<String>,
tags_any_prefix: &Option<String>,
tags_any_not: &Option<String>,
tags_any_not_prefix: &Option<String>,
) -> SearchTagParams {
SearchTagParams {
exact_tags: Self::apply_prefix_tags(
Self::parse_tag_string(tags_exact),
Self::parse_tag_string(tags_exact_prefix),
),
all_tags: Self::apply_prefix_tags(
Self::parse_tag_string(tags_all),
Self::parse_tag_string(tags_all_prefix),
),
all_not_tags: Self::apply_prefix_tags(
Self::parse_tag_string(tags_all_not),
Self::parse_tag_string(tags_all_not_prefix),
),
any_tags: Self::apply_prefix_tags(
Self::parse_tag_string(tags_any),
Self::parse_tag_string(tags_any_prefix),
),
any_not_tags: Self::apply_prefix_tags(
Self::parse_tag_string(tags_any_not),
Self::parse_tag_string(tags_any_not_prefix),
),
}
}
}
#[derive(Debug)]
pub struct SearchTagParams {
pub exact_tags: Option<HashSet<Tag>>,
pub all_tags: Option<HashSet<Tag>>,
pub all_not_tags: Option<HashSet<Tag>>,
pub any_tags: Option<HashSet<Tag>>,
pub any_not_tags: Option<HashSet<Tag>>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn given_valid_tag_string_when_parse_tag_string_then_returns_tag_set() {
let tag_str = Some("tag1,tag2,tag3".to_string());
let result = ArgumentProcessor::parse_tag_string(&tag_str);
assert!(result.is_some());
let tags = result.unwrap();
assert_eq!(tags.len(), 3);
assert!(tags.contains(&Tag::new("tag1").unwrap()));
assert!(tags.contains(&Tag::new("tag2").unwrap()));
assert!(tags.contains(&Tag::new("tag3").unwrap()));
}
#[test]
fn given_none_tag_string_when_parse_tag_string_then_returns_none() {
let result = ArgumentProcessor::parse_tag_string(&None);
assert!(result.is_none());
}
#[test]
fn given_base_and_prefix_tags_when_apply_prefix_tags_then_returns_merged_set() {
let base = {
let mut set = HashSet::new();
set.insert(Tag::new("base1").unwrap());
set.insert(Tag::new("base2").unwrap());
Some(set)
};
let prefix = {
let mut set = HashSet::new();
set.insert(Tag::new("prefix1").unwrap());
Some(set)
};
let result = ArgumentProcessor::apply_prefix_tags(base, prefix);
assert!(result.is_some());
let combined = result.unwrap();
assert_eq!(combined.len(), 3);
assert!(combined.contains(&Tag::new("base1").unwrap()));
assert!(combined.contains(&Tag::new("base2").unwrap()));
assert!(combined.contains(&Tag::new("prefix1").unwrap()));
}
#[test]
fn given_only_base_tags_when_apply_prefix_tags_then_returns_base_set() {
let base = {
let mut set = HashSet::new();
set.insert(Tag::new("base1").unwrap());
Some(set)
};
let result = ArgumentProcessor::apply_prefix_tags(base, None);
assert!(result.is_some());
let tags = result.unwrap();
assert_eq!(tags.len(), 1);
assert!(tags.contains(&Tag::new("base1").unwrap()));
}
#[test]
fn given_only_prefix_tags_when_apply_prefix_tags_then_returns_prefix_set() {
let prefix = {
let mut set = HashSet::new();
set.insert(Tag::new("prefix1").unwrap());
Some(set)
};
let result = ArgumentProcessor::apply_prefix_tags(None, prefix);
assert!(result.is_some());
let tags = result.unwrap();
assert_eq!(tags.len(), 1);
assert!(tags.contains(&Tag::new("prefix1").unwrap()));
}
#[test]
fn given_no_tags_when_apply_prefix_tags_then_returns_none() {
let result = ArgumentProcessor::apply_prefix_tags(None, None);
assert!(result.is_none());
}
#[test]
fn given_valid_tag_string_when_parse_tags_with_error_handling_then_returns_ok_result() {
let tag_str = Some("tag1,tag2".to_string());
let result = ArgumentProcessor::parse_tags_with_error_handling(&tag_str);
assert!(result.is_ok());
let tags = result.unwrap();
assert_eq!(tags.len(), 2);
}
#[test]
fn given_none_tag_string_when_parse_tags_with_error_handling_then_returns_ok_none() {
let result = ArgumentProcessor::parse_tags_with_error_handling(&None);
assert!(result.is_ok());
let tags = result.unwrap();
assert!(tags.is_empty());
}
}