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}