use crate::models::UpdateTaskRequest;
#[derive(Debug, Clone)]
pub struct TaskUpdateBuilder {
updates: Vec<String>,
}
impl TaskUpdateBuilder {
#[must_use]
pub fn new() -> Self {
Self {
updates: Vec::new(),
}
}
#[must_use]
pub fn from_request(request: &UpdateTaskRequest) -> Self {
let mut builder = Self::new();
if request.title.is_some() {
builder = builder.add_field("title");
}
if request.notes.is_some() {
builder = builder.add_field("notes");
}
if request.start_date.is_some() {
builder = builder.add_field("startDate");
}
if request.deadline.is_some() {
builder = builder.add_field("deadline");
}
if request.status.is_some() {
builder = builder.add_field("status");
}
if request.project_uuid.is_some() {
builder = builder.add_field("project");
}
if request.area_uuid.is_some() {
builder = builder.add_field("area");
}
builder
}
#[must_use]
pub fn add_field(mut self, field_name: &str) -> Self {
self.updates.push(format!("{field_name} = ?"));
self
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.updates.is_empty()
}
#[must_use]
pub fn len(&self) -> usize {
self.updates.len()
}
#[must_use]
pub fn build_query_string(&self) -> String {
if self.updates.is_empty() {
return "UPDATE TMTask SET userModificationDate = ? WHERE uuid = ?".to_string();
}
let mut all_updates = self.updates.clone();
all_updates.push("userModificationDate = ?".to_string());
format!(
"UPDATE TMTask SET {} WHERE uuid = ?",
all_updates.join(", ")
)
}
#[must_use]
pub fn fields(&self) -> Vec<String> {
self.updates
.iter()
.map(|u| u.split(" = ").next().unwrap_or("").to_string())
.collect()
}
}
impl Default for TaskUpdateBuilder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::models::TaskStatus;
use crate::models::ThingsId;
use chrono::NaiveDate;
#[test]
fn test_task_update_builder_empty() {
let builder = TaskUpdateBuilder::new();
assert!(builder.is_empty());
assert_eq!(builder.len(), 0);
let query = builder.build_query_string();
assert!(query.contains("userModificationDate = ?"));
}
#[test]
fn test_task_update_builder_single_field() {
let builder = TaskUpdateBuilder::new().add_field("title");
assert!(!builder.is_empty());
assert_eq!(builder.len(), 1);
let query = builder.build_query_string();
assert!(query.contains("title = ?"));
assert!(query.contains("userModificationDate = ?"));
}
#[test]
fn test_task_update_builder_multiple_fields() {
let builder = TaskUpdateBuilder::new()
.add_field("title")
.add_field("notes")
.add_field("status");
assert_eq!(builder.len(), 3);
let query = builder.build_query_string();
assert!(query.contains("title = ?"));
assert!(query.contains("notes = ?"));
assert!(query.contains("status = ?"));
}
#[test]
fn test_task_update_builder_from_request() {
let request = UpdateTaskRequest {
uuid: ThingsId::new_v4(),
title: Some("Updated Title".to_string()),
notes: Some("Updated Notes".to_string()),
start_date: Some(NaiveDate::from_ymd_opt(2025, 1, 15).unwrap()),
deadline: Some(NaiveDate::from_ymd_opt(2025, 2, 1).unwrap()),
status: Some(TaskStatus::Incomplete),
project_uuid: Some(ThingsId::new_v4()),
area_uuid: Some(ThingsId::new_v4()),
tags: Some(vec!["tag1".to_string(), "tag2".to_string()]),
};
let builder = TaskUpdateBuilder::from_request(&request);
assert_eq!(builder.len(), 7);
let query = builder.build_query_string();
assert!(query.contains("title = ?"));
assert!(query.contains("notes = ?"));
assert!(query.contains("startDate = ?"));
assert!(query.contains("deadline = ?"));
assert!(query.contains("status = ?"));
assert!(query.contains("project = ?"));
assert!(query.contains("area = ?"));
assert!(!query.contains("cachedTags = ?"));
}
#[test]
fn test_task_update_builder_from_partial_request() {
let request = UpdateTaskRequest {
uuid: ThingsId::new_v4(),
title: Some("Updated Title".to_string()),
notes: None,
start_date: None,
deadline: None,
status: None,
project_uuid: None,
area_uuid: None,
tags: None,
};
let builder = TaskUpdateBuilder::from_request(&request);
assert_eq!(builder.len(), 1);
let query = builder.build_query_string();
assert!(query.contains("title = ?"));
assert!(!query.contains("notes = ?"));
assert!(!query.contains("status = ?"));
}
#[test]
fn test_task_update_builder_fields() {
let builder = TaskUpdateBuilder::new()
.add_field("title")
.add_field("status");
let fields = builder.fields();
assert_eq!(fields.len(), 2);
assert!(fields.contains(&"title".to_string()));
assert!(fields.contains(&"status".to_string()));
}
}