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