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 { function: Box::new(f) }
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}