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}