use chrono_tz::Tz;
use core::result::Result as CoreResult;
use derive_builder::Builder;
use derive_getters::Getters;
use matrix_sdk_base::ruma::events::{macros::EventContent, room::message::TextMessageEventContent};
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
use tracing::trace;
use super::{BelongsTo, Date, Display, Update, UtcDateTime};
use crate::{util::deserialize_some, Result as ActerResult};
#[derive(Clone, Serialize, Deserialize, Debug)]
pub enum SpecialTaskListRole {
Inbox,
Trash,
}
#[derive(Clone, Serialize_repr, Deserialize_repr, PartialEq, Eq, Debug)]
#[repr(u8)]
#[derive(Default)]
pub enum Priority {
#[default]
Undefined = 0,
Highest = 1,
SecondHighest = 2,
Three = 3,
Four = 4,
Five = 5,
Six = 6,
Seven = 7,
SecondLowest = 8,
Lowest = 9,
}
impl Priority {
fn is_undefinied(&self) -> bool {
matches!(self, Priority::Undefined)
}
}
#[derive(Clone, Debug, Deserialize, Serialize, EventContent, Builder, Getters)]
#[ruma_event(type = "global.acter.dev.tasklist", kind = MessageLike)]
#[builder(name = "TaskListBuilder", derive(Debug))]
pub struct TaskListEventContent {
pub name: String,
#[builder(setter(into), default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub role: Option<SpecialTaskListRole>,
#[builder(setter(into), default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<TextMessageEventContent>,
#[builder(setter(into), default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub display: Option<Display>,
#[builder(default)]
#[serde(default)]
pub sort_order: u32,
#[builder(setter(into), default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub time_zone: Option<Tz>,
#[builder(setter(into), default)]
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub keywords: Vec<String>,
#[builder(setter(into), default)]
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub categories: Vec<String>,
}
#[derive(Clone, Debug, Deserialize, Serialize, EventContent, Builder)]
#[ruma_event(type = "global.acter.dev.tasklist.update", kind = MessageLike)]
#[builder(name = "TaskListUpdateBuilder", derive(Debug))]
pub struct TaskListUpdateEventContent {
#[builder(setter(into))]
#[serde(rename = "m.relates_to")]
pub task_list: Update,
#[builder(default)]
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_some"
)]
pub name: Option<String>,
#[builder(setter(into), default)]
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_some"
)]
pub role: Option<Option<SpecialTaskListRole>>,
#[builder(setter(into), default)]
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_some"
)]
pub description: Option<Option<TextMessageEventContent>>,
#[builder(setter(into), default)]
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_some"
)]
pub display: Option<Option<Display>>,
#[builder(default)]
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_some"
)]
pub sort_order: Option<u32>,
#[builder(setter(into), default)]
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_some"
)]
pub time_zone: Option<Option<Tz>>,
#[builder(setter(into), default)]
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_some"
)]
pub keywords: Option<Vec<String>>,
#[builder(setter(into), default)]
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_some"
)]
pub categories: Option<Vec<String>>,
}
impl TaskListUpdateEventContent {
pub fn apply(&self, task_list: &mut TaskListEventContent) -> ActerResult<bool> {
let mut updated = false;
if let Some(name) = &self.name {
task_list.name.clone_from(name);
updated = true;
}
if let Some(role) = &self.role {
task_list.role.clone_from(role);
updated = true;
}
if let Some(description) = &self.description {
task_list.description.clone_from(description);
updated = true;
}
if let Some(display) = &self.display {
task_list.display.clone_from(display);
updated = true;
}
if let Some(sort_order) = &self.sort_order {
task_list.sort_order = *sort_order;
updated = true;
}
if let Some(time_zone) = &self.time_zone {
task_list.time_zone = *time_zone;
updated = true;
}
if let Some(keywords) = &self.keywords {
task_list.keywords.clone_from(keywords);
updated = true;
}
if let Some(categories) = &self.categories {
task_list.categories.clone_from(categories);
updated = true;
}
trace!(update = ?self, ?updated, ?task_list, "TaskList updated");
Ok(updated)
}
}
#[derive(Clone, Debug, Deserialize, Serialize, EventContent, Builder, Getters)]
#[ruma_event(type = "global.acter.dev.task", kind = MessageLike)]
#[builder(
name = "TaskBuilder",
build_fn(validate = "Self::validate"),
derive(Debug)
)]
pub struct TaskEventContent {
pub title: String,
#[builder(setter(into))]
#[serde(rename = "m.relates_to")]
pub task_list_id: BelongsTo,
#[builder(setter(into), default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<TextMessageEventContent>,
#[builder(setter(into), default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub due_date: Option<Date>,
#[builder(setter(into), default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub utc_due_time_of_day: Option<i32>,
#[builder(setter(into), default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub utc_start: Option<UtcDateTime>,
#[builder(default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub progress_percent: Option<u8>,
#[builder(default)]
#[serde(default)]
pub sort_order: u32,
#[builder(default)]
#[serde(default, skip_serializing_if = "Priority::is_undefinied")]
pub priority: Priority,
#[builder(setter(into), default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub display: Option<Display>,
#[builder(setter(into), default)]
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub keywords: Vec<String>,
#[builder(setter(into), default)]
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub categories: Vec<String>,
}
impl TaskBuilder {
fn validate(&self) -> CoreResult<(), String> {
if let Some(Some(percent)) = &self.progress_percent {
if *percent > 100 {
return Err("Progress percent can’t be higher than 100".to_string());
}
}
Ok(())
}
}
#[derive(Clone, Debug, Deserialize, Serialize, EventContent, Builder)]
#[ruma_event(type = "global.acter.dev.task.update", kind = MessageLike)]
#[builder(name = "TaskUpdateBuilder", derive(Debug))]
pub struct TaskUpdateEventContent {
#[builder(setter(into))]
#[serde(rename = "m.relates_to")]
pub task: Update,
#[builder(default)]
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_some"
)]
pub title: Option<String>,
#[builder(default)]
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_some"
)]
pub description: Option<Option<TextMessageEventContent>>,
#[builder(default)]
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_some"
)]
pub due_date: Option<Option<Date>>,
#[builder(default)]
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_some"
)]
pub utc_due_time_of_day: Option<Option<i32>>,
#[builder(default)]
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_some"
)]
pub utc_start: Option<Option<UtcDateTime>>,
#[builder(default)]
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_some"
)]
pub progress_percent: Option<Option<u8>>,
#[builder(default)]
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_some"
)]
pub sort_order: Option<u32>,
#[builder(default)]
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_some"
)]
pub priority: Option<Priority>,
#[builder(default)]
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_some"
)]
pub display: Option<Option<Display>>,
#[builder(default)]
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_some"
)]
pub keywords: Option<Vec<String>>,
#[builder(default)]
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_some"
)]
pub categories: Option<Vec<String>>,
}
impl TaskUpdateEventContent {
pub fn apply(&self, task: &mut TaskEventContent) -> ActerResult<bool> {
let mut updated = false;
if let Some(title) = &self.title {
task.title.clone_from(title);
updated = true;
}
if let Some(description) = &self.description {
task.description.clone_from(description);
updated = true;
}
if let Some(due_date) = &self.due_date {
task.due_date = *due_date;
updated = true;
}
if let Some(utc_due_time_of_day) = &self.utc_due_time_of_day {
task.utc_due_time_of_day = *utc_due_time_of_day;
updated = true;
}
if let Some(utc_start) = &self.utc_start {
task.utc_start = *utc_start;
updated = true;
}
if let Some(progress_percent) = &self.progress_percent {
task.progress_percent = *progress_percent;
updated = true;
}
if let Some(sort_order) = &self.sort_order {
task.sort_order = *sort_order;
updated = true;
}
if let Some(priority) = &self.priority {
task.priority = priority.clone();
updated = true;
}
if let Some(display) = &self.display {
task.display.clone_from(display);
updated = true;
}
if let Some(keywords) = &self.keywords {
task.keywords.clone_from(keywords);
updated = true;
}
if let Some(categories) = &self.categories {
task.categories.clone_from(categories);
updated = true;
}
trace!(update = ?self, ?updated, ?task, "Task updated");
Ok(updated)
}
}
#[derive(Clone, Debug, Deserialize, Serialize, EventContent, Builder, Getters)]
#[ruma_event(type = "global.acter.dev.task.self_assign", kind = MessageLike)]
#[builder(name = "TaskSelfAssignBuilder", derive(Debug))]
pub struct TaskSelfAssignEventContent {
#[builder(setter(into))]
#[serde(rename = "m.relates_to")]
pub task: BelongsTo,
}
#[derive(Clone, Debug, Deserialize, Serialize, EventContent, Builder, Getters)]
#[ruma_event(type = "global.acter.dev.task.self_unassign", kind = MessageLike)]
#[builder(name = "TaskSelfUnassignBuilder", derive(Debug))]
pub struct TaskSelfUnassignEventContent {
#[builder(setter(into))]
#[serde(rename = "m.relates_to")]
pub task: BelongsTo,
}