style: fix the rustfmt linted files
	
		
			
	
		
	
	
		
	
		
			Some checks failed
		
		
	
	
		
			
				
	
				actionlint check / actionlint check (pull_request) Successful in 5s
				
			
		
			
				
	
				checkov check / checkov check (pull_request) Successful in 52s
				
			
		
			
				
	
				conventional commit messages check / conventional commit messages check (pull_request) Successful in 4s
				
			
		
			
				
	
				conventional pull request title check / conventional pull request title check (pull_request) Successful in 2s
				
			
		
			
				
	
				dotenv-linter check / dotenv-linter check (pull_request) Successful in 5s
				
			
		
			
				
	
				GitLeaks check / GitLeaks check (pull_request) Successful in 6s
				
			
		
			
				
	
				hadolint check / hadolint check (pull_request) Successful in 7s
				
			
		
			
				
	
				htmlhint check / htmlhint check (pull_request) Successful in 9s
				
			
		
			
				
	
				markdownlint check / markdownlint check (pull_request) Successful in 9s
				
			
		
			
				
	
				Prettier check / Prettier check (pull_request) Successful in 10s
				
			
		
			
				
	
				Rust check / Rust check (pull_request) Failing after 6m20s
				
			
		
			
				
	
				ShellCheck check / ShellCheck check (pull_request) Successful in 12s
				
			
		
			
				
	
				Stylelint check / Stylelint check (pull_request) Successful in 11s
				
			
		
			
				
	
				yamllint check / yamllint check (pull_request) Successful in 13s
				
			
		
		
	
	
				
					
				
			
		
			Some checks failed
		
		
	
	actionlint check / actionlint check (pull_request) Successful in 5s
				
			checkov check / checkov check (pull_request) Successful in 52s
				
			conventional commit messages check / conventional commit messages check (pull_request) Successful in 4s
				
			conventional pull request title check / conventional pull request title check (pull_request) Successful in 2s
				
			dotenv-linter check / dotenv-linter check (pull_request) Successful in 5s
				
			GitLeaks check / GitLeaks check (pull_request) Successful in 6s
				
			hadolint check / hadolint check (pull_request) Successful in 7s
				
			htmlhint check / htmlhint check (pull_request) Successful in 9s
				
			markdownlint check / markdownlint check (pull_request) Successful in 9s
				
			Prettier check / Prettier check (pull_request) Successful in 10s
				
			Rust check / Rust check (pull_request) Failing after 6m20s
				
			ShellCheck check / ShellCheck check (pull_request) Successful in 12s
				
			Stylelint check / Stylelint check (pull_request) Successful in 11s
				
			yamllint check / yamllint check (pull_request) Successful in 13s
				
			This commit is contained in:
		| @@ -25,7 +25,7 @@ pub(crate) fn BottomPanel(display_form: Signal<bool>) -> Element { | ||||
|                 // Necessary for a smooth – not instant – height transition. | ||||
|                 async_std::task::sleep(std::time::Duration::from_millis(500)).await; | ||||
|                 /* The check is necessary for the situation when the user expands the panel while | ||||
|                    it is being closed. */ | ||||
|                 it is being closed. */ | ||||
|                 if !display_form() { | ||||
|                     expanded.set(false); | ||||
|                 } | ||||
|   | ||||
| @@ -4,7 +4,10 @@ use dioxus::dioxus_core::Element; | ||||
| use dioxus::prelude::*; | ||||
|  | ||||
| #[component] | ||||
| pub(crate) fn CategoryInput(selected_category: Signal<Category>, class: Option<&'static str>) -> Element { | ||||
| pub(crate) fn CategoryInput( | ||||
|     selected_category: Signal<Category>, | ||||
|     class: Option<&'static str>, | ||||
| ) -> Element { | ||||
|     rsx! { | ||||
|         div { | ||||
|             class: format!("flex flex-row gap-2 {}", class.unwrap_or("")), | ||||
|   | ||||
| @@ -4,6 +4,5 @@ use dioxus::prelude::*; | ||||
|  | ||||
| #[component] | ||||
| pub(crate) fn Home() -> Element { | ||||
|     rsx! { | ||||
|     } | ||||
|     rsx! {} | ||||
| } | ||||
|   | ||||
| @@ -11,17 +11,14 @@ use dioxus::prelude::*; | ||||
| #[component] | ||||
| pub(crate) fn Layout() -> Element { | ||||
|     let mut display_form = use_signal(|| false); | ||||
|     let project_being_edited = use_context_provider::<Signal<Option<Project>>>( | ||||
|         || Signal::new(None) | ||||
|     ); | ||||
|     let task_being_edited = use_context_provider::<Signal<Option<Task>>>( | ||||
|         || Signal::new(None) | ||||
|     ); | ||||
|      | ||||
|     let project_being_edited = | ||||
|         use_context_provider::<Signal<Option<Project>>>(|| Signal::new(None)); | ||||
|     let task_being_edited = use_context_provider::<Signal<Option<Task>>>(|| Signal::new(None)); | ||||
|  | ||||
|     use_effect(move || { | ||||
|         display_form.set(project_being_edited().is_some() || task_being_edited().is_some()); | ||||
|     }); | ||||
|      | ||||
|  | ||||
|     rsx! { | ||||
|         Outlet::<Route> {} | ||||
|         StickyBottom { | ||||
|   | ||||
| @@ -1,16 +1,16 @@ | ||||
| pub(crate) mod app; | ||||
| pub(crate) mod bottom_panel; | ||||
| pub(crate) mod category_input; | ||||
| pub(crate) mod form_open_button; | ||||
| pub(crate) mod home; | ||||
| pub(crate) mod layout; | ||||
| pub(crate) mod navigation; | ||||
| pub(crate) mod navigation_item; | ||||
| pub(crate) mod pages; | ||||
| pub(crate) mod project_form; | ||||
| pub(crate) mod reoccurrence_input; | ||||
| pub(crate) mod sticky_bottom; | ||||
| pub(crate) mod subtasks_form; | ||||
| pub(crate) mod task_form; | ||||
| pub(crate) mod task_list; | ||||
| pub(crate) mod pages; | ||||
| pub(crate) mod navigation; | ||||
| pub(crate) mod form_open_button; | ||||
| pub(crate) mod bottom_panel; | ||||
| pub(crate) mod sticky_bottom; | ||||
| pub(crate) mod category_input; | ||||
| pub(crate) mod reoccurrence_input; | ||||
| pub(crate) mod layout; | ||||
| pub(crate) mod navigation_item; | ||||
| pub(crate) mod subtasks_form; | ||||
| pub(crate) mod task_list_item; | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| use dioxus::prelude::*; | ||||
| use crate::route::Route; | ||||
| use dioxus::prelude::*; | ||||
|  | ||||
| #[component] | ||||
| pub(crate) fn NavigationItem(route: Route, children: Element) -> Element { | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| use crate::components::pages::category_page::CategoryPage; | ||||
| use crate::models::category::Category; | ||||
| use dioxus::core_macro::rsx; | ||||
| use dioxus::dioxus_core::Element; | ||||
| use dioxus::prelude::*; | ||||
| use crate::components::pages::category_page::CategoryPage; | ||||
|  | ||||
| #[component] | ||||
| pub(crate) fn CategoryDonePage() -> Element { | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| use crate::components::pages::category_page::CategoryPage; | ||||
| use crate::models::category::Category; | ||||
| use dioxus::core_macro::rsx; | ||||
| use dioxus::dioxus_core::Element; | ||||
| use dioxus::prelude::*; | ||||
| use crate::components::pages::category_page::CategoryPage; | ||||
|  | ||||
| #[component] | ||||
| pub(crate) fn CategoryInboxPage() -> Element { | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| use crate::components::pages::category_page::CategoryPage; | ||||
| use crate::models::category::Category; | ||||
| use dioxus::core_macro::rsx; | ||||
| use dioxus::dioxus_core::Element; | ||||
| use dioxus::prelude::*; | ||||
| use crate::components::pages::category_page::CategoryPage; | ||||
|  | ||||
| #[component] | ||||
| pub(crate) fn CategoryLongTermPage() -> Element { | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| use crate::components::pages::category_page::CategoryPage; | ||||
| use crate::models::category::Category; | ||||
| use dioxus::core_macro::rsx; | ||||
| use dioxus::dioxus_core::Element; | ||||
| use dioxus::prelude::*; | ||||
| use crate::components::pages::category_page::CategoryPage; | ||||
|  | ||||
| #[component] | ||||
| pub(crate) fn CategoryNextStepsPage() -> Element { | ||||
|   | ||||
| @@ -28,6 +28,6 @@ pub(crate) fn CategoryPage(category: Category) -> Element { | ||||
|                 "Errors occurred: {errors:?}" | ||||
|             } | ||||
|         }, | ||||
|         value => panic!("Unexpected query result: {value:?}") | ||||
|         value => panic!("Unexpected query result: {value:?}"), | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| use crate::components::pages::category_page::CategoryPage; | ||||
| use crate::models::category::Category; | ||||
| use dioxus::core_macro::rsx; | ||||
| use dioxus::dioxus_core::Element; | ||||
| use dioxus::prelude::*; | ||||
| use crate::components::pages::category_page::CategoryPage; | ||||
|  | ||||
| #[component] | ||||
| pub(crate) fn CategorySomedayMaybePage() -> Element { | ||||
|   | ||||
| @@ -25,7 +25,7 @@ pub(crate) fn CategoryTodayPage() -> Element { | ||||
|  | ||||
|     let long_term_tasks_query = use_tasks_with_subtasks_in_category_query(Category::LongTerm); | ||||
|     let long_term_tasks_query_result = long_term_tasks_query.result(); | ||||
|      | ||||
|  | ||||
|     rsx! { | ||||
|         div { | ||||
|             class: "pt-4 flex flex-col gap-8", | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| use crate::components::pages::category_page::CategoryPage; | ||||
| use crate::models::category::Category; | ||||
| use dioxus::core_macro::rsx; | ||||
| use dioxus::dioxus_core::Element; | ||||
| use dioxus::prelude::*; | ||||
| use crate::components::pages::category_page::CategoryPage; | ||||
|  | ||||
| #[component] | ||||
| pub(crate) fn CategoryTrashPage() -> Element { | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| use crate::components::pages::category_page::CategoryPage; | ||||
| use crate::models::category::Category; | ||||
| use dioxus::core_macro::rsx; | ||||
| use dioxus::dioxus_core::Element; | ||||
| use dioxus::prelude::*; | ||||
| use crate::components::pages::category_page::CategoryPage; | ||||
|  | ||||
| #[component] | ||||
| pub(crate) fn CategoryWaitingForPage() -> Element { | ||||
|   | ||||
| @@ -1,12 +1,12 @@ | ||||
| pub(crate) mod category_inbox_page; | ||||
| pub(crate) mod category_calendar_page; | ||||
| pub(crate) mod category_today_page; | ||||
| pub(crate) mod category_waiting_for_page; | ||||
| pub(crate) mod category_done_page; | ||||
| pub(crate) mod category_inbox_page; | ||||
| pub(crate) mod category_long_term_page; | ||||
| pub(crate) mod category_next_steps_page; | ||||
| pub(crate) mod category_page; | ||||
| pub(crate) mod category_someday_maybe_page; | ||||
| pub(crate) mod category_done_page; | ||||
| pub(crate) mod category_today_page; | ||||
| pub(crate) mod category_trash_page; | ||||
| pub(crate) mod category_waiting_for_page; | ||||
| pub(crate) mod not_found_page; | ||||
| pub(crate) mod projects_page; | ||||
| pub(crate) mod category_page; | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| use dioxus::prelude::*; | ||||
| use dioxus_query::prelude::QueryResult; | ||||
| use crate::models::project::Project; | ||||
| use crate::query::projects::use_projects_query; | ||||
| use crate::query::QueryValue; | ||||
| use dioxus::prelude::*; | ||||
| use dioxus_query::prelude::QueryResult; | ||||
|  | ||||
| #[component] | ||||
| pub(crate) fn ProjectsPage() -> Element { | ||||
|   | ||||
| @@ -7,8 +7,10 @@ use dioxus::prelude::*; | ||||
| use dioxus_query::prelude::use_query_client; | ||||
|  | ||||
| #[component] | ||||
| pub(crate) fn ProjectForm(project: Option<Project>, on_successful_submit: EventHandler<()>) | ||||
|                           -> Element { | ||||
| pub(crate) fn ProjectForm( | ||||
|     project: Option<Project>, | ||||
|     on_successful_submit: EventHandler<()>, | ||||
| ) -> Element { | ||||
|     let query_client = use_query_client::<QueryValue, QueryErrors, QueryKey>(); | ||||
|     let project_for_submit = project.clone(); | ||||
|  | ||||
|   | ||||
| @@ -6,7 +6,7 @@ use dioxus::prelude::*; | ||||
| #[component] | ||||
| pub(crate) fn ReoccurrenceIntervalInput( | ||||
|     reoccurrence_interval: Signal<Option<ReoccurrenceInterval>>, | ||||
|     class_buttons: Option<&'static str> | ||||
|     class_buttons: Option<&'static str>, | ||||
| ) -> Element { | ||||
|     rsx! { | ||||
|         button { | ||||
|   | ||||
| @@ -4,6 +4,7 @@ use crate::components::subtasks_form::SubtasksForm; | ||||
| use crate::models::category::{CalendarTime, Category, Reoccurrence}; | ||||
| use crate::models::task::NewTask; | ||||
| use crate::models::task::Task; | ||||
| use crate::query::projects::use_projects_query; | ||||
| use crate::query::{QueryErrors, QueryKey, QueryValue}; | ||||
| use crate::route::Route; | ||||
| use crate::server::tasks::{create_task, delete_task, edit_task}; | ||||
| @@ -13,7 +14,6 @@ use dioxus::dioxus_core::Element; | ||||
| use dioxus::prelude::*; | ||||
| use dioxus_i18n::t; | ||||
| use dioxus_query::prelude::{use_query_client, QueryResult}; | ||||
| use crate::query::projects::use_projects_query; | ||||
|  | ||||
| const REMINDER_OFFSETS: [Option<Duration>; 17] = [ | ||||
|     None, | ||||
| @@ -40,7 +40,8 @@ pub(crate) fn TaskForm(task: Option<Task>, on_successful_submit: EventHandler<() | ||||
|     let projects_query = use_projects_query(); | ||||
|  | ||||
|     let route = use_route::<Route>(); | ||||
|     let selected_category = use_signal(|| if let Some(task) = &task { | ||||
|     let selected_category = use_signal(|| { | ||||
|         if let Some(task) = &task { | ||||
|             task.category().clone() | ||||
|         } else { | ||||
|             match route { | ||||
| @@ -56,26 +57,41 @@ pub(crate) fn TaskForm(task: Option<Task>, on_successful_submit: EventHandler<() | ||||
|                 _ => Category::Inbox, | ||||
|             } | ||||
|         } | ||||
|     ); | ||||
|     let category_calendar_reoccurrence_interval = use_signal(|| task.as_ref().and_then(|task| | ||||
|         if let Category::Calendar { reoccurrence: Some(reoccurrence), .. } = task.category() { | ||||
|             Some(reoccurrence.interval().clone()) | ||||
|         } else { | ||||
|             None | ||||
|         } | ||||
|     )); | ||||
|     let mut category_calendar_has_time = use_signal(|| task.as_ref().is_some_and( | ||||
|         |task| matches!(*task.category(), Category::Calendar { time: Some(_), .. })) | ||||
|     ); | ||||
|     let mut category_calendar_reminder_offset_index = use_signal(|| task.as_ref().and_then(|task| | ||||
|         if let Category::Calendar { time: Some(time), .. } = task.category() { | ||||
|             REMINDER_OFFSETS.iter().position(|&reminder_offset| | ||||
|                 reminder_offset == time.reminder_offset() | ||||
|             ) | ||||
|         } else { | ||||
|             None | ||||
|         } | ||||
|     ).unwrap_or(REMINDER_OFFSETS.len() - 1)); | ||||
|     }); | ||||
|     let category_calendar_reoccurrence_interval = use_signal(|| { | ||||
|         task.as_ref().and_then(|task| { | ||||
|             if let Category::Calendar { | ||||
|                 reoccurrence: Some(reoccurrence), | ||||
|                 .. | ||||
|             } = task.category() | ||||
|             { | ||||
|                 Some(reoccurrence.interval().clone()) | ||||
|             } else { | ||||
|                 None | ||||
|             } | ||||
|         }) | ||||
|     }); | ||||
|     let mut category_calendar_has_time = use_signal(|| { | ||||
|         task.as_ref().is_some_and(|task| { | ||||
|             matches!(*task.category(), Category::Calendar { time: Some(_), .. }) | ||||
|         }) | ||||
|     }); | ||||
|     let mut category_calendar_reminder_offset_index = use_signal(|| { | ||||
|         task.as_ref() | ||||
|             .and_then(|task| { | ||||
|                 if let Category::Calendar { | ||||
|                     time: Some(time), .. | ||||
|                 } = task.category() | ||||
|                 { | ||||
|                     REMINDER_OFFSETS | ||||
|                         .iter() | ||||
|                         .position(|&reminder_offset| reminder_offset == time.reminder_offset()) | ||||
|                 } else { | ||||
|                     None | ||||
|                 } | ||||
|             }) | ||||
|             .unwrap_or(REMINDER_OFFSETS.len() - 1) | ||||
|     }); | ||||
|  | ||||
|     let query_client = use_query_client::<QueryValue, QueryErrors, QueryKey>(); | ||||
|     let task_for_submit = task.clone(); | ||||
|   | ||||
| @@ -1,13 +1,13 @@ | ||||
| use crate::components::task_list_item::TaskListItem; | ||||
| use crate::models::category::Category; | ||||
| use crate::models::task::{Task, TaskWithSubtasks}; | ||||
| use crate::query::{QueryErrors, QueryKey, QueryValue}; | ||||
| use crate::server::tasks::complete_task; | ||||
| use dioxus::core_macro::rsx; | ||||
| use dioxus::dioxus_core::Element; | ||||
| use dioxus::prelude::*; | ||||
| use dioxus_query::prelude::use_query_client; | ||||
| use tracing::info; | ||||
| use crate::components::task_list_item::TaskListItem; | ||||
| use crate::query::{QueryErrors, QueryKey, QueryValue}; | ||||
| use crate::server::tasks::complete_task; | ||||
|  | ||||
| #[component] | ||||
| pub(crate) fn TaskList(tasks: Vec<TaskWithSubtasks>, class: Option<&'static str>) -> Element { | ||||
| @@ -15,7 +15,7 @@ pub(crate) fn TaskList(tasks: Vec<TaskWithSubtasks>, class: Option<&'static str> | ||||
|     let mut task_being_edited = use_context::<Signal<Option<Task>>>(); | ||||
|  | ||||
|     tasks.sort(); | ||||
|      | ||||
|  | ||||
|     rsx! { | ||||
|         div { | ||||
|             class: format!("flex flex-col {}", class.unwrap_or("")), | ||||
|   | ||||
| @@ -1,4 +1,3 @@ | ||||
| use dioxus_i18n::prelude::i18n; | ||||
| use crate::internationalization::LocaleFromLanguageIdentifier; | ||||
| use crate::models::category::Category; | ||||
| use crate::models::task::TaskWithSubtasks; | ||||
| @@ -6,6 +5,7 @@ use chrono::{Datelike, Local}; | ||||
| use dioxus::core_macro::rsx; | ||||
| use dioxus::dioxus_core::Element; | ||||
| use dioxus::prelude::*; | ||||
| use dioxus_i18n::prelude::i18n; | ||||
| use dioxus_i18n::t; | ||||
| use voca_rs::Voca; | ||||
|  | ||||
|   | ||||
| @@ -8,8 +8,8 @@ pub enum Error { | ||||
| } | ||||
|  | ||||
| impl From<diesel::result::Error> for Error { | ||||
|     fn from(_: diesel::result::Error) -> Self {  | ||||
|         Self::ServerInternal  | ||||
|     fn from(_: diesel::result::Error) -> Self { | ||||
|         Self::ServerInternal | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| use std::fmt::Display; | ||||
| use std::str::FromStr; | ||||
| use serde::Deserialize; | ||||
| use serde_with::serde_derive::Serialize; | ||||
| use std::fmt::Display; | ||||
| use std::str::FromStr; | ||||
|  | ||||
| #[derive(Serialize, Deserialize, Clone, Debug)] | ||||
| pub struct ErrorVec<T> { | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| pub(crate) mod error; | ||||
| pub(crate) mod error_vec; | ||||
| pub(crate) mod project_error; | ||||
| pub(crate) mod task_error; | ||||
| pub(crate) mod subtask_error; | ||||
| pub(crate) mod task_error; | ||||
|   | ||||
| @@ -13,7 +13,8 @@ pub enum ProjectError { | ||||
|  | ||||
| impl From<ValidationErrors> for ErrorVec<ProjectError> { | ||||
|     fn from(validation_errors: ValidationErrors) -> Self { | ||||
|         validation_errors.errors() | ||||
|         validation_errors | ||||
|             .errors() | ||||
|             .iter() | ||||
|             .flat_map(|(&field, error_kind)| match field { | ||||
|                 "title" => match error_kind { | ||||
|   | ||||
| @@ -14,7 +14,8 @@ pub enum SubtaskError { | ||||
|  | ||||
| impl From<ValidationErrors> for ErrorVec<SubtaskError> { | ||||
|     fn from(validation_errors: ValidationErrors) -> Self { | ||||
|         validation_errors.errors() | ||||
|         validation_errors | ||||
|             .errors() | ||||
|             .iter() | ||||
|             .flat_map(|(&field, error_kind)| match field { | ||||
|                 "title" => match error_kind { | ||||
| @@ -39,24 +40,24 @@ impl From<diesel::result::Error> for SubtaskError { | ||||
|     fn from(diesel_error: diesel::result::Error) -> Self { | ||||
|         match diesel_error { | ||||
|             diesel::result::Error::DatabaseError( | ||||
|                 diesel::result::DatabaseErrorKind::ForeignKeyViolation, info | ||||
|             ) => { | ||||
|                 match info.constraint_name() { | ||||
|                     Some("subtasks_task_id_fkey") => Self::TaskNotFound, | ||||
|                     _ => Self::Error(Error::ServerInternal) | ||||
|                 } | ||||
|             } | ||||
|             _ => { | ||||
|                 Self::Error(Error::ServerInternal) | ||||
|             } | ||||
|                 diesel::result::DatabaseErrorKind::ForeignKeyViolation, | ||||
|                 info, | ||||
|             ) => match info.constraint_name() { | ||||
|                 Some("subtasks_task_id_fkey") => Self::TaskNotFound, | ||||
|                 _ => Self::Error(Error::ServerInternal), | ||||
|             }, | ||||
|             _ => Self::Error(Error::ServerInternal), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<ErrorVec<Error>> for ErrorVec<SubtaskError> { | ||||
|     fn from(error_vec: ErrorVec<Error>) -> Self { | ||||
|         Vec::from(error_vec).into_iter() | ||||
|             .map(SubtaskError::Error).collect::<Vec<SubtaskError>>().into() | ||||
|         Vec::from(error_vec) | ||||
|             .into_iter() | ||||
|             .map(SubtaskError::Error) | ||||
|             .collect::<Vec<SubtaskError>>() | ||||
|             .into() | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -14,7 +14,8 @@ pub enum TaskError { | ||||
|  | ||||
| impl From<ValidationErrors> for ErrorVec<TaskError> { | ||||
|     fn from(validation_errors: ValidationErrors) -> Self { | ||||
|         validation_errors.errors() | ||||
|         validation_errors | ||||
|             .errors() | ||||
|             .iter() | ||||
|             .flat_map(|(&field, error_kind)| match field { | ||||
|                 "title" => match error_kind { | ||||
| @@ -39,16 +40,13 @@ impl From<diesel::result::Error> for TaskError { | ||||
|     fn from(diesel_error: diesel::result::Error) -> Self { | ||||
|         match diesel_error { | ||||
|             diesel::result::Error::DatabaseError( | ||||
|                 diesel::result::DatabaseErrorKind::ForeignKeyViolation, info | ||||
|             ) => { | ||||
|                 match info.constraint_name() { | ||||
|                     Some("tasks_project_id_fkey") => Self::ProjectNotFound, | ||||
|                     _ => Self::Error(Error::ServerInternal) | ||||
|                 } | ||||
|             } | ||||
|             _ => { | ||||
|                 Self::Error(Error::ServerInternal) | ||||
|             } | ||||
|                 diesel::result::DatabaseErrorKind::ForeignKeyViolation, | ||||
|                 info, | ||||
|             ) => match info.constraint_name() { | ||||
|                 Some("tasks_project_id_fkey") => Self::ProjectNotFound, | ||||
|                 _ => Self::Error(Error::ServerInternal), | ||||
|             }, | ||||
|             _ => Self::Error(Error::ServerInternal), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| use std::ops::Deref; | ||||
| use std::sync::Mutex; | ||||
| use chrono::Locale; | ||||
| use dioxus::fullstack::once_cell::sync::Lazy; | ||||
| use feruca::Collator; | ||||
| use std::ops::Deref; | ||||
| use std::sync::Mutex; | ||||
| use unic_langid_impl::LanguageIdentifier; | ||||
|  | ||||
| pub(crate) static COLLATOR: Lazy<Mutex<Collator>> = Lazy::new(|| Mutex::new(Collator::default())); | ||||
| @@ -19,7 +19,11 @@ impl<'a> Deref for LocaleFromLanguageIdentifier<'a> { | ||||
|  | ||||
| impl<'a> From<LocaleFromLanguageIdentifier<'a>> for Locale { | ||||
|     fn from(language_identifier: LocaleFromLanguageIdentifier) -> Self { | ||||
|         language_identifier.to_string().replace("-", "_").parse().unwrap() | ||||
|         language_identifier | ||||
|             .to_string() | ||||
|             .replace("-", "_") | ||||
|             .parse() | ||||
|             .unwrap() | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,13 +1,13 @@ | ||||
| mod components; | ||||
| mod errors; | ||||
| mod internationalization; | ||||
| mod migrations; | ||||
| mod models; | ||||
| mod query; | ||||
| mod route; | ||||
| mod schema; | ||||
| mod server; | ||||
| mod query; | ||||
| mod utils; | ||||
| mod internationalization; | ||||
| mod migrations; | ||||
|  | ||||
| use components::app::App; | ||||
| use dioxus::prelude::*; | ||||
|   | ||||
| @@ -1,4 +1,3 @@ | ||||
| use std::hash::Hash; | ||||
| use crate::schema::tasks; | ||||
| use chrono::{Duration, NaiveDate, NaiveTime}; | ||||
| use diesel::deserialize::FromSql; | ||||
| @@ -9,6 +8,7 @@ use diesel::{AsExpression, BoxableExpression, FromSqlRow, PgJsonbExpressionMetho | ||||
| use serde::{Deserialize, Serialize}; | ||||
| use serde_json::json; | ||||
| use serde_with::DurationSeconds; | ||||
| use std::hash::Hash; | ||||
| use std::io::Write; | ||||
|  | ||||
| #[serde_with::serde_as] | ||||
| @@ -30,7 +30,7 @@ pub enum Category { | ||||
| } | ||||
|  | ||||
| impl Category { | ||||
|     pub fn eq_sql_predicate(&self) -> Box<dyn BoxableExpression<tasks::table, Pg, SqlType=Bool>> { | ||||
|     pub fn eq_sql_predicate(&self) -> Box<dyn BoxableExpression<tasks::table, Pg, SqlType = Bool>> { | ||||
|         use crate::schema::tasks::dsl::*; | ||||
|  | ||||
|         match self { | ||||
| @@ -99,13 +99,17 @@ pub struct Reoccurrence { | ||||
|  | ||||
| impl Reoccurrence { | ||||
|     pub fn new(start_date: NaiveDate, interval: ReoccurrenceInterval, length: u32) -> Self { | ||||
|         Self { start_date, interval, length } | ||||
|         Self { | ||||
|             start_date, | ||||
|             interval, | ||||
|             length, | ||||
|         } | ||||
|     } | ||||
|      | ||||
|  | ||||
|     pub fn start_date(&self) -> NaiveDate { | ||||
|         self.start_date | ||||
|     } | ||||
|      | ||||
|  | ||||
|     pub fn interval(&self) -> &ReoccurrenceInterval { | ||||
|         &self.interval | ||||
|     } | ||||
| @@ -125,9 +129,12 @@ pub struct CalendarTime { | ||||
|  | ||||
| impl CalendarTime { | ||||
|     pub fn new(time: NaiveTime, reminder_offset: Option<Duration>) -> Self { | ||||
|         Self { time, reminder_offset } | ||||
|         Self { | ||||
|             time, | ||||
|             reminder_offset, | ||||
|         } | ||||
|     } | ||||
|      | ||||
|  | ||||
|     pub fn time(&self) -> NaiveTime { | ||||
|         self.time | ||||
|     } | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| pub(crate) mod project; | ||||
| pub(crate) mod category; | ||||
| pub(crate) mod task; | ||||
| pub(crate) mod project; | ||||
| pub(crate) mod subtask; | ||||
| pub(crate) mod task; | ||||
|   | ||||
| @@ -1,10 +1,10 @@ | ||||
| use std::cmp::Ordering; | ||||
| use chrono::NaiveDateTime; | ||||
| use crate::internationalization::COLLATOR; | ||||
| use crate::schema::projects; | ||||
| use chrono::NaiveDateTime; | ||||
| use diesel::prelude::*; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| use std::cmp::Ordering; | ||||
| use validator::Validate; | ||||
| use crate::internationalization::COLLATOR; | ||||
|  | ||||
| const TITLE_LENGTH_MIN: u64 = 1; | ||||
| const TITLE_LENGTH_MAX: u64 = 255; | ||||
| @@ -27,11 +27,11 @@ impl Project { | ||||
|     pub fn title(&self) -> &str { | ||||
|         &self.title | ||||
|     } | ||||
|      | ||||
|  | ||||
|     pub fn created_at(&self) -> NaiveDateTime { | ||||
|         self.created_at | ||||
|     } | ||||
|      | ||||
|  | ||||
|     pub fn updated_at(&self) -> NaiveDateTime { | ||||
|         self.updated_at | ||||
|     } | ||||
| @@ -47,14 +47,21 @@ impl PartialOrd<Self> for Project { | ||||
|  | ||||
| impl Ord for Project { | ||||
|     fn cmp(&self, other: &Self) -> Ordering { | ||||
|         COLLATOR.lock().unwrap().collate(self.title(), other.title()) | ||||
|         COLLATOR | ||||
|             .lock() | ||||
|             .unwrap() | ||||
|             .collate(self.title(), other.title()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Insertable, Serialize, Deserialize, Validate, Clone, Debug)] | ||||
| #[diesel(table_name = projects)] | ||||
| pub struct NewProject { | ||||
|     #[validate(length(min = "TITLE_LENGTH_MIN", max = "TITLE_LENGTH_MAX", code = "title_length"))] | ||||
|     #[validate(length( | ||||
|         min = "TITLE_LENGTH_MIN", | ||||
|         max = "TITLE_LENGTH_MAX", | ||||
|         code = "title_length" | ||||
|     ))] | ||||
|     pub title: String, | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,16 +1,25 @@ | ||||
| use std::cmp::Ordering; | ||||
| use crate::models::task::Task; | ||||
| use crate::schema::subtasks; | ||||
| use chrono::NaiveDateTime; | ||||
| use diesel::prelude::*; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| use std::cmp::Ordering; | ||||
| use validator::Validate; | ||||
|  | ||||
| const TITLE_LENGTH_MIN: u64 = 1; | ||||
| const TITLE_LENGTH_MAX: u64 = 255; | ||||
|  | ||||
| #[derive(Queryable, Selectable, Identifiable, Associations, Serialize, Deserialize, PartialEq, | ||||
|     Clone, Debug)] | ||||
| #[derive( | ||||
|     Queryable, | ||||
|     Selectable, | ||||
|     Identifiable, | ||||
|     Associations, | ||||
|     Serialize, | ||||
|     Deserialize, | ||||
|     PartialEq, | ||||
|     Clone, | ||||
|     Debug, | ||||
| )] | ||||
| #[diesel(belongs_to(Task, foreign_key = task_id))] | ||||
| #[diesel(table_name = subtasks)] | ||||
| #[diesel(check_for_backend(diesel::pg::Pg))] | ||||
| @@ -59,7 +68,8 @@ impl PartialOrd<Self> for Subtask { | ||||
|  | ||||
| impl Ord for Subtask { | ||||
|     fn cmp(&self, other: &Self) -> Ordering { | ||||
|         self.is_completed().cmp(&other.is_completed()) | ||||
|         self.is_completed() | ||||
|             .cmp(&other.is_completed()) | ||||
|             .then(self.created_at().cmp(&other.created_at())) | ||||
|     } | ||||
| } | ||||
| @@ -68,14 +78,22 @@ impl Ord for Subtask { | ||||
| #[diesel(table_name = subtasks)] | ||||
| pub struct NewSubtask { | ||||
|     pub task_id: i32, | ||||
|     #[validate(length(min = "TITLE_LENGTH_MIN", max = "TITLE_LENGTH_MAX", code = "title_length"))] | ||||
|     #[validate(length( | ||||
|         min = "TITLE_LENGTH_MIN", | ||||
|         max = "TITLE_LENGTH_MAX", | ||||
|         code = "title_length" | ||||
|     ))] | ||||
|     pub title: String, | ||||
|     pub is_completed: bool, | ||||
| } | ||||
|  | ||||
| impl NewSubtask { | ||||
|     pub fn new(task_id: i32, title: String, is_completed: bool) -> Self { | ||||
|         Self { task_id, title, is_completed } | ||||
|         Self { | ||||
|             task_id, | ||||
|             title, | ||||
|             is_completed, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -67,23 +67,39 @@ impl Ord for Task { | ||||
|         match (&self.category, &other.category) { | ||||
|             (Category::Inbox, Category::Inbox) => self.created_at.cmp(&other.created_at), | ||||
|             ( | ||||
|                 Category::Calendar { date: self_date, time: self_time, .. }, | ||||
|                 Category::Calendar { date: other_date, time: other_time, .. } | ||||
|             ) => self_date.cmp(other_date) | ||||
|                 .then(ReverseOrdOption::from( | ||||
|                     &self_time.as_ref().map(|calendar_time| calendar_time.time()) | ||||
|                 ).cmp(&ReverseOrdOption::from( | ||||
|                     &other_time.as_ref().map(|calendar_time| calendar_time.time()) | ||||
|                 ))) | ||||
|                 .then(ReverseOrdOption::from(&self.deadline()).cmp( | ||||
|                     &ReverseOrdOption::from(&other.deadline()) | ||||
|                 )) | ||||
|                 Category::Calendar { | ||||
|                     date: self_date, | ||||
|                     time: self_time, | ||||
|                     .. | ||||
|                 }, | ||||
|                 Category::Calendar { | ||||
|                     date: other_date, | ||||
|                     time: other_time, | ||||
|                     .. | ||||
|                 }, | ||||
|             ) => self_date | ||||
|                 .cmp(other_date) | ||||
|                 .then( | ||||
|                     ReverseOrdOption::from( | ||||
|                         &self_time.as_ref().map(|calendar_time| calendar_time.time()), | ||||
|                     ) | ||||
|                     .cmp(&ReverseOrdOption::from( | ||||
|                         &other_time | ||||
|                             .as_ref() | ||||
|                             .map(|calendar_time| calendar_time.time()), | ||||
|                     )), | ||||
|                 ) | ||||
|                 .then( | ||||
|                     ReverseOrdOption::from(&self.deadline()) | ||||
|                         .cmp(&ReverseOrdOption::from(&other.deadline())), | ||||
|                 ) | ||||
|                 .then(self.created_at.cmp(&other.created_at)), | ||||
|             (Category::Done, Category::Done) | (Category::Trash, Category::Trash) => { | ||||
|                 self.updated_at.cmp(&other.updated_at).reverse() | ||||
|             } | ||||
|             (_, _) => ReverseOrdOption::from(&self.deadline()) | ||||
|                 .cmp(&ReverseOrdOption::from(&other.deadline())) | ||||
|                 .then(self.created_at.cmp(&other.created_at)), | ||||
|             (Category::Done, Category::Done) | (Category::Trash, Category::Trash) | ||||
|             => self.updated_at.cmp(&other.updated_at).reverse(), | ||||
|             (_, _) => ReverseOrdOption::from(&self.deadline()).cmp( | ||||
|                 &ReverseOrdOption::from(&other.deadline()) | ||||
|             ).then(self.created_at.cmp(&other.created_at)), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -125,7 +141,11 @@ impl Ord for TaskWithSubtasks { | ||||
| #[derive(Insertable, Serialize, Deserialize, Validate, Clone, Debug)] | ||||
| #[diesel(table_name = tasks)] | ||||
| pub struct NewTask { | ||||
|     #[validate(length(min = "TITLE_LENGTH_MIN", max = "TITLE_LENGTH_MAX", code = "title_length"))] | ||||
|     #[validate(length( | ||||
|         min = "TITLE_LENGTH_MIN", | ||||
|         max = "TITLE_LENGTH_MAX", | ||||
|         code = "title_length" | ||||
|     ))] | ||||
|     pub title: String, | ||||
|     pub deadline: Option<chrono::NaiveDate>, | ||||
|     pub category: Category, | ||||
| @@ -139,7 +159,12 @@ impl NewTask { | ||||
|         category: Category, | ||||
|         project_id: Option<i32>, | ||||
|     ) -> Self { | ||||
|         Self { title, deadline, category, project_id } | ||||
|         Self { | ||||
|             title, | ||||
|             deadline, | ||||
|             category, | ||||
|             project_id, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -5,9 +5,9 @@ use crate::models::project::Project; | ||||
| use crate::models::subtask::Subtask; | ||||
| use crate::models::task::{Task, TaskWithSubtasks}; | ||||
|  | ||||
| pub(crate) mod tasks; | ||||
| pub(crate) mod projects; | ||||
| pub(crate) mod subtasks; | ||||
| pub(crate) mod tasks; | ||||
|  | ||||
| #[derive(PartialEq, Debug)] | ||||
| pub(crate) enum QueryValue { | ||||
|   | ||||
| @@ -12,8 +12,9 @@ async fn fetch_projects(keys: Vec<QueryKey>) -> QueryResult<QueryValue, QueryErr | ||||
|         match get_projects().await { | ||||
|             Ok(projects) => Ok(QueryValue::Projects(projects)), | ||||
|             Err(ServerFnError::WrappedServerError(errors)) => Err(QueryErrors::Error(errors)), | ||||
|             Err(error) => panic!("Unexpected error: {:?}", error) | ||||
|         }.into() | ||||
|             Err(error) => panic!("Unexpected error: {:?}", error), | ||||
|         } | ||||
|         .into() | ||||
|     } else { | ||||
|         panic!("Unexpected query keys: {:?}", keys); | ||||
|     } | ||||
|   | ||||
| @@ -3,9 +3,13 @@ use crate::server::subtasks::get_subtasks_of_task; | ||||
| use dioxus::prelude::ServerFnError; | ||||
| use dioxus_query::prelude::{use_get_query, QueryResult, UseQuery}; | ||||
|  | ||||
| pub(crate) fn use_subtasks_of_task_query(task_id: i32) | ||||
|                                          -> UseQuery<QueryValue, QueryErrors, QueryKey> { | ||||
|     use_get_query([QueryKey::SubtasksOfTaskId(task_id)], fetch_subtasks_of_task) | ||||
| pub(crate) fn use_subtasks_of_task_query( | ||||
|     task_id: i32, | ||||
| ) -> UseQuery<QueryValue, QueryErrors, QueryKey> { | ||||
|     use_get_query( | ||||
|         [QueryKey::SubtasksOfTaskId(task_id)], | ||||
|         fetch_subtasks_of_task, | ||||
|     ) | ||||
| } | ||||
|  | ||||
| async fn fetch_subtasks_of_task(keys: Vec<QueryKey>) -> QueryResult<QueryValue, QueryErrors> { | ||||
| @@ -13,8 +17,9 @@ async fn fetch_subtasks_of_task(keys: Vec<QueryKey>) -> QueryResult<QueryValue, | ||||
|         match get_subtasks_of_task(*task_id).await { | ||||
|             Ok(subtasks) => Ok(QueryValue::Subtasks(subtasks)), | ||||
|             Err(ServerFnError::WrappedServerError(errors)) => Err(QueryErrors::Error(errors)), | ||||
|             Err(error) => panic!("Unexpected error: {:?}", error) | ||||
|         }.into() | ||||
|             Err(error) => panic!("Unexpected error: {:?}", error), | ||||
|         } | ||||
|         .into() | ||||
|     } else { | ||||
|         panic!("Unexpected query keys: {:?}", keys); | ||||
|     } | ||||
|   | ||||
| @@ -1,14 +1,16 @@ | ||||
| use dioxus::prelude::ServerFnError; | ||||
| use dioxus_query::prelude::{use_get_query, QueryResult, UseQuery}; | ||||
| use crate::models::category::Category; | ||||
| use crate::query::{QueryErrors, QueryKey, QueryValue}; | ||||
| use crate::server::tasks::{get_tasks_in_category, get_tasks_with_subtasks_in_category}; | ||||
| use dioxus::prelude::ServerFnError; | ||||
| use dioxus_query::prelude::{use_get_query, QueryResult, UseQuery}; | ||||
|  | ||||
|  | ||||
|  | ||||
| pub(crate) fn use_tasks_in_category_query(category: Category) | ||||
|                                           -> UseQuery<QueryValue, QueryErrors, QueryKey> { | ||||
|     use_get_query([QueryKey::TasksInCategory(category), QueryKey::Tasks], fetch_tasks_in_category) | ||||
| pub(crate) fn use_tasks_in_category_query( | ||||
|     category: Category, | ||||
| ) -> UseQuery<QueryValue, QueryErrors, QueryKey> { | ||||
|     use_get_query( | ||||
|         [QueryKey::TasksInCategory(category), QueryKey::Tasks], | ||||
|         fetch_tasks_in_category, | ||||
|     ) | ||||
| } | ||||
|  | ||||
| async fn fetch_tasks_in_category(keys: Vec<QueryKey>) -> QueryResult<QueryValue, QueryErrors> { | ||||
| @@ -16,34 +18,37 @@ async fn fetch_tasks_in_category(keys: Vec<QueryKey>) -> QueryResult<QueryValue, | ||||
|         match get_tasks_in_category(category.clone()).await { | ||||
|             Ok(tasks) => Ok(QueryValue::Tasks(tasks)), | ||||
|             Err(ServerFnError::WrappedServerError(errors)) => Err(QueryErrors::Error(errors)), | ||||
|             Err(error) => panic!("Unexpected error: {:?}", error) | ||||
|         }.into() | ||||
|             Err(error) => panic!("Unexpected error: {:?}", error), | ||||
|         } | ||||
|         .into() | ||||
|     } else { | ||||
|         panic!("Unexpected query keys: {:?}", keys); | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub(crate) fn use_tasks_with_subtasks_in_category_query(category: Category) | ||||
|                                           -> UseQuery<QueryValue, QueryErrors, QueryKey> { | ||||
| pub(crate) fn use_tasks_with_subtasks_in_category_query( | ||||
|     category: Category, | ||||
| ) -> UseQuery<QueryValue, QueryErrors, QueryKey> { | ||||
|     use_get_query( | ||||
|         [ | ||||
|             QueryKey::TasksWithSubtasksInCategory( | ||||
|             category.clone()), | ||||
|             QueryKey::TasksWithSubtasksInCategory(category.clone()), | ||||
|             QueryKey::TasksInCategory(category), | ||||
|             QueryKey::Tasks | ||||
|             QueryKey::Tasks, | ||||
|         ], | ||||
|         fetch_tasks_with_subtasks_in_category | ||||
|         fetch_tasks_with_subtasks_in_category, | ||||
|     ) | ||||
| } | ||||
|  | ||||
| async fn fetch_tasks_with_subtasks_in_category(keys: Vec<QueryKey>) | ||||
|     -> QueryResult<QueryValue, QueryErrors> { | ||||
| async fn fetch_tasks_with_subtasks_in_category( | ||||
|     keys: Vec<QueryKey>, | ||||
| ) -> QueryResult<QueryValue, QueryErrors> { | ||||
|     if let Some(QueryKey::TasksWithSubtasksInCategory(category)) = keys.first() { | ||||
|         match get_tasks_with_subtasks_in_category(category.clone()).await { | ||||
|             Ok(tasks) => Ok(QueryValue::TasksWithSubtasks(tasks)), | ||||
|             Err(ServerFnError::WrappedServerError(errors)) => Err(QueryErrors::Error(errors)), | ||||
|             Err(error) => panic!("Unexpected error: {:?}", error) | ||||
|         }.into() | ||||
|             Err(error) => panic!("Unexpected error: {:?}", error), | ||||
|         } | ||||
|         .into() | ||||
|     } else { | ||||
|         panic!("Unexpected query keys: {:?}", keys); | ||||
|     } | ||||
|   | ||||
| @@ -1,15 +1,15 @@ | ||||
| use crate::components::layout::Layout; | ||||
| use crate::components::pages::category_calendar_page::CategoryCalendarPage; | ||||
| use crate::components::pages::category_done_page::CategoryDonePage; | ||||
| use crate::components::pages::category_inbox_page::CategoryInboxPage; | ||||
| use crate::components::pages::category_long_term_page::CategoryLongTermPage; | ||||
| use crate::components::pages::category_next_steps_page::CategoryNextStepsPage; | ||||
| use crate::components::pages::category_someday_maybe_page::CategorySomedayMaybePage; | ||||
| use crate::components::pages::category_today_page::CategoryTodayPage; | ||||
| use crate::components::pages::category_trash_page::CategoryTrashPage; | ||||
| use crate::components::pages::category_waiting_for_page::CategoryWaitingForPage; | ||||
| use crate::components::pages::category_someday_maybe_page::CategorySomedayMaybePage; | ||||
| use crate::components::pages::category_done_page::CategoryDonePage; | ||||
| use crate::components::pages::category_calendar_page::CategoryCalendarPage; | ||||
| use crate::components::pages::category_long_term_page::CategoryLongTermPage; | ||||
| use crate::components::pages::projects_page::ProjectsPage; | ||||
| use crate::components::pages::not_found_page::NotFoundPage; | ||||
| use crate::components::layout::Layout; | ||||
| use crate::components::pages::projects_page::ProjectsPage; | ||||
| use dioxus::prelude::*; | ||||
|  | ||||
| // All variants have the same postfix because they have to match the component names. | ||||
|   | ||||
| @@ -35,8 +35,4 @@ diesel::table! { | ||||
| diesel::joinable!(subtasks -> tasks (task_id)); | ||||
| diesel::joinable!(tasks -> projects (project_id)); | ||||
|  | ||||
| diesel::allow_tables_to_appear_in_same_query!( | ||||
|     projects, | ||||
|     subtasks, | ||||
|     tasks, | ||||
| ); | ||||
| diesel::allow_tables_to_appear_in_same_query!(projects, subtasks, tasks,); | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| use std::env; | ||||
| use dioxus::prelude::ServerFnError; | ||||
| use unic_langid_impl::LanguageIdentifier; | ||||
| use dioxus::prelude::*; | ||||
| use dotenvy::dotenv; | ||||
| use std::env; | ||||
| use unic_langid_impl::LanguageIdentifier; | ||||
|  | ||||
| #[server] | ||||
| pub(crate) async fn get_language_identifier() -> Result<LanguageIdentifier, ServerFnError> { | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| pub(crate) mod database_connection; | ||||
| pub(crate) mod projects; | ||||
| pub(crate) mod tasks; | ||||
| pub(crate) mod subtasks; | ||||
| pub(crate) mod internationalization; | ||||
| pub(crate) mod projects; | ||||
| pub(crate) mod subtasks; | ||||
| pub(crate) mod tasks; | ||||
|   | ||||
| @@ -8,21 +8,23 @@ use dioxus::prelude::*; | ||||
| use validator::Validate; | ||||
|  | ||||
| #[server] | ||||
| pub(crate) async fn create_project(new_project: NewProject) | ||||
|                                    -> Result<Project, ServerFnError<ErrorVec<ProjectError>>> { | ||||
| pub(crate) async fn create_project( | ||||
|     new_project: NewProject, | ||||
| ) -> Result<Project, ServerFnError<ErrorVec<ProjectError>>> { | ||||
|     use crate::schema::projects; | ||||
|  | ||||
|     // TODO: replace with model sanitization (https://github.com/matous-volf/todo-baggins/issues/13) | ||||
|     let mut new_project = new_project; | ||||
|     new_project.title = new_project.title.trim().to_owned(); | ||||
|  | ||||
|     new_project.validate() | ||||
|     new_project | ||||
|         .validate() | ||||
|         .map_err::<ErrorVec<ProjectError>, _>(|errors| errors.into())?; | ||||
|  | ||||
|     let mut connection = establish_database_connection() | ||||
|         .map_err::<ErrorVec<ProjectError>, _>( | ||||
|             |_| vec![ProjectError::Error(Error::ServerInternal)].into() | ||||
|         )?; | ||||
|     let mut connection = | ||||
|         establish_database_connection().map_err::<ErrorVec<ProjectError>, _>(|_| { | ||||
|             vec![ProjectError::Error(Error::ServerInternal)].into() | ||||
|         })?; | ||||
|  | ||||
|     let new_project = diesel::insert_into(projects::table) | ||||
|         .values(&new_project) | ||||
| @@ -34,41 +36,39 @@ pub(crate) async fn create_project(new_project: NewProject) | ||||
| } | ||||
|  | ||||
| #[server] | ||||
| pub(crate) async fn get_projects() | ||||
|     -> Result<Vec<Project>, ServerFnError<ErrorVec<Error>>> { | ||||
| pub(crate) async fn get_projects() -> Result<Vec<Project>, ServerFnError<ErrorVec<Error>>> { | ||||
|     use crate::schema::projects::dsl::*; | ||||
|  | ||||
|     let mut connection = establish_database_connection() | ||||
|         .map_err::<ErrorVec<Error>, _>( | ||||
|             |_| vec![Error::ServerInternal].into() | ||||
|         )?; | ||||
|         .map_err::<ErrorVec<Error>, _>(|_| vec![Error::ServerInternal].into())?; | ||||
|  | ||||
|     let results = projects | ||||
|         .select(Project::as_select()) | ||||
|         .load::<Project>(&mut connection) | ||||
|         .map_err::<ErrorVec<Error>, _>( | ||||
|             |_| vec![Error::ServerInternal].into() | ||||
|         )?; | ||||
|         .map_err::<ErrorVec<Error>, _>(|_| vec![Error::ServerInternal].into())?; | ||||
|  | ||||
|     Ok(results) | ||||
| } | ||||
|  | ||||
| #[server] | ||||
| pub(crate) async fn edit_project(project_id: i32, new_project: NewProject) | ||||
|                                  -> Result<Project, ServerFnError<ErrorVec<ProjectError>>> { | ||||
| pub(crate) async fn edit_project( | ||||
|     project_id: i32, | ||||
|     new_project: NewProject, | ||||
| ) -> Result<Project, ServerFnError<ErrorVec<ProjectError>>> { | ||||
|     use crate::schema::projects::dsl::*; | ||||
|  | ||||
|     // TODO: replace with model sanitization (https://github.com/matous-volf/todo-baggins/issues/13) | ||||
|     let mut new_project = new_project; | ||||
|     new_project.title = new_project.title.trim().to_owned(); | ||||
|  | ||||
|     new_project.validate() | ||||
|     new_project | ||||
|         .validate() | ||||
|         .map_err::<ErrorVec<ProjectError>, _>(|errors| errors.into())?; | ||||
|  | ||||
|     let mut connection = establish_database_connection() | ||||
|         .map_err::<ErrorVec<ProjectError>, _>( | ||||
|             |_| vec![ProjectError::Error(Error::ServerInternal)].into() | ||||
|         )?; | ||||
|     let mut connection = | ||||
|         establish_database_connection().map_err::<ErrorVec<ProjectError>, _>(|_| { | ||||
|             vec![ProjectError::Error(Error::ServerInternal)].into() | ||||
|         })?; | ||||
|  | ||||
|     let updated_project = diesel::update(projects) | ||||
|         .filter(id.eq(project_id)) | ||||
| @@ -83,15 +83,14 @@ pub(crate) async fn edit_project(project_id: i32, new_project: NewProject) | ||||
| // TODO: Get rid of this suppression. | ||||
| //noinspection DuplicatedCode | ||||
| #[server] | ||||
| pub(crate) async fn delete_project(project_id: i32) | ||||
|                                    -> Result<(), ServerFnError<ErrorVec<Error>>> { | ||||
| pub(crate) async fn delete_project(project_id: i32) -> Result<(), ServerFnError<ErrorVec<Error>>> { | ||||
|     use crate::schema::projects::dsl::*; | ||||
|  | ||||
|     let mut connection = establish_database_connection() | ||||
|         .map_err::<ErrorVec<Error>, _>(|_| vec![Error::ServerInternal].into())?; | ||||
|  | ||||
|      | ||||
|     diesel::delete(projects.filter(id.eq(project_id))).execute(&mut connection) | ||||
|     diesel::delete(projects.filter(id.eq(project_id))) | ||||
|         .execute(&mut connection) | ||||
|         .map_err::<ErrorVec<Error>, _>(|error| vec![error.into()].into())?; | ||||
|  | ||||
|     Ok(()) | ||||
|   | ||||
| @@ -3,43 +3,47 @@ use crate::errors::error_vec::ErrorVec; | ||||
| use crate::errors::subtask_error::SubtaskError; | ||||
| use crate::models::subtask::{NewSubtask, Subtask}; | ||||
| use crate::server::database_connection::establish_database_connection; | ||||
| use crate::server::tasks::trigger_task_updated_at; | ||||
| use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl, SelectableHelper}; | ||||
| use dioxus::prelude::*; | ||||
| use validator::Validate; | ||||
| use crate::server::tasks::trigger_task_updated_at; | ||||
|  | ||||
| #[server] | ||||
| pub(crate) async fn create_subtask(new_subtask: NewSubtask) | ||||
|                                    -> Result<Subtask, ServerFnError<ErrorVec<SubtaskError>>> { | ||||
| pub(crate) async fn create_subtask( | ||||
|     new_subtask: NewSubtask, | ||||
| ) -> Result<Subtask, ServerFnError<ErrorVec<SubtaskError>>> { | ||||
|     use crate::schema::subtasks; | ||||
|  | ||||
|     // TODO: replace with model sanitization (https://github.com/matous-volf/todo-baggins/issues/13) | ||||
|     let mut new_subtask = new_subtask; | ||||
|     new_subtask.title = new_subtask.title.trim().to_owned(); | ||||
|  | ||||
|     new_subtask.validate() | ||||
|     new_subtask | ||||
|         .validate() | ||||
|         .map_err::<ErrorVec<SubtaskError>, _>(|errors| errors.into())?; | ||||
|  | ||||
|     let mut connection = establish_database_connection() | ||||
|         .map_err::<ErrorVec<SubtaskError>, _>( | ||||
|             |_| vec![SubtaskError::Error(Error::ServerInternal)].into() | ||||
|         )?; | ||||
|     let mut connection = | ||||
|         establish_database_connection().map_err::<ErrorVec<SubtaskError>, _>(|_| { | ||||
|             vec![SubtaskError::Error(Error::ServerInternal)].into() | ||||
|         })?; | ||||
|  | ||||
|     let created_subtask = diesel::insert_into(subtasks::table) | ||||
|         .values(&new_subtask) | ||||
|         .returning(Subtask::as_returning()) | ||||
|         .get_result(&mut connection) | ||||
|         .map_err::<ErrorVec<SubtaskError>, _>(|error| vec![error.into()].into())?; | ||||
|      | ||||
|     trigger_task_updated_at(new_subtask.task_id).await | ||||
|  | ||||
|     trigger_task_updated_at(new_subtask.task_id) | ||||
|         .await | ||||
|         .map_err::<ErrorVec<SubtaskError>, _>(|error_vec| error_vec.into())?; | ||||
|  | ||||
|     Ok(created_subtask) | ||||
| } | ||||
|  | ||||
| #[server] | ||||
| pub(crate) async fn get_subtasks_of_task(filtered_task_id: i32) | ||||
|                                          -> Result<Vec<Subtask>, ServerFnError<ErrorVec<Error>>> { | ||||
| pub(crate) async fn get_subtasks_of_task( | ||||
|     filtered_task_id: i32, | ||||
| ) -> Result<Vec<Subtask>, ServerFnError<ErrorVec<Error>>> { | ||||
|     use crate::schema::subtasks::dsl::*; | ||||
|  | ||||
|     let mut connection = establish_database_connection() | ||||
| @@ -55,43 +59,46 @@ pub(crate) async fn get_subtasks_of_task(filtered_task_id: i32) | ||||
| } | ||||
|  | ||||
| #[server] | ||||
| pub(crate) async fn edit_subtask(subtask_id: i32, new_subtask: NewSubtask) | ||||
|                                  -> Result<Subtask, ServerFnError<ErrorVec<SubtaskError>>> { | ||||
| pub(crate) async fn edit_subtask( | ||||
|     subtask_id: i32, | ||||
|     new_subtask: NewSubtask, | ||||
| ) -> Result<Subtask, ServerFnError<ErrorVec<SubtaskError>>> { | ||||
|     use crate::schema::subtasks::dsl::*; | ||||
|  | ||||
|     // TODO: replace with model sanitization (https://github.com/matous-volf/todo-baggins/issues/13) | ||||
|     let mut new_subtask = new_subtask; | ||||
|     new_subtask.title = new_subtask.title.trim().to_owned(); | ||||
|  | ||||
|     new_subtask.validate() | ||||
|     new_subtask | ||||
|         .validate() | ||||
|         .map_err::<ErrorVec<SubtaskError>, _>(|errors| errors.into())?; | ||||
|  | ||||
|     let mut connection = establish_database_connection() | ||||
|         .map_err::<ErrorVec<SubtaskError>, _>( | ||||
|             |_| vec![SubtaskError::Error(Error::ServerInternal)].into() | ||||
|         )?; | ||||
|     let mut connection = | ||||
|         establish_database_connection().map_err::<ErrorVec<SubtaskError>, _>(|_| { | ||||
|             vec![SubtaskError::Error(Error::ServerInternal)].into() | ||||
|         })?; | ||||
|  | ||||
|     let updated_task = diesel::update(subtasks) | ||||
|         .filter(id.eq(subtask_id)) | ||||
|         .set(( | ||||
|             title.eq(new_subtask.title), | ||||
|             is_completed.eq(new_subtask.is_completed) | ||||
|             is_completed.eq(new_subtask.is_completed), | ||||
|         )) | ||||
|         .returning(Subtask::as_returning()) | ||||
|         .get_result(&mut connection) | ||||
|         .map_err::<ErrorVec<SubtaskError>, _>(|error| vec![error.into()].into())?; | ||||
|  | ||||
|     trigger_task_updated_at(new_subtask.task_id).await | ||||
|     trigger_task_updated_at(new_subtask.task_id) | ||||
|         .await | ||||
|         .map_err::<ErrorVec<SubtaskError>, _>(|error_vec| error_vec.into())?; | ||||
|      | ||||
|  | ||||
|     Ok(updated_task) | ||||
| } | ||||
|  | ||||
| #[server] | ||||
| pub(crate) async fn restore_subtasks_of_task(filtered_task_id: i32) -> Result< | ||||
|     Vec<Subtask>, | ||||
|     ServerFnError<ErrorVec<Error>> | ||||
| > { | ||||
| pub(crate) async fn restore_subtasks_of_task( | ||||
|     filtered_task_id: i32, | ||||
| ) -> Result<Vec<Subtask>, ServerFnError<ErrorVec<Error>>> { | ||||
|     use crate::schema::subtasks::dsl::*; | ||||
|  | ||||
|     let mut connection = establish_database_connection() | ||||
| @@ -110,8 +117,7 @@ pub(crate) async fn restore_subtasks_of_task(filtered_task_id: i32) -> Result< | ||||
| // TODO: Get rid of this suppression. | ||||
| //noinspection DuplicatedCode | ||||
| #[server] | ||||
| pub(crate) async fn delete_subtask(subtask_id: i32) | ||||
|                                    -> Result<(), ServerFnError<ErrorVec<Error>>> { | ||||
| pub(crate) async fn delete_subtask(subtask_id: i32) -> Result<(), ServerFnError<ErrorVec<Error>>> { | ||||
|     use crate::schema::subtasks::dsl::*; | ||||
|  | ||||
|     let mut connection = establish_database_connection() | ||||
|   | ||||
| @@ -1,34 +1,36 @@ | ||||
| use chrono::{Datelike, Days, Local, Months, NaiveDate}; | ||||
| use crate::errors::error::Error; | ||||
| use crate::errors::error_vec::ErrorVec; | ||||
| use crate::models::task::{NewTask, Task, TaskWithSubtasks}; | ||||
| use crate::server::database_connection::establish_database_connection; | ||||
| use diesel::{ExpressionMethods, OptionalExtension, QueryDsl, RunQueryDsl, SelectableHelper}; | ||||
| use dioxus::prelude::*; | ||||
| use diesel::prelude::*; | ||||
| use time::util::days_in_year_month; | ||||
| use validator::Validate; | ||||
| use crate::errors::task_error::TaskError; | ||||
| use crate::models::category::{Category, ReoccurrenceInterval}; | ||||
| use crate::models::subtask::Subtask; | ||||
| use crate::models::task::{NewTask, Task, TaskWithSubtasks}; | ||||
| use crate::server::database_connection::establish_database_connection; | ||||
| use crate::server::subtasks::restore_subtasks_of_task; | ||||
| use chrono::{Datelike, Days, Local, Months, NaiveDate}; | ||||
| use diesel::prelude::*; | ||||
| use diesel::{ExpressionMethods, OptionalExtension, QueryDsl, RunQueryDsl, SelectableHelper}; | ||||
| use dioxus::prelude::*; | ||||
| use time::util::days_in_year_month; | ||||
| use validator::Validate; | ||||
|  | ||||
| #[server] | ||||
| pub(crate) async fn create_task(new_task: NewTask) | ||||
|                                 -> Result<Task, ServerFnError<ErrorVec<TaskError>>> { | ||||
| pub(crate) async fn create_task( | ||||
|     new_task: NewTask, | ||||
| ) -> Result<Task, ServerFnError<ErrorVec<TaskError>>> { | ||||
|     use crate::schema::tasks; | ||||
|      | ||||
|  | ||||
|     // TODO: replace with model sanitization (https://github.com/matous-volf/todo-baggins/issues/13) | ||||
|     let mut new_task = new_task; | ||||
|     new_task.title = new_task.title.trim().to_owned(); | ||||
|  | ||||
|     new_task.validate() | ||||
|     new_task | ||||
|         .validate() | ||||
|         .map_err::<ErrorVec<TaskError>, _>(|errors| errors.into())?; | ||||
|  | ||||
|     let mut connection = establish_database_connection() | ||||
|         .map_err::<ErrorVec<TaskError>, _>( | ||||
|             |_| vec![TaskError::Error(Error::ServerInternal)].into() | ||||
|         )?; | ||||
|     let mut connection = | ||||
|         establish_database_connection().map_err::<ErrorVec<TaskError>, _>(|_| { | ||||
|             vec![TaskError::Error(Error::ServerInternal)].into() | ||||
|         })?; | ||||
|  | ||||
|     let new_task = diesel::insert_into(tasks::table) | ||||
|         .values(&new_task) | ||||
| @@ -58,31 +60,27 @@ pub(crate) async fn get_task(task_id: i32) -> Result<Task, ServerFnError<ErrorVe | ||||
| } | ||||
|  | ||||
| #[server] | ||||
| pub(crate) async fn get_tasks_in_category(filtered_category: Category) | ||||
|                                           -> Result<Vec<Task>, ServerFnError<ErrorVec<Error>>> { | ||||
| pub(crate) async fn get_tasks_in_category( | ||||
|     filtered_category: Category, | ||||
| ) -> Result<Vec<Task>, ServerFnError<ErrorVec<Error>>> { | ||||
|     use crate::schema::tasks::dsl::*; | ||||
|  | ||||
|     let mut connection = establish_database_connection() | ||||
|         .map_err::<ErrorVec<Error>, _>( | ||||
|             |_| vec![Error::ServerInternal].into() | ||||
|         )?; | ||||
|         .map_err::<ErrorVec<Error>, _>(|_| vec![Error::ServerInternal].into())?; | ||||
|  | ||||
|     let results = tasks | ||||
|         .select(Task::as_select()) | ||||
|         .filter(filtered_category.eq_sql_predicate()) | ||||
|         .load::<Task>(&mut connection) | ||||
|         .map_err::<ErrorVec<Error>, _>( | ||||
|             |_| vec![Error::ServerInternal].into() | ||||
|         )?; | ||||
|         .map_err::<ErrorVec<Error>, _>(|_| vec![Error::ServerInternal].into())?; | ||||
|  | ||||
|     Ok(results) | ||||
| } | ||||
|  | ||||
| #[server] | ||||
| pub(crate) async fn get_tasks_with_subtasks_in_category(filtered_category: Category) -> Result< | ||||
|     Vec<TaskWithSubtasks>, | ||||
|     ServerFnError<ErrorVec<Error>> | ||||
| > { | ||||
| pub(crate) async fn get_tasks_with_subtasks_in_category( | ||||
|     filtered_category: Category, | ||||
| ) -> Result<Vec<TaskWithSubtasks>, ServerFnError<ErrorVec<Error>>> { | ||||
|     use crate::schema::tasks; | ||||
|  | ||||
|     let mut connection = establish_database_connection() | ||||
| @@ -90,7 +88,8 @@ pub(crate) async fn get_tasks_with_subtasks_in_category(filtered_category: Categ | ||||
|  | ||||
|     let tasks_in_category = tasks::table | ||||
|         .filter(filtered_category.eq_sql_predicate()) | ||||
|         .select(Task::as_select()).load(&mut connection) | ||||
|         .select(Task::as_select()) | ||||
|         .load(&mut connection) | ||||
|         .map_err::<ErrorVec<Error>, _>(|_| vec![Error::ServerInternal].into())?; | ||||
|  | ||||
|     let subtasks = Subtask::belonging_to(&tasks_in_category) | ||||
| @@ -109,21 +108,24 @@ pub(crate) async fn get_tasks_with_subtasks_in_category(filtered_category: Categ | ||||
| } | ||||
|  | ||||
| #[server] | ||||
| pub(crate) async fn edit_task(task_id: i32, mut new_task: NewTask) | ||||
|                               -> Result<Task, ServerFnError<ErrorVec<TaskError>>> { | ||||
| pub(crate) async fn edit_task( | ||||
|     task_id: i32, | ||||
|     mut new_task: NewTask, | ||||
| ) -> Result<Task, ServerFnError<ErrorVec<TaskError>>> { | ||||
|     use crate::schema::tasks::dsl::*; | ||||
|  | ||||
|     // TODO: replace with model sanitization (https://github.com/matous-volf/todo-baggins/issues/13) | ||||
|     let mut new_task = new_task; | ||||
|     new_task.title = new_task.title.trim().to_owned(); | ||||
|      | ||||
|     new_task.validate() | ||||
|  | ||||
|     new_task | ||||
|         .validate() | ||||
|         .map_err::<ErrorVec<TaskError>, _>(|errors| errors.into())?; | ||||
|  | ||||
|     let mut connection = establish_database_connection() | ||||
|         .map_err::<ErrorVec<TaskError>, _>( | ||||
|             |_| vec![TaskError::Error(Error::ServerInternal)].into() | ||||
|         )?; | ||||
|     let mut connection = | ||||
|         establish_database_connection().map_err::<ErrorVec<TaskError>, _>(|_| { | ||||
|             vec![TaskError::Error(Error::ServerInternal)].into() | ||||
|         })?; | ||||
|  | ||||
|     let updated_task = diesel::update(tasks) | ||||
|         .filter(id.eq(task_id)) | ||||
| @@ -149,15 +151,20 @@ pub(crate) async fn complete_task(task_id: i32) -> Result<Task, ServerFnError<Er | ||||
|         reoccurrence: Some(reoccurrence), | ||||
|         date, | ||||
|         .. | ||||
|     } = &mut new_task.category { | ||||
|     } = &mut new_task.category | ||||
|     { | ||||
|         match reoccurrence.interval() { | ||||
|             ReoccurrenceInterval::Day => *date = *date + Days::new(reoccurrence.length() as u64), | ||||
|             ReoccurrenceInterval::Month | ReoccurrenceInterval::Year => { | ||||
|                 *date = *date + Months::new( | ||||
|                     reoccurrence.length() * | ||||
|                         if *(reoccurrence.interval()) == ReoccurrenceInterval::Year | ||||
|                         { 12 } else { 1 } | ||||
|                 ); | ||||
|                 *date = *date | ||||
|                     + Months::new( | ||||
|                         reoccurrence.length() | ||||
|                             * if *(reoccurrence.interval()) == ReoccurrenceInterval::Year { | ||||
|                                 12 | ||||
|                             } else { | ||||
|                                 1 | ||||
|                             }, | ||||
|                     ); | ||||
|                 *date = NaiveDate::from_ymd_opt( | ||||
|                     date.year(), | ||||
|                     date.month(), | ||||
| @@ -165,7 +172,8 @@ pub(crate) async fn complete_task(task_id: i32) -> Result<Task, ServerFnError<Er | ||||
|                         date.year(), | ||||
|                         (date.month() as u8).try_into().unwrap(), | ||||
|                     ) as u32), | ||||
|                 ).unwrap() | ||||
|                 ) | ||||
|                 .unwrap() | ||||
|             } | ||||
|         } | ||||
|         restore_subtasks_of_task(task_id).await?; | ||||
| @@ -173,7 +181,8 @@ pub(crate) async fn complete_task(task_id: i32) -> Result<Task, ServerFnError<Er | ||||
|         new_task.category = Category::Done; | ||||
|     } | ||||
|  | ||||
|     let updated_task = edit_task(task_id, new_task).await | ||||
|     let updated_task = edit_task(task_id, new_task) | ||||
|         .await | ||||
|         .map_err::<ErrorVec<Error>, _>(|_| vec![Error::ServerInternal].into())?; | ||||
|  | ||||
|     Ok(updated_task) | ||||
| @@ -182,14 +191,14 @@ pub(crate) async fn complete_task(task_id: i32) -> Result<Task, ServerFnError<Er | ||||
| // TODO: Get rid of this suppression. | ||||
| //noinspection DuplicatedCode | ||||
| #[server] | ||||
| pub(crate) async fn delete_task(task_id: i32) | ||||
|                                 -> Result<(), ServerFnError<ErrorVec<Error>>> { | ||||
| pub(crate) async fn delete_task(task_id: i32) -> Result<(), ServerFnError<ErrorVec<Error>>> { | ||||
|     use crate::schema::tasks::dsl::*; | ||||
|  | ||||
|     let mut connection = establish_database_connection() | ||||
|         .map_err::<ErrorVec<Error>, _>(|_| vec![Error::ServerInternal].into())?; | ||||
|  | ||||
|     diesel::delete(tasks.filter(id.eq(task_id))).execute(&mut connection) | ||||
|     diesel::delete(tasks.filter(id.eq(task_id))) | ||||
|         .execute(&mut connection) | ||||
|         .map_err::<ErrorVec<Error>, _>(|error| vec![error.into()].into())?; | ||||
|  | ||||
|     Ok(()) | ||||
| @@ -197,11 +206,9 @@ pub(crate) async fn delete_task(task_id: i32) | ||||
|  | ||||
| pub(crate) async fn trigger_task_updated_at(task_id: i32) -> Result<Task, ErrorVec<Error>> { | ||||
|     use crate::schema::tasks::dsl::*; | ||||
|      | ||||
|  | ||||
|     let mut connection = establish_database_connection() | ||||
|         .map_err::<ErrorVec<Error>, _>( | ||||
|             |_| vec![Error::ServerInternal].into() | ||||
|         )?; | ||||
|         .map_err::<ErrorVec<Error>, _>(|_| vec![Error::ServerInternal].into())?; | ||||
|  | ||||
|     let updated_task = diesel::update(tasks) | ||||
|         .filter(id.eq(task_id)) | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| use std::cmp::Ordering; | ||||
| use std::ops::Deref; | ||||
| /* The default ordering of `Option`s is `None` being less than `Some`. The purpose of this struct is | ||||
|    to reverse that. */ | ||||
| to reverse that. */ | ||||
| #[derive(PartialEq)] | ||||
| pub(crate) struct ReverseOrdOption<'a, T>(&'a Option<T>); | ||||
|  | ||||
| @@ -27,7 +27,7 @@ impl<'a, T: Ord> Ord for ReverseOrdOption<'a, T> { | ||||
|             (None, None) => Ordering::Equal, | ||||
|             (None, Some(_)) => Ordering::Greater, | ||||
|             (Some(_), None) => Ordering::Less, | ||||
|             (Some(self_time), Some(other_time)) => self_time.cmp(other_time) | ||||
|             (Some(self_time), Some(other_time)) => self_time.cmp(other_time), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user