feat: list sorting #42

Merged
matous-volf merged 9 commits from feat/list-sorting into main 2024-09-09 17:09:22 +00:00
16 changed files with 277 additions and 116 deletions

View File

@ -41,7 +41,7 @@ pub(crate) fn BottomPanel(display_form: Signal<bool>) -> Element {
(false, _, false) => "h-[66px]",
(false, _, true) => "h-[130px]",
(true, Route::ProjectsPage, _) => "h-[130px]",
(true, _, _) => "h-[448px]",
(true, _, _) => "h-[506px]",
}
),
if expanded() {

View File

@ -13,3 +13,4 @@ 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;

View File

@ -6,11 +6,12 @@ use crate::query::QueryValue;
use chrono::{Local, Locale};
use dioxus::prelude::*;
use dioxus_query::prelude::QueryResult;
use crate::components::task_list_item::TaskListItem;
#[component]
pub(crate) fn CategoryTodayPage() -> Element {
let today_date = Local::now().date_naive();
let calendar_tasks_query = use_tasks_with_subtasks_in_category_query(Category::Calendar {
date: today_date,
reoccurrence: None,
@ -26,48 +27,36 @@ pub(crate) fn CategoryTodayPage() -> Element {
class: "pt-4 flex flex-col gap-8",
match long_term_tasks_query_result.value() {
QueryResult::Ok(QueryValue::TasksWithSubtasks(tasks))
| QueryResult::Loading(Some(QueryValue::TasksWithSubtasks(tasks))) => rsx! {
div {
class: "flex flex-col gap-4",
| QueryResult::Loading(Some(QueryValue::TasksWithSubtasks(tasks))) => {
let mut tasks = tasks.clone();
tasks.sort();
rsx! {
div {
class: "px-8 flex flex-row items-center gap-2 font-bold",
i {
class: "fa-solid fa-water text-xl w-6 text-center"
class: "flex flex-col gap-4",
div {
class: "px-8 flex flex-row items-center gap-2 font-bold",
i {
class: "fa-solid fa-water text-xl w-6 text-center"
}
div {
class: "mt-1",
"Long-term"
}
}
div {
class: "mt-1",
"Long-term"
}
}
div {
for task in tasks {
div {
key: "{task.task().id()}",
class: format!(
"px-8 pt-5 {} flex flex-row gap-4",
if task.task().deadline().is_some() {
"pb-0.5"
} else {
"pb-5"
}
),
for task in tasks {
div {
class: "flex flex-col",
div {
class: "mt grow font-medium",
{task.task().title()}
},
div {
class: "flex flex-row gap-3",
if let Some(deadline) = task.task().deadline() {
div {
class: "text-sm text-zinc-400",
i {
class: "fa-solid fa-bomb"
},
{deadline.format(" %m. %d.").to_string()}
}
key: "{task.task().id()}",
class: format!(
"px-8 pt-5 {} flex flex-row gap-4",
if task.task().deadline().is_some() {
"pb-0.5"
} else {
"pb-5"
}
),
TaskListItem {
task: task.clone()
}
}
}

View File

@ -12,20 +12,24 @@ pub(crate) fn ProjectsPage() -> Element {
rsx! {
match projects_query.result().value() {
QueryResult::Ok(QueryValue::Projects(projects))
| QueryResult::Loading(Some(QueryValue::Projects(projects))) => rsx! {
div {
class: "flex flex-col",
for project in projects.clone() {
div {
key: "{project.id()}",
class: format!(
"px-8 py-4 select-none {}",
if project_being_edited().is_some_and(|p| p.id() == project.id()) {
"bg-zinc-700"
} else { "" }
),
onclick: move |_| project_being_edited.set(Some(project.clone())),
{project.title()}
| QueryResult::Loading(Some(QueryValue::Projects(projects))) => {
let mut projects = projects.clone();
projects.sort();
rsx! {
div {
class: "flex flex-col",
for project in projects {
div {
key: "{project.id()}",
class: format!(
"px-8 py-4 select-none {}",
if project_being_edited().is_some_and(|p| p.id() == project.id()) {
"bg-zinc-700"
} else { "" }
),
onclick: move |_| project_being_edited.set(Some(project.clone())),
{project.title()}
}
}
}
}

View File

@ -64,8 +64,10 @@ pub(crate) fn SubtasksForm(task: Task) -> Element {
match subtasks_query.result().value() {
QueryResult::Ok(QueryValue::Subtasks(subtasks))
| QueryResult::Loading(Some(QueryValue::Subtasks(subtasks))) => {
let mut subtasks = subtasks.clone();
subtasks.sort();
coderabbitai[bot] commented 2024-09-09 17:01:00 +00:00 (Migrated from github.com)
Review

Consider performance implications of cloning and sorting subtasks.

The addition of sorting to the subtasks is a great feature for improving the user interface. However, cloning and then sorting the entire list of subtasks could be performance-intensive, especially with a large number of subtasks. Consider optimizing this by sorting the subtasks in place if the data structure allows, or by using more efficient sorting algorithms or data structures that maintain order.

**Consider performance implications of cloning and sorting subtasks.** The addition of sorting to the subtasks is a great feature for improving the user interface. However, cloning and then sorting the entire list of subtasks could be performance-intensive, especially with a large number of subtasks. Consider optimizing this by sorting the subtasks in place if the data structure allows, or by using more efficient sorting algorithms or data structures that maintain order. <!-- This is an auto-generated comment by CodeRabbit -->
rsx! {
for subtask in subtasks.clone() {
for subtask in subtasks {
div {
coderabbitai[bot] commented 2024-09-09 17:01:00 +00:00 (Migrated from github.com)
Review

Optimize cloning within the rendering loop.

Each iteration of the loop clones subtask and task multiple times, which can lead to significant performance overhead, especially with a large number of subtasks. Consider restructuring the code to minimize cloning, possibly by using references or restructuring the data flow to reduce the need for cloning.


Improve error handling strategy.

The use of panic! in the default case of the match statement handling query results is a risky approach that could lead to crashes in production. Consider replacing this with a more user-friendly error message or a recovery strategy that does not terminate the application.

**Optimize cloning within the rendering loop.** Each iteration of the loop clones `subtask` and `task` multiple times, which can lead to significant performance overhead, especially with a large number of subtasks. Consider restructuring the code to minimize cloning, possibly by using references or restructuring the data flow to reduce the need for cloning. --- **Improve error handling strategy.** The use of `panic!` in the default case of the match statement handling query results is a risky approach that could lead to crashes in production. Consider replacing this with a more user-friendly error message or a recovery strategy that does not terminate the application. <!-- This is an auto-generated comment by CodeRabbit -->
key: "{subtask.id()}",
class: "flex flex-row items-center gap-3",

View File

@ -4,6 +4,7 @@ use dioxus::core_macro::rsx;
use dioxus::dioxus_core::Element;
use dioxus::prelude::*;
use dioxus_query::prelude::use_query_client;
use crate::components::task_list_item::TaskListItem;
use crate::query::{QueryErrors, QueryKey, QueryValue};
use crate::server::tasks::complete_task;
@ -12,6 +13,8 @@ pub(crate) fn TaskList(tasks: Vec<TaskWithSubtasks>, class: Option<&'static str>
let query_client = use_query_client::<QueryValue, QueryErrors, QueryKey>();
let mut task_being_edited = use_context::<Signal<Option<Task>>>();
tasks.sort();
rsx! {
div {
class: format!("flex flex-col {}", class.unwrap_or("")),
@ -74,50 +77,8 @@ pub(crate) fn TaskList(tasks: Vec<TaskWithSubtasks>, class: Option<&'static str>
}
}
},
div {
class: "flex flex-col",
div {
class: "mt-1 grow font-medium",
{task.task().title()}
},
div {
class: "flex flex-row gap-4",
if let Some(deadline) = task.task().deadline() {
div {
class: "text-sm text-zinc-400",
i {
class: "fa-solid fa-bomb"
},
{deadline.format(" %m. %-d.").to_string()}
}
}
if let Category::Calendar { time, .. } = task.task().category() {
if let Some(calendar_time) = time {
div {
class: "text-sm text-zinc-400",
i {
class: "fa-solid fa-clock"
},
{calendar_time.time().format(" %k:%M").to_string()}
}
}
}
if !task.subtasks().is_empty() {
div {
class: "text-sm text-zinc-400",
i {
class: "fa-solid fa-list-check"
},
{format!(
" {}/{}",
task.subtasks().iter()
.filter(|subtask| subtask.is_completed())
.count(),
task.subtasks().len()
)}
}
}
}
TaskListItem {
task: task.clone()
}
}
}

View File

@ -0,0 +1,61 @@
use chrono::{Datelike, Local};
use crate::models::category::Category;
use crate::models::task::TaskWithSubtasks;
use dioxus::core_macro::rsx;
use dioxus::dioxus_core::Element;
use dioxus::prelude::*;
#[component]
pub(crate) fn TaskListItem(task: TaskWithSubtasks) -> Element {
rsx! {
div {
class: "flex flex-col",
div {
class: "mt-1 grow font-medium",
{task.task().title()}
},
div {
class: "flex flex-row gap-4",
if let Some(deadline) = task.task().deadline() {
div {
class: "text-sm text-zinc-400",
i {
class: "fa-solid fa-bomb"
},
{deadline.format(if deadline.year() == Local::now().year() {
" %m. %-d."
} else {
" %m. %-d. %Y"
}).to_string()}
}
}
if let Category::Calendar { time, .. } = task.task().category() {
if let Some(calendar_time) = time {
div {
class: "text-sm text-zinc-400",
i {
class: "fa-solid fa-clock"
},
{calendar_time.time().format(" %k:%M").to_string()}
}
}
}
if !task.subtasks().is_empty() {
div {
class: "text-sm text-zinc-400",
i {
class: "fa-solid fa-list-check"
},
{format!(
" {}/{}",
task.subtasks().iter()
.filter(|subtask| subtask.is_completed())
.count(),
task.subtasks().len()
)}
}
}
}
}
}
}
coderabbitai[bot] commented 2024-09-09 17:00:59 +00:00 (Migrated from github.com)
Review

Approved: TaskListItem component implementation.

The component is well-implemented with effective use of conditional rendering and UI elements. The handling of tasks, deadlines, and subtasks is appropriate and enhances user interaction.

Consider adding comments to complex conditional blocks for better maintainability and readability, especially for new developers or when revisiting the code later.

**Approved: `TaskListItem` component implementation.** The component is well-implemented with effective use of conditional rendering and UI elements. The handling of tasks, deadlines, and subtasks is appropriate and enhances user interaction. Consider adding comments to complex conditional blocks for better maintainability and readability, especially for new developers or when revisiting the code later. <!-- This is an auto-generated comment by CodeRabbit -->

View File

@ -53,6 +53,13 @@ impl From<diesel::result::Error> for SubtaskError {
}
}
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()
}
coderabbitai[bot] commented 2024-09-09 17:00:59 +00:00 (Migrated from github.com)
Review

Approved: Conversion from ErrorVec<Error> to ErrorVec<SubtaskError>.

The implementation is correct and enhances the error handling capabilities by allowing seamless integration of generic errors into SubtaskError. This is a valuable addition for robust error management.

Consider adding error logging at this conversion point to aid in debugging and maintaining error traceability.

**Approved: Conversion from `ErrorVec<Error>` to `ErrorVec<SubtaskError>`.** The implementation is correct and enhances the error handling capabilities by allowing seamless integration of generic errors into `SubtaskError`. This is a valuable addition for robust error management. Consider adding error logging at this conversion point to aid in debugging and maintaining error traceability. <!-- This is an auto-generated comment by CodeRabbit -->
}
// Has to be implemented for Dioxus server functions.
impl Display for SubtaskError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {

View File

@ -5,6 +5,7 @@ mod route;
mod schema;
mod server;
mod query;
mod utils;
use components::app::App;
use dioxus::prelude::*;

View File

@ -1,3 +1,4 @@
use std::cmp::Ordering;
use chrono::NaiveDateTime;
use crate::schema::projects;
use diesel::prelude::*;
@ -35,6 +36,20 @@ impl Project {
}
}
impl Eq for Project {}
impl PartialOrd<Self> for Project {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Project {
fn cmp(&self, other: &Self) -> Ordering {
self.title().cmp(other.title())
}
}
#[derive(Insertable, Serialize, Deserialize, Validate, Clone, Debug)]
#[diesel(table_name = projects)]
pub struct NewProject {

View File

@ -1,3 +1,4 @@
use std::cmp::Ordering;
use crate::models::task::Task;
use crate::schema::subtasks;
use chrono::NaiveDateTime;
@ -48,6 +49,21 @@ impl Subtask {
}
}
impl Eq for Subtask {}
impl PartialOrd<Self> for Subtask {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Subtask {
fn cmp(&self, other: &Self) -> Ordering {
self.is_completed().cmp(&other.is_completed())
.then(self.created_at().cmp(&other.created_at()))
}
}
#[derive(Insertable, Serialize, Deserialize, Validate, Clone, Debug)]
#[diesel(table_name = subtasks)]
pub struct NewSubtask {
coderabbitai[bot] commented 2024-09-09 17:00:59 +00:00 (Migrated from github.com)
Review

Approved: Ord trait implementation for Subtask.

The dual-layered comparison logic is well-implemented, prioritizing completion status and then ordering by creation timestamps. This is logical and efficient for the intended use case.

However, consider caching the results of is_completed() and created_at() if they are called frequently in performance-critical sections.

**Approved: `Ord` trait implementation for `Subtask`.** The dual-layered comparison logic is well-implemented, prioritizing completion status and then ordering by creation timestamps. This is logical and efficient for the intended use case. However, consider caching the results of `is_completed()` and `created_at()` if they are called frequently in performance-critical sections. <!-- This is an auto-generated comment by CodeRabbit -->

View File

@ -1,10 +1,12 @@
use chrono::NaiveDateTime;
use crate::models::category::Category;
use crate::models::subtask::Subtask;
use crate::schema::tasks;
use crate::utils::reverse_ord_option::ReverseOrdOption;
use chrono::NaiveDateTime;
use diesel::prelude::*;
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use validator::Validate;
use crate::models::subtask::Subtask;
const TITLE_LENGTH_MIN: u64 = 1;
const TITLE_LENGTH_MAX: u64 = 255;
@ -52,6 +54,40 @@ impl Task {
}
}
impl Eq for Task {}
impl PartialOrd<Self> for Task {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Task {
fn cmp(&self, other: &Self) -> Ordering {
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())
))
.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)),
}
}
}
#[derive(Serialize, Deserialize, PartialEq, Clone, Debug)]
pub struct TaskWithSubtasks {
task: Task,
@ -72,6 +108,20 @@ impl TaskWithSubtasks {
}
}
impl Eq for TaskWithSubtasks {}
impl PartialOrd<Self> for TaskWithSubtasks {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for TaskWithSubtasks {
fn cmp(&self, other: &Self) -> Ordering {
self.task().cmp(other.task())
}
}
#[derive(Insertable, Serialize, Deserialize, Validate, Clone, Debug)]
#[diesel(table_name = tasks)]
pub struct NewTask {

View File

@ -6,6 +6,7 @@ use crate::server::database_connection::establish_database_connection;
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)
@ -25,6 +26,9 @@ pub(crate) async fn create_subtask(new_subtask: NewSubtask)
.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
.map_err::<ErrorVec<SubtaskError>, _>(|error_vec| error_vec.into())?;
Ok(created_subtask)
}
@ -35,17 +39,13 @@ pub(crate) async fn get_subtasks_of_task(filtered_task_id: i32)
use crate::schema::subtasks::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 = subtasks
.select(Subtask::as_select())
.filter(task_id.eq(filtered_task_id))
.load::<Subtask>(&mut connection)
.map_err::<ErrorVec<Error>, _>(
|_| vec![Error::ServerInternal].into()
)?;
.map_err::<ErrorVec<Error>, _>(|_| vec![Error::ServerInternal].into())?;
Ok(results)
}
@ -73,27 +73,28 @@ pub(crate) async fn edit_subtask(subtask_id: i32, new_subtask: NewSubtask)
.get_result(&mut connection)
.map_err::<ErrorVec<SubtaskError>, _>(|error| vec![error.into()].into())?;
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<SubtaskError>>
ServerFnError<ErrorVec<Error>>
> {
use crate::schema::subtasks::dsl::*;
let mut connection = establish_database_connection()
.map_err::<ErrorVec<SubtaskError>, _>(
|_| vec![SubtaskError::Error(Error::ServerInternal)].into()
)?;
.map_err::<ErrorVec<Error>, _>(|_| vec![Error::ServerInternal].into())?;
let updated_subtasks = diesel::update(subtasks)
.filter(task_id.eq(filtered_task_id))
.set(is_completed.eq(false))
.returning(Subtask::as_returning())
.get_results(&mut connection)
.map_err::<ErrorVec<SubtaskError>, _>(|error| vec![error.into()].into())?;
.map_err::<ErrorVec<Error>, _>(|error| vec![error.into()].into())?;
Ok(updated_subtasks)
}
@ -108,8 +109,12 @@ pub(crate) async fn delete_subtask(subtask_id: i32)
let mut connection = establish_database_connection()
.map_err::<ErrorVec<Error>, _>(|_| vec![Error::ServerInternal].into())?;
diesel::delete(subtasks.filter(id.eq(subtask_id))).execute(&mut connection)
let deleted_subtask = diesel::delete(subtasks.filter(id.eq(subtask_id)))
.returning(Subtask::as_returning())
.get_result(&mut connection)
.map_err::<ErrorVec<Error>, _>(|error| vec![error.into()].into())?;
coderabbitai[bot] commented 2024-09-09 17:01:00 +00:00 (Migrated from github.com)
Review

Tip

Codebase Verification

Inconsistent error handling in delete_subtask.

The error handling for trigger_task_updated_at in create_subtask and edit_subtask is consistent, using map_err to convert the error type. However, delete_subtask does not follow this pattern, which may lead to inconsistencies in error handling across the codebase. Consider reviewing and aligning the error handling in delete_subtask with the other functions.

  • src/server/subtasks.rs: Review error handling in delete_subtask.
Analysis chain

Approve timestamp update integration, suggest verifying error handling.

The integration of trigger_task_updated_at in edit_subtask is consistent with the changes made in create_subtask, ensuring that the parent task's timestamp is updated upon subtask modification. The error handling appears correct, but consistency across the system should be verified.

Run the following script to verify the error handling consistency:

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify error handling consistency for `trigger_task_updated_at`.

# Test: Search for error handling patterns. Expect: Consistent error mapping.
rg --type rust -A 5 $'trigger_task_updated_at'

Length of output: 1617

> [!TIP] > Codebase Verification **Inconsistent error handling in `delete_subtask`.** The error handling for `trigger_task_updated_at` in `create_subtask` and `edit_subtask` is consistent, using `map_err` to convert the error type. However, `delete_subtask` does not follow this pattern, which may lead to inconsistencies in error handling across the codebase. Consider reviewing and aligning the error handling in `delete_subtask` with the other functions. - `src/server/subtasks.rs`: Review error handling in `delete_subtask`. <details> <summary>Analysis chain</summary> **Approve timestamp update integration, suggest verifying error handling.** The integration of `trigger_task_updated_at` in `edit_subtask` is consistent with the changes made in `create_subtask`, ensuring that the parent task's timestamp is updated upon subtask modification. The error handling appears correct, but consistency across the system should be verified. Run the following script to verify the error handling consistency: </details> <details> <summary>Scripts executed</summary> The following scripts were executed for the analysis: Script: ```shell #!/bin/bash # Description: Verify error handling consistency for `trigger_task_updated_at`. # Test: Search for error handling patterns. Expect: Consistent error mapping. rg --type rust -A 5 $'trigger_task_updated_at' ``` Length of output: 1617 </details> <!-- This is an auto-generated comment by CodeRabbit -->
trigger_task_updated_at(deleted_subtask.task_id()).await?;
Ok(())
}

View File

@ -1,4 +1,4 @@
use chrono::{Datelike, Days, Months, NaiveDate};
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};
@ -80,7 +80,7 @@ pub(crate) async fn get_tasks_with_subtasks_in_category(filtered_category: Categ
ServerFnError<ErrorVec<Error>>
> {
use crate::schema::tasks;
let mut connection = establish_database_connection()
.map_err::<ErrorVec<Error>, _>(|_| vec![Error::ServerInternal].into())?;
@ -160,8 +160,7 @@ pub(crate) async fn complete_task(task_id: i32) -> Result<Task, ServerFnError<Er
).unwrap()
}
}
restore_subtasks_of_task(task_id).await
.map_err::<ErrorVec<Error>, _>(|_| vec![Error::ServerInternal].into())?;
restore_subtasks_of_task(task_id).await?;
} else {
new_task.category = Category::Done;
}
@ -187,3 +186,21 @@ pub(crate) async fn delete_task(task_id: i32)
Ok(())
}
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()
)?;
let updated_task = diesel::update(tasks)
.filter(id.eq(task_id))
.set(updated_at.eq(Local::now().naive_local()))
.returning(Task::as_returning())
.get_result(&mut connection)
.map_err::<ErrorVec<Error>, _>(|error| vec![error.into()].into())?;
Ok(updated_task)
}

1
src/utils/mod.rs Normal file
View File

@ -0,0 +1 @@
pub(crate) mod reverse_ord_option;
coderabbitai[bot] commented 2024-09-09 17:00:59 +00:00 (Migrated from github.com)
Review

Approve module declaration but recommend adding documentation.

The module declaration pub(crate) mod reverse_ord_option; is correctly scoped for internal crate usage. However, it would be beneficial to add documentation to explain the purpose and functionality of this module, especially since it is a new addition to the codebase.

Would you like me to help draft initial documentation for this module?

**Approve module declaration but recommend adding documentation.** The module declaration `pub(crate) mod reverse_ord_option;` is correctly scoped for internal crate usage. However, it would be beneficial to add documentation to explain the purpose and functionality of this module, especially since it is a new addition to the codebase. Would you like me to help draft initial documentation for this module? <!-- This is an auto-generated comment by CodeRabbit -->

View File

@ -0,0 +1,31 @@
use std::cmp::Ordering;
/* The default ordering of `Option`s is `None` being less than `Some`. The purpose of this struct is
to reverse that. */
#[derive(PartialEq)]
pub(crate) struct ReverseOrdOption<'a, T>(&'a Option<T>);
impl<'a, T: Ord> Eq for ReverseOrdOption<'a, T> {}
impl<'a, T: Ord> PartialOrd<Self> for ReverseOrdOption<'a, T> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<'a, T: Ord> Ord for ReverseOrdOption<'a, T> {
fn cmp(&self, other: &Self) -> Ordering {
match (self.0.as_ref(), other.0.as_ref()) {
(None, None) => Ordering::Equal,
(None, Some(_)) => Ordering::Greater,
(Some(_), None) => Ordering::Less,
(Some(self_time), Some(other_time)) => self_time.cmp(other_time)
}
}
}
impl<'a, T> From<&'a Option<T>> for ReverseOrdOption<'a, T> {
fn from(value: &'a Option<T>) -> Self {
ReverseOrdOption(value)
}
}