feat: internationalization ()

This commit is contained in:
Matouš Volf 2024-09-10 16:44:47 +02:00 committed by GitHub
commit 144905369e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 314 additions and 42 deletions

@ -1 +1,2 @@
DATABASE_URL=postgres://app:app@db/todo_baggins DATABASE_URL=postgres://app:app@db/todo_baggins
LANGUAGE_CODE=en-US

87
Cargo.lock generated

@ -1021,6 +1021,22 @@ dependencies = [
"tracing", "tracing",
] ]
[[package]]
name = "dioxus-sdk"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "271adf41837fbbceb955fefd71816d4a3fbbab2829f8c0ea0364584b531ce999"
dependencies = [
"cfg-if",
"dioxus",
"js-sys",
"serde",
"serde_json",
"tracing",
"unic-langid",
"uuid",
]
[[package]] [[package]]
name = "dioxus-signals" name = "dioxus-signals"
version = "0.5.7" version = "0.5.7"
@ -1096,6 +1112,17 @@ dependencies = [
"syn 2.0.74", "syn 2.0.74",
] ]
[[package]]
name = "displaydoc"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.74",
]
[[package]] [[package]]
name = "dotenvy" name = "dotenvy"
version = "0.15.7" version = "0.15.7"
@ -1384,8 +1411,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"js-sys",
"libc", "libc",
"wasi", "wasi",
"wasm-bindgen",
] ]
[[package]] [[package]]
@ -2772,6 +2801,12 @@ dependencies = [
"lock_api", "lock_api",
] ]
[[package]]
name = "stfu8"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51f1e89f093f99e7432c491c382b88a6860a5adbe6bf02574bf0a08efff1978"
[[package]] [[package]]
name = "strsim" name = "strsim"
version = "0.11.1" version = "0.11.1"
@ -2872,6 +2907,15 @@ dependencies = [
"time-core", "time-core",
] ]
[[package]]
name = "tinystr"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
dependencies = [
"displaydoc",
]
[[package]] [[package]]
name = "tinyvec" name = "tinyvec"
version = "1.8.0" version = "1.8.0"
@ -2903,6 +2947,7 @@ dependencies = [
"dioxus", "dioxus",
"dioxus-logger", "dioxus-logger",
"dioxus-query", "dioxus-query",
"dioxus-sdk",
"dotenvy", "dotenvy",
"serde", "serde",
"serde_json", "serde_json",
@ -2910,7 +2955,9 @@ dependencies = [
"time", "time",
"tracing", "tracing",
"tracing-wasm", "tracing-wasm",
"unic-langid-impl",
"validator", "validator",
"voca_rs",
] ]
[[package]] [[package]]
@ -3138,6 +3185,25 @@ version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "unic-langid"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23dd9d1e72a73b25e07123a80776aae3e7b0ec461ef94f9151eed6ec88005a44"
dependencies = [
"unic-langid-impl",
]
[[package]]
name = "unic-langid-impl"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a5422c1f65949306c99240b81de9f3f15929f5a8bfe05bb44b034cc8bf593e5"
dependencies = [
"serde",
"tinystr",
]
[[package]] [[package]]
name = "unicase" name = "unicase"
version = "2.7.0" version = "2.7.0"
@ -3203,6 +3269,16 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
[[package]]
name = "uuid"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314"
dependencies = [
"getrandom",
"wasm-bindgen",
]
[[package]] [[package]]
name = "validator" name = "validator"
version = "0.18.1" version = "0.18.1"
@ -3257,6 +3333,17 @@ version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "voca_rs"
version = "1.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e44efbf25e32768d5ecd22244feacc3d3b3eca72d318f5ef0a4764c2c158e18"
dependencies = [
"regex",
"stfu8",
"unicode-segmentation",
]
[[package]] [[package]]
name = "waker-fn" name = "waker-fn"
version = "1.2.0" version = "1.2.0"

