Skip to main content

qubit_function/comparator/
box_comparator.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026.
4 *    Haixing Hu, Qubit Co. Ltd.
5 *
6 *    All rights reserved.
7 *
8 ******************************************************************************/
9//! Defines the `BoxComparator` public type.
10
11#![allow(unused_imports)]
12
13use super::*;
14
15/// A boxed comparator with single ownership.
16///
17/// `BoxComparator` wraps a comparator function in a `Box`, providing single
18/// ownership semantics. It is not cloneable and consumes `self` in
19/// composition operations.
20///
21/// # Type Parameters
22///
23/// * `T` - The type of values being compared
24///
25/// # Examples
26///
27/// ```rust
28/// use qubit_function::comparator::{Comparator, BoxComparator};
29/// use std::cmp::Ordering;
30///
31/// let cmp = BoxComparator::new(|a: &i32, b: &i32| a.cmp(b));
32/// assert_eq!(cmp.compare(&5, &3), Ordering::Greater);
33/// ```
34///
35/// # Author
36///
37/// Haixing Hu
38pub struct BoxComparator<T> {
39    pub(super) function: Box<dyn Fn(&T, &T) -> Ordering>,
40}
41
42impl<T> BoxComparator<T> {
43    /// Creates a new `BoxComparator` from a closure.
44    ///
45    /// # Parameters
46    ///
47    /// * `f` - The closure to wrap
48    ///
49    /// # Returns
50    ///
51    /// A new `BoxComparator` instance.
52    ///
53    /// # Examples
54    ///
55    /// ```rust
56    /// use qubit_function::comparator::BoxComparator;
57    ///
58    /// let cmp = BoxComparator::new(|a: &i32, b: &i32| a.cmp(b));
59    /// ```
60    #[inline]
61    pub fn new<F>(f: F) -> Self
62    where
63        F: Fn(&T, &T) -> Ordering + 'static,
64    {
65        Self {
66            function: Box::new(f),
67        }
68    }
69
70    /// Returns a comparator that imposes the reverse ordering.
71    ///
72    /// # Returns
73    ///
74    /// A new `BoxComparator` that reverses the comparison order.
75    ///
76    /// # Examples
77    ///
78    /// ```rust
79    /// use qubit_function::comparator::{Comparator, BoxComparator};
80    /// use std::cmp::Ordering;
81    ///
82    /// let cmp = BoxComparator::new(|a: &i32, b: &i32| a.cmp(b));
83    /// let rev = cmp.reversed();
84    /// assert_eq!(rev.compare(&5, &3), Ordering::Less);
85    /// ```
86    #[inline]
87    pub fn reversed(self) -> Self
88    where
89        T: 'static,
90    {
91        BoxComparator::new(move |a, b| (self.function)(b, a))
92    }
93
94    /// Returns a comparator that uses this comparator first, then another
95    /// comparator if this one considers the values equal.
96    ///
97    /// # Parameters
98    ///
99    /// * `other` - The comparator to use for tie-breaking. **Note: This
100    ///   parameter is passed by value and will transfer ownership.** If you
101    ///   need to preserve the original comparator, clone it first (if it
102    ///   implements `Clone`). Can be:
103    ///   - A `BoxComparator<T>`
104    ///   - An `RcComparator<T>`
105    ///   - An `ArcComparator<T>`
106    ///   - Any type implementing `Comparator<T>`
107    ///
108    /// # Returns
109    ///
110    /// A new `BoxComparator` that chains this comparator with another.
111    ///
112    /// # Examples
113    ///
114    /// ```rust
115    /// use qubit_function::comparator::{Comparator, BoxComparator};
116    /// use std::cmp::Ordering;
117    ///
118    /// #[derive(Debug)]
119    /// struct Person {
120    ///     name: String,
121    ///     age: i32,
122    /// }
123    ///
124    /// let by_name = BoxComparator::new(|a: &Person, b: &Person| {
125    ///     a.name.cmp(&b.name)
126    /// });
127    /// let by_age = BoxComparator::new(|a: &Person, b: &Person| {
128    ///     a.age.cmp(&b.age)
129    /// });
130    ///
131    /// // by_age is moved here
132    /// let cmp = by_name.then_comparing(by_age);
133    ///
134    /// let p1 = Person { name: "Alice".to_string(), age: 30 };
135    /// let p2 = Person { name: "Alice".to_string(), age: 25 };
136    /// assert_eq!(cmp.compare(&p1, &p2), Ordering::Greater);
137    /// // by_age.compare(&p1, &p2); // Would not compile - moved
138    /// ```
139    #[inline]
140    pub fn then_comparing(self, other: Self) -> Self
141    where
142        T: 'static,
143    {
144        BoxComparator::new(move |a, b| match (self.function)(a, b) {
145            Ordering::Equal => (other.function)(a, b),
146            ord => ord,
147        })
148    }
149
150    /// Returns a comparator that compares values by a key extracted by the
151    /// given function.
152    ///
153    /// # Parameters
154    ///
155    /// * `key_fn` - A function that extracts a comparable key from values
156    ///
157    /// # Returns
158    ///
159    /// A new `BoxComparator` that compares by the extracted key.
160    ///
161    /// # Examples
162    ///
163    /// ```rust
164    /// use qubit_function::comparator::{Comparator, BoxComparator};
165    /// use std::cmp::Ordering;
166    ///
167    /// #[derive(Debug)]
168    /// struct Person {
169    ///     name: String,
170    ///     age: i32,
171    /// }
172    ///
173    /// let by_age = BoxComparator::comparing(|p: &Person| &p.age);
174    /// let p1 = Person { name: "Alice".to_string(), age: 30 };
175    /// let p2 = Person { name: "Bob".to_string(), age: 25 };
176    /// assert_eq!(by_age.compare(&p1, &p2), Ordering::Greater);
177    /// ```
178    #[inline]
179    pub fn comparing<K, F>(key_fn: F) -> Self
180    where
181        K: Ord,
182        F: Fn(&T) -> &K + 'static,
183    {
184        BoxComparator::new(move |a: &T, b: &T| key_fn(a).cmp(key_fn(b)))
185    }
186
187    /// Converts this comparator into a closure.
188    ///
189    /// # Returns
190    ///
191    /// A closure that implements `Fn(&T, &T) -> Ordering`.
192    ///
193    /// # Examples
194    ///
195    /// ```rust
196    /// use qubit_function::comparator::{Comparator, BoxComparator};
197    /// use std::cmp::Ordering;
198    ///
199    /// let cmp = BoxComparator::new(|a: &i32, b: &i32| a.cmp(b));
200    /// let func = cmp.into_fn();
201    /// assert_eq!(func(&5, &3), Ordering::Greater);
202    /// ```
203    #[inline]
204    pub fn into_fn(self) -> impl Fn(&T, &T) -> Ordering {
205        move |a: &T, b: &T| (self.function)(a, b)
206    }
207}
208
209impl<T> Comparator<T> for BoxComparator<T> {
210    #[inline]
211    fn compare(&self, a: &T, b: &T) -> Ordering {
212        (self.function)(a, b)
213    }
214}