diff --git a/CHANGELOG.md b/CHANGELOG.md index f2ba60a..aec3b30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## unreleased +### Fixed + + - Fix `PartialEq` and `Ord` implementations. ([PR #36](https://github.com/teloxide/dptree/pull/36)) + - For `Type` they were inconsistent and are replaced by the standard derived implementations. + - Adding a length check when checking equality of `DependencyMap`. + ## 0.5.0 - 2025-06-19 ### Added diff --git a/src/di.rs b/src/di.rs index 5e63de7..0eab331 100644 --- a/src/di.rs +++ b/src/di.rs @@ -93,7 +93,7 @@ impl PartialEq for DependencyMap { fn eq(&self, other: &Self) -> bool { let keys1 = self.map.keys(); let keys2 = other.map.keys(); - keys1.zip(keys2).map(|(k1, k2)| k1 == k2).all(|x| x) + keys1.len() == keys2.len() && keys1.zip(keys2).map(|(k1, k2)| k1 == k2).all(|x| x) } } @@ -326,4 +326,18 @@ mod tests { assert_eq!(map.try_get(), Some(Arc::new(42i32))); assert_eq!(map.try_get::(), None); } + + #[test] + fn same_keys() { + let mut map_bool1 = DependencyMap::new(); + let mut map_bool2 = DependencyMap::new(); + let map_empty = DependencyMap::new(); + + map_bool1.insert(false); + map_bool2.insert(true); + + assert_eq!(map_bool1, map_bool2); + assert_ne!(map_bool1, map_empty); + assert_ne!(map_bool2, map_empty); + } } diff --git a/src/handler/core.rs b/src/handler/core.rs index faae09e..ec74358 100644 --- a/src/handler/core.rs +++ b/src/handler/core.rs @@ -7,7 +7,6 @@ use crate::{description, prelude::DependencyMap, HandlerDescription}; use std::{ any::TypeId, - cmp::Ordering, collections::{BTreeMap, BTreeSet}, fmt::Write, future::Future, @@ -102,14 +101,16 @@ pub enum HandlerSignature { /// A run-time representation of a type. Used only for run-time type inference /// and checking of handler chains. /// +/// Type name field placed before type identifier field so that the derived Ord +/// implementation sorts types alphabetically. /// See [`crate::type_check`]. -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct Type { - /// The unique type identifier. - pub id: TypeId, - /// The type name used for printing. pub name: &'static str, + + /// The unique type identifier. + pub id: TypeId, } impl Hash for Type { @@ -119,29 +120,6 @@ impl Hash for Type { } } -impl PartialEq for Type { - /// Equality is done by type identifiers (type names are ignored). - fn eq(&self, other: &Self) -> bool { - self.id == other.id - } -} - -impl Eq for Type {} - -impl PartialOrd for Type { - /// The partial order is done by type names for better diagnostics. - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for Type { - /// The total order is done by type names for better diagnostics. - fn cmp(&self, other: &Self) -> Ordering { - self.name.cmp(other.name) - } -} - type DynFn<'a, Output> = dyn Fn(DependencyMap, Cont<'a, Output>) -> HandlerResult<'a, Output> + Send + Sync + 'a; @@ -679,7 +657,7 @@ mod tests { handler::{endpoint, filter, filter_async}, }; - use std::{collections::HashSet, iter::FromIterator}; + use std::{any::Any, collections::HashSet, iter::FromIterator}; use maplit::{btreemap, btreeset, hashset}; @@ -1018,6 +996,34 @@ Make sure all the required values are provided to the handler. For more informat ); } + #[test] + fn type_eq_ord_consistent() { + #[derive(Clone)] + struct A; + + let ta1 = Type { id: A.type_id(), name: "A1" }; + let ta2 = Type { id: A.type_id(), name: "A2" }; + + assert!(!(ta1 == ta2)); + assert!(ta1 < ta2); + assert!(!(ta1 > ta2)); + } + + #[test] + fn type_btreeset_not_contains_duplicate_name() { + #[derive(Clone)] + struct A; + #[derive(Clone)] + struct B; + + let ta = Type { id: A.type_id(), name: "DuplicateName" }; + let tb = Type { id: B.type_id(), name: "DuplicateName" }; + let set = btreeset! {ta}; + + assert!(ta != tb); + assert!(!set.contains(&tb)); + } + #[tokio::test] async fn type_infer_check_chained_combinators() { #[derive(Clone)]