@ -24,6 +24,9 @@ serde_with = { version = "3.9.0", features = ["chrono_0_4"] }
async-std = "1.12.0" async-std = "1.12.0"
dioxus-query = "0.5.1" dioxus-query = "0.5.1"
time = "0.3.36" time = "0.3.36"
dioxus-sdk = { version = "0.5.0", features = ["i18n"] }
unic-langid-impl = "0.9.5"
voca_rs = "1.15.2"
[features] [features]
default = [] default = []

@ -4,11 +4,17 @@ use dioxus::dioxus_core::Element;
use dioxus::prelude::*; use dioxus::prelude::*;
use dioxus_query::prelude::{use_init_query_client}; use dioxus_query::prelude::{use_init_query_client};
use crate::query::{QueryErrors, QueryKey, QueryValue}; use crate::query::{QueryErrors, QueryKey, QueryValue};
use dioxus_sdk::i18n::{use_init_i18n};
use crate::internationalization::get_languages;
use crate::server::internationalization::get_language_identifier;
#[component] #[component]
pub(crate) fn App() -> Element { pub(crate) fn App() -> Element {
use_init_query_client::<QueryValue, QueryErrors, QueryKey>(); use_init_query_client::<QueryValue, QueryErrors, QueryKey>();
let language_identifier = use_server_future(get_language_identifier)?.unwrap().unwrap();
use_init_i18n(language_identifier.clone(), language_identifier, get_languages);
rsx! { rsx! {
div { div {
class: "min-h-screen text-zinc-200 bg-zinc-800 pt-4 pb-36", class: "min-h-screen text-zinc-200 bg-zinc-800 pt-4 pb-36",

@ -1,13 +1,16 @@
use crate::components::task_list::TaskList;
use crate::internationalization::LocaleFromLanguageIdentifier;
use crate::models::category::Category; use crate::models::category::Category;
use chrono::{Datelike, Local, Locale}; use crate::models::task::TaskWithSubtasks;
use crate::query::tasks::use_tasks_with_subtasks_in_category_query;
use crate::query::QueryValue;
use chrono::{Datelike, Local};
use dioxus::core_macro::rsx; use dioxus::core_macro::rsx;
use dioxus::dioxus_core::Element; use dioxus::dioxus_core::Element;
use dioxus::prelude::*; use dioxus::prelude::*;
use dioxus_query::prelude::QueryResult; use dioxus_query::prelude::QueryResult;
use crate::components::task_list::TaskList; use dioxus_sdk::i18n::use_i18;
use crate::query::QueryValue; use dioxus_sdk::translate;
use crate::query::tasks::use_tasks_with_subtasks_in_category_query;
use crate::models::task::{TaskWithSubtasks};
const CALENDAR_LENGTH_DAYS: usize = 366 * 3; const CALENDAR_LENGTH_DAYS: usize = 366 * 3;
@ -20,6 +23,8 @@ pub(crate) fn CategoryCalendarPage() -> Element {
}); });
let tasks_query_result = tasks.result(); let tasks_query_result = tasks.result();
let i18 = use_i18();
rsx! { rsx! {
match tasks_query_result.value() { match tasks_query_result.value() {
QueryResult::Ok(QueryValue::TasksWithSubtasks(tasks)) QueryResult::Ok(QueryValue::TasksWithSubtasks(tasks))
@ -37,14 +42,17 @@ pub(crate) fn CategoryCalendarPage() -> Element {
div { div {
class: "pt-1", class: "pt-1",
{ {
date_current date_current.format_localized(translate!(
.format_localized( i18,
format!( if date_current.year() == Local::now().year() {
"%A %-d. %B{}", "formats.date_weekday_format"
if date_current.year() != today_date.year() } else {
{" %Y"} else {""} "formats.date_weekday_year_format"
}
).as_str(), ).as_str(),
Locale::en_US LocaleFromLanguageIdentifier::from(
&(i18.selected_language)()
).into()
) )
.to_string() .to_string()
} }

@ -1,12 +1,16 @@
use crate::components::task_list::TaskList; use crate::components::task_list::TaskList;
use crate::components::task_list_item::TaskListItem;
use crate::internationalization::LocaleFromLanguageIdentifier;
use crate::models::category::Category; use crate::models::category::Category;
use crate::models::task::TaskWithSubtasks; use crate::models::task::TaskWithSubtasks;
use crate::query::tasks::{use_tasks_with_subtasks_in_category_query}; use crate::query::tasks::use_tasks_with_subtasks_in_category_query;
use crate::query::QueryValue; use crate::query::QueryValue;
use chrono::{Local, Locale}; use chrono::Local;
use dioxus::prelude::*; use dioxus::prelude::*;
use dioxus_query::prelude::QueryResult; use dioxus_query::prelude::QueryResult;
use crate::components::task_list_item::TaskListItem; use dioxus_sdk::i18n::use_i18;
use dioxus_sdk::translate;
use voca_rs::Voca;
#[component] #[component]
pub(crate) fn CategoryTodayPage() -> Element { pub(crate) fn CategoryTodayPage() -> Element {
@ -22,6 +26,8 @@ pub(crate) fn CategoryTodayPage() -> Element {
let long_term_tasks_query = use_tasks_with_subtasks_in_category_query(Category::LongTerm); 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(); let long_term_tasks_query_result = long_term_tasks_query.result();
let i18 = use_i18();
rsx! { rsx! {
div { div {
class: "pt-4 flex flex-col gap-8", class: "pt-4 flex flex-col gap-8",
@ -40,7 +46,7 @@ pub(crate) fn CategoryTodayPage() -> Element {
} }
div { div {
class: "mt-1", class: "mt-1",
"Long-term" {translate!(i18, "long_term")._upper_first()}
} }
} }
div { div {
@ -103,7 +109,7 @@ pub(crate) fn CategoryTodayPage() -> Element {
} }
div { div {
class: "mt-1", class: "mt-1",
"Overdue" {translate!(i18, "overdue")._upper_first()}
} }
} }
TaskList { TaskList {
@ -122,9 +128,23 @@ pub(crate) fn CategoryTodayPage() -> Element {
div { div {
class: "mt-1", class: "mt-1",
{ {
today_date let format = translate!(i18, "formats.date_weekday_format");
.format_localized("Today, %A %-d. %B", Locale::en_US) let today_date = today_date.format_localized(
.to_string() format.as_str(),
LocaleFromLanguageIdentifier::from(
&(i18.selected_language)()
).into()
).to_string();
format!(
"{} {}",
translate!(i18, "today")._upper_first(),
if translate!(i18, "formats.weekday_lowercase_first")
.parse().unwrap() {
today_date._lower_first()
} else {
today_date
}
)
} }
} }
} }

@ -1,5 +1,6 @@
use crate::components::category_input::CategoryInput; use crate::components::category_input::CategoryInput;
use crate::components::reoccurrence_input::ReoccurrenceIntervalInput; use crate::components::reoccurrence_input::ReoccurrenceIntervalInput;
use crate::components::subtasks_form::SubtasksForm;
use crate::models::category::{CalendarTime, Category, Reoccurrence}; use crate::models::category::{CalendarTime, Category, Reoccurrence};
use crate::models::task::NewTask; use crate::models::task::NewTask;
use crate::models::task::Task; use crate::models::task::Task;
@ -7,12 +8,14 @@ use crate::query::{QueryErrors, QueryKey, QueryValue};
use crate::route::Route; use crate::route::Route;
use crate::server::projects::get_projects; use crate::server::projects::get_projects;
use crate::server::tasks::{create_task, delete_task, edit_task}; use crate::server::tasks::{create_task, delete_task, edit_task};
use chrono::{Duration}; use chrono::Duration;
use dioxus::core_macro::{component, rsx}; use dioxus::core_macro::{component, rsx};
use dioxus::dioxus_core::Element; use dioxus::dioxus_core::Element;
use dioxus::prelude::*; use dioxus::prelude::*;
use dioxus_query::prelude::use_query_client; use dioxus_query::prelude::use_query_client;
use crate::components::subtasks_form::SubtasksForm; use dioxus_sdk::i18n::use_i18;
use dioxus_sdk::translate;
use voca_rs::Voca;
const REMINDER_OFFSETS: [Option<Duration>; 17] = [ const REMINDER_OFFSETS: [Option<Duration>; 17] = [
None, None,
@ -79,6 +82,8 @@ pub(crate) fn TaskForm(task: Option<Task>, on_successful_submit: EventHandler<()
let query_client = use_query_client::<QueryValue, QueryErrors, QueryKey>(); let query_client = use_query_client::<QueryValue, QueryErrors, QueryKey>();
let task_for_submit = task.clone(); let task_for_submit = task.clone();
let i18 = use_i18();
rsx! { rsx! {
div { div {
class: "p-4 flex flex-col gap-4", class: "p-4 flex flex-col gap-4",
@ -172,7 +177,7 @@ pub(crate) fn TaskForm(task: Option<Task>, on_successful_submit: EventHandler<()
id: "input_project", id: "input_project",
option { option {
value: 0, value: 0,
"None" {translate!(i18, "none")}
}, },
for project in projects { for project in projects {
option { option {
@ -330,13 +335,14 @@ pub(crate) fn TaskForm(task: Option<Task>, on_successful_submit: EventHandler<()
label { label {
r#for: "category_calendar_reminder_offset_index", r#for: "category_calendar_reminder_offset_index",
class: "pr-3 min-w-16 text-right", class: "pr-3 min-w-16 text-right",
{REMINDER_OFFSETS[category_calendar_reminder_offset_index()].map( {REMINDER_OFFSETS[category_calendar_reminder_offset_index()]
|offset| if offset.num_hours() < 1 { .map(
format!("{} min", offset.num_minutes()) |offset| if offset.num_hours() < 1 {
} else { format!("{} min", offset.num_minutes())
format!("{} h", offset.num_hours()) } else {
} format!("{} h", offset.num_hours())
).unwrap_or_else(|| "none".to_string())} }
).unwrap_or_else(|| translate!(i18, "none"))}
} }
} }
} }

@ -1,12 +1,18 @@
use chrono::{Datelike, Local}; use crate::internationalization::LocaleFromLanguageIdentifier;
use crate::models::category::Category; use crate::models::category::Category;
use crate::models::task::TaskWithSubtasks; use crate::models::task::TaskWithSubtasks;
use chrono::{Datelike, Local};
use dioxus::core_macro::rsx; use dioxus::core_macro::rsx;
use dioxus::dioxus_core::Element; use dioxus::dioxus_core::Element;
use dioxus::prelude::*; use dioxus::prelude::*;
use dioxus_sdk::i18n::use_i18;
use dioxus_sdk::translate;
use voca_rs::Voca;
#[component] #[component]
pub(crate) fn TaskListItem(task: TaskWithSubtasks) -> Element { pub(crate) fn TaskListItem(task: TaskWithSubtasks) -> Element {
let i18 = use_i18();
rsx! { rsx! {
div { div {
class: "flex flex-col", class: "flex flex-col",
@ -22,11 +28,47 @@ pub(crate) fn TaskListItem(task: TaskWithSubtasks) -> Element {
i { i {
class: "fa-solid fa-bomb" class: "fa-solid fa-bomb"
}, },
{deadline.format(if deadline.year() == Local::now().year() { {
" %m. %-d." let today_date = Local::now().date_naive();
} else { format!(
" %m. %-d. %Y" " {}",
}).to_string()} if deadline == today_date - chrono::Days::new(1) {
translate!(i18, "yesterday")
} else if deadline == today_date {
translate!(i18, "today")
} else if deadline == today_date + chrono::Days::new(1) {
translate!(i18, "tomorrow")
} else if deadline > today_date
&& deadline <= today_date + chrono::Days::new(7) {
let deadline = deadline.format_localized(
"%A",
LocaleFromLanguageIdentifier::from(
&(i18.selected_language)()
).into()
).to_string();
if translate!(i18, "formats.weekday_lowercase_first")
.parse().unwrap() {
deadline._lower_first()
} else {
deadline
}
} else {
let format = translate!(i18,
if deadline.year() == today_date.year() {
"formats.date_format"
} else {
"formats.date_year_format"
}
);
deadline.format_localized(
format.as_str(),
LocaleFromLanguageIdentifier::from(
&(i18.selected_language)()
).into()
).to_string()
}
)
}
} }
} }
if let Category::Calendar { time, .. } = task.task().category() { if let Category::Calendar { time, .. } = task.task().category() {
@ -36,7 +78,10 @@ pub(crate) fn TaskListItem(task: TaskWithSubtasks) -> Element {
i { i {
class: "fa-solid fa-clock" class: "fa-solid fa-clock"
}, },
{calendar_time.time().format(" %k:%M").to_string()} {
let format = translate!(i18, "formats.time_format");
format!(" {}",calendar_time.time().format(format.as_str()))
}
} }
} }
} }

@ -0,0 +1,19 @@
{
"id": "cs-CZ",
"texts": {
"none": "žádný",
"long_term": "dlouhodobé",
"yesterday": "včera",
"today": "dnes",
"tomorrow": "zítra",
"overdue": "zpožděné",
"formats": {
"date_format": "%-d. %B",
"date_year_format": "%-d. %B %Y",
"date_weekday_format": "%A %-d. %B",
"date_weekday_year_format": "%A %-d. %B %Y",
"weekday_lowercase_first": "true",
"time_format": "%-H:%M"
}
}
}

@ -0,0 +1,19 @@
{
"id": "en-US",
"texts": {
"none": "none",
"long_term": "long-term",
"yesterday": "yesterday",
"today": "today",
"tomorrow": "tomorrow",
"overdue": "overdue",
"formats": {
"date_format": "%B %-d",
"date_year_format": "%B %-d, %Y",
"date_weekday_format": "%A, %B %-d",
"date_weekday_year_format": "%A, %B %-d, %Y",
"weekday_lowercase_first": "false",
"time_format": "%-I:%M %P"
}
}
}

@ -0,0 +1,34 @@
use std::ops::Deref;
use std::str::FromStr;
use chrono::Locale;
use dioxus_sdk::i18n::Language;
use unic_langid_impl::LanguageIdentifier;
const EN_US: &str = include_str!("en_us.json");
const CS_CZ: &str = include_str!("cs_cz.json");
pub(crate) fn get_languages() -> Vec<Language> {
Vec::from([EN_US, CS_CZ]).into_iter().map(|texts| Language::from_str(texts).unwrap()).collect()
}
pub(crate) struct LocaleFromLanguageIdentifier<'a>(&'a LanguageIdentifier);
impl<'a> Deref for LocaleFromLanguageIdentifier<'a> {
type Target = LanguageIdentifier;
fn deref(&self) -> &Self::Target {
self.0
}
}
impl<'a> From<LocaleFromLanguageIdentifier<'a>> for Locale {
fn from(language_identifier: LocaleFromLanguageIdentifier) -> Self {
language_identifier.to_string().replace("-", "_").parse().unwrap()
}
}
impl<'a> From<&'a LanguageIdentifier> for LocaleFromLanguageIdentifier<'a> {
fn from(language_identifier: &'a LanguageIdentifier) -> Self {
LocaleFromLanguageIdentifier(language_identifier)
}
}

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

@ -73,7 +73,7 @@ impl Ord for Task {
.then(ReverseOrdOption::from( .then(ReverseOrdOption::from(
&self_time.as_ref().map(|calendar_time| calendar_time.time()) &self_time.as_ref().map(|calendar_time| calendar_time.time())
).cmp(&ReverseOrdOption::from( ).cmp(&ReverseOrdOption::from(
&other_time.as_ref().map(|calendar_time| calendar_time.time()) &other_time.as_ref().map(|calendar_time| calendar_time.time())
))) )))
.then(ReverseOrdOption::from(&self.deadline()).cmp( .then(ReverseOrdOption::from(&self.deadline()).cmp(
&ReverseOrdOption::from(&other.deadline()) &ReverseOrdOption::from(&other.deadline())

@ -4,9 +4,9 @@ use dotenvy::dotenv;
use std::env; use std::env;
pub(crate) fn establish_database_connection() -> ConnectionResult<PgConnection> { pub(crate) fn establish_database_connection() -> ConnectionResult<PgConnection> {
dotenv().ok(); dotenv().expect("Could not load environment variables.");
let database_url = let database_url =
env::var("DATABASE_URL").expect("The environment variable DATABASE_URL must be set."); env::var("DATABASE_URL").expect("The environment variable DATABASE_URL has to be set.");
PgConnection::establish(&database_url) PgConnection::establish(&database_url)
} }

@ -0,0 +1,14 @@
use std::env;
use dioxus::prelude::ServerFnError;
use unic_langid_impl::LanguageIdentifier;
use dioxus::prelude::*;
use dotenvy::dotenv;
#[server]
pub(crate) async fn get_language_identifier() -> Result<LanguageIdentifier, ServerFnError> {
dotenv().expect("Could not load environment variables from the .env file.");
Ok(env::var("LANGUAGE_CODE")
.expect("The environment variable LANGUAGE_CODE must be set.")
.parse::<LanguageIdentifier>()?)
}

@ -2,3 +2,4 @@ mod database_connection;
pub(crate) mod projects; pub(crate) mod projects;
pub(crate) mod tasks; pub(crate) mod tasks;
pub(crate) mod subtasks; pub(crate) mod subtasks;
pub(crate) mod internationalization;

@ -1,10 +1,18 @@
use std::cmp::Ordering; 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 /* 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)] #[derive(PartialEq)]
pub(crate) struct ReverseOrdOption<'a, T>(&'a Option<T>); pub(crate) struct ReverseOrdOption<'a, T>(&'a Option<T>);
impl<'a, T> Deref for ReverseOrdOption<'a, T> {
type Target = Option<T>;
fn deref(&self) -> &Self::Target {
self.0
}
}
impl<'a, T: Ord> Eq for ReverseOrdOption<'a, T> {} impl<'a, T: Ord> Eq for ReverseOrdOption<'a, T> {}
impl<'a, T: Ord> PartialOrd<Self> for ReverseOrdOption<'a, T> { impl<'a, T: Ord> PartialOrd<Self> for ReverseOrdOption<'a, T> {
@ -15,7 +23,7 @@ impl<'a, T: Ord> PartialOrd<Self> for ReverseOrdOption<'a, T> {
impl<'a, T: Ord> Ord for ReverseOrdOption<'a, T> { impl<'a, T: Ord> Ord for ReverseOrdOption<'a, T> {
fn cmp(&self, other: &Self) -> Ordering { fn cmp(&self, other: &Self) -> Ordering {
match (self.0.as_ref(), other.0.as_ref()) { match (self.as_ref(), other.as_ref()) {
(None, None) => Ordering::Equal, (None, None) => Ordering::Equal,
(None, Some(_)) => Ordering::Greater, (None, Some(_)) => Ordering::Greater,
(Some(_), None) => Ordering::Less, (Some(_), None) => Ordering::Less,