pub mod filter_sort;
pub mod sprint;
pub use filter_sort::{
count_filtered_cards, filter_and_sort_cards, ArchivedCardListFilter, CardListFilter,
};
use crate::{Board, Card, Column, Sprint};
use std::collections::HashSet;
use uuid::Uuid;
pub struct CardQueryBuilder<'a> {
cards: &'a [Card],
columns: &'a [Column],
sprints: &'a [Sprint],
board: &'a Board,
column_id: Option<Uuid>,
sprint_filter: Option<HashSet<Uuid>>,
hide_assigned: bool,
search_query: Option<String>,
}
impl<'a> CardQueryBuilder<'a> {
pub fn new(
cards: &'a [Card],
columns: &'a [Column],
sprints: &'a [Sprint],
board: &'a Board,
) -> Self {
Self {
cards,
columns,
sprints,
board,
column_id: None,
sprint_filter: None,
hide_assigned: false,
search_query: None,
}
}
pub fn in_column(mut self, column_id: Uuid) -> Self {
self.column_id = Some(column_id);
self
}
pub fn in_sprints(mut self, sprint_ids: impl IntoIterator<Item = Uuid>) -> Self {
self.sprint_filter = Some(sprint_ids.into_iter().collect());
self
}
pub fn hide_assigned(mut self) -> Self {
self.hide_assigned = true;
self
}
pub fn search(mut self, query: impl Into<String>) -> Self {
let q = query.into();
if !q.is_empty() {
self.search_query = Some(q);
}
self
}
pub fn execute(self) -> Vec<Uuid> {
let filter = CardListFilter {
board_id: Some(self.board.id),
column_id: self.column_id,
sprint_ids: self.sprint_filter,
hide_assigned: self.hide_assigned,
search: self.search_query,
..Default::default()
};
filter_and_sort_cards(
self.cards,
self.columns,
self.sprints,
Some(self.board),
&filter,
)
.into_iter()
.map(|c| c.id)
.collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{SortField, SortOrder};
fn create_test_board() -> Board {
let mut board = Board::new("Test", None::<String>);
board.update_task_sort(SortField::Default, SortOrder::Ascending);
board
}
fn create_test_column(board: &Board, name: &str, position: i32) -> Column {
Column::new(board.id, name.to_string(), position)
}
fn create_test_card(board: &mut Board, column: &Column, title: &str, position: i32) -> Card {
Card::new(board, column.id, title.to_string(), position)
}
#[test]
fn test_filter_and_sort_cards_basic() {
let mut board = create_test_board();
let column = create_test_column(&board, "Todo", 0);
let card1 = create_test_card(&mut board, &column, "Task 1", 0);
let card2 = create_test_card(&mut board, &column, "Task 2", 1);
let columns = vec![column.clone()];
let cards = vec![card1.clone(), card2.clone()];
let result = CardQueryBuilder::new(&cards, &columns, &[], &board).execute();
assert_eq!(result.len(), 2);
assert!(result.contains(&card1.id));
assert!(result.contains(&card2.id));
}
#[test]
fn test_filter_by_column() {
let mut board = create_test_board();
let column1 = create_test_column(&board, "Todo", 0);
let column2 = create_test_column(&board, "Done", 1);
let card1 = create_test_card(&mut board, &column1, "Task 1", 0);
let card2 = create_test_card(&mut board, &column2, "Task 2", 0);
let columns = vec![column1.clone(), column2.clone()];
let cards = vec![card1.clone(), card2.clone()];
let result = CardQueryBuilder::new(&cards, &columns, &[], &board)
.in_column(column1.id)
.execute();
assert_eq!(result.len(), 1);
assert_eq!(result[0], card1.id);
}
#[test]
fn test_filter_by_search() {
let mut board = create_test_board();
let column = create_test_column(&board, "Todo", 0);
let card1 = create_test_card(&mut board, &column, "Fix bug", 0);
let card2 = create_test_card(&mut board, &column, "Add feature", 1);
let columns = vec![column.clone()];
let cards = vec![card1.clone(), card2.clone()];
let result = CardQueryBuilder::new(&cards, &columns, &[], &board)
.search("bug")
.execute();
assert_eq!(result.len(), 1);
assert_eq!(result[0], card1.id);
}
#[test]
fn test_hide_assigned_cards() {
let mut board = create_test_board();
let column = create_test_column(&board, "Todo", 0);
let mut card1 = create_test_card(&mut board, &column, "Assigned", 0);
card1.sprint_id = Some(Uuid::new_v4());
let card2 = create_test_card(&mut board, &column, "Unassigned", 1);
let columns = vec![column.clone()];
let cards = vec![card1.clone(), card2.clone()];
let result = CardQueryBuilder::new(&cards, &columns, &[], &board)
.hide_assigned()
.execute();
assert_eq!(result.len(), 1);
assert_eq!(result[0], card2.id);
}
#[test]
fn test_query_builder() {
let mut board = create_test_board();
let column = create_test_column(&board, "Todo", 0);
let card1 = create_test_card(&mut board, &column, "Fix bug", 0);
let card2 = create_test_card(&mut board, &column, "Add feature", 1);
let columns = vec![column.clone()];
let cards = vec![card1.clone(), card2.clone()];
let result = CardQueryBuilder::new(&cards, &columns, &[], &board)
.search("bug")
.execute();
assert_eq!(result.len(), 1);
assert_eq!(result[0], card1.id);
}
#[test]
fn test_query_builder_with_column() {
let mut board = create_test_board();
let column1 = create_test_column(&board, "Todo", 0);
let column2 = create_test_column(&board, "Done", 1);
let card1 = create_test_card(&mut board, &column1, "Task 1", 0);
let card2 = create_test_card(&mut board, &column2, "Task 2", 0);
let columns = vec![column1.clone(), column2.clone()];
let cards = vec![card1.clone(), card2.clone()];
let result = CardQueryBuilder::new(&cards, &columns, &[], &board)
.in_column(column1.id)
.execute();
assert_eq!(result.len(), 1);
assert_eq!(result[0], card1.id);
}
}