use crate::app::host_state::GroupBy;
use crate::ssh_config::model::HostEntry;
#[derive(Debug, Clone, PartialEq)]
pub struct DisplayTag {
pub name: String,
pub is_user: bool,
}
pub fn select_display_tags(
host: &HostEntry,
group_by: &GroupBy,
detail_mode: bool,
) -> Vec<DisplayTag> {
let group_name = match group_by {
GroupBy::Provider => host.provider.clone(),
GroupBy::Tag(t) => Some(t.clone()),
GroupBy::None => None,
};
let not_group = |t: &&str| {
group_name
.as_ref()
.is_none_or(|g| !t.eq_ignore_ascii_case(g))
};
let user_tags: Vec<DisplayTag> = host
.tags
.iter()
.map(|t| t.as_str())
.filter(not_group)
.map(|t| DisplayTag {
name: t.to_string(),
is_user: true,
})
.collect();
let limit = if detail_mode { 1 } else { 3 };
let is_grouped = !matches!(group_by, GroupBy::None);
if is_grouped {
user_tags.into_iter().take(limit).collect()
} else {
let provider_tags = host
.provider_tags
.iter()
.chain(host.provider.iter())
.map(|t| DisplayTag {
name: t.to_string(),
is_user: false,
});
user_tags
.into_iter()
.chain(provider_tags)
.take(limit)
.collect()
}
}
#[derive(Default)]
pub struct TagState {
pub input: Option<String>,
pub cursor: usize,
pub list: Vec<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BulkTagAction {
Leave,
AddToAll,
RemoveFromAll,
}
impl BulkTagAction {
pub fn cycle(self) -> Self {
match self {
BulkTagAction::Leave => BulkTagAction::AddToAll,
BulkTagAction::AddToAll => BulkTagAction::RemoveFromAll,
BulkTagAction::RemoveFromAll => BulkTagAction::Leave,
}
}
pub fn glyph(self) -> &'static str {
match self {
BulkTagAction::Leave => "[~]",
BulkTagAction::AddToAll => "[x]",
BulkTagAction::RemoveFromAll => "[ ]",
}
}
}
#[derive(Debug, Clone)]
pub struct BulkTagRow {
pub tag: String,
pub initial_count: usize,
pub action: BulkTagAction,
}
#[derive(Debug, Default)]
pub struct BulkTagEditorState {
pub rows: Vec<BulkTagRow>,
pub aliases: Vec<String>,
pub skipped_included: Vec<String>,
pub new_tag_input: Option<String>,
pub new_tag_cursor: usize,
pub initial_actions: Vec<BulkTagAction>,
}
impl BulkTagEditorState {
pub fn is_dirty(&self) -> bool {
debug_assert!(
self.rows.len() >= self.initial_actions.len(),
"rows must be append-only after baseline capture; \
shorter rows breaks the dirty-check"
);
if self.rows.len() != self.initial_actions.len() {
return self
.rows
.iter()
.skip(self.initial_actions.len())
.any(|r| r.action != BulkTagAction::Leave)
|| self
.rows
.iter()
.zip(self.initial_actions.iter())
.any(|(r, baseline)| r.action != *baseline);
}
self.rows
.iter()
.zip(self.initial_actions.iter())
.any(|(r, baseline)| r.action != *baseline)
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct BulkTagApplyResult {
pub changed_hosts: usize,
pub added: usize,
pub removed: usize,
pub skipped_included: usize,
}