Skip to main content

qubit_function/comparator/
box_comparator.rs

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