numeric_sort/
lib.rs

1#![doc = include_str!("../README.md")]
2#![forbid(unsafe_code)]
3#![no_std]
4
5#[cfg(any(test, feature = "alloc"))]
6extern crate alloc;
7
8#[cfg(test)]
9use alloc::format;
10
11use core::cmp::{PartialOrd, Ord, PartialEq, Eq, Ordering};
12use core::iter::FusedIterator;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
15enum Sign {
16    Negative, Positive,
17}
18
19/// A reference to a substring of one or more non- (decimal) digits.
20/// 
21/// [`Ord`] and [`Eq`] are implemented for this type, which is equivalent to comparing the raw [`str`] values.
22#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq)]
23pub struct Text<'a> {
24    content: &'a str,
25}
26impl<'a> Text<'a> {
27    /// Greedily reads a (non-empty) [`Text`] segment from the beginning of the string.
28    /// Returns [`None`] if the string is empty or starts with a (decimal) digit.
29    pub fn read(src: &'a str) -> Option<Self> {
30        if Number::read(src).is_some() { return None; }
31
32        let mut pos = src.char_indices();
33        loop {
34            match pos.next() {
35                None => return if !src.is_empty() { Some(Self { content: src }) } else { None },
36                Some((i, ch)) => {
37                    if (ch.is_ascii_whitespace() || ch.is_ascii_punctuation()) && Number::read(&src[i + 1..]).is_some() {
38                        return Some(Self { content: &src[..i + 1] });
39                    }
40                    if ch.is_digit(10) {
41                        return if i != 0 { Some(Self { content: &src[..i] }) } else { None };
42                    }
43                }
44            }
45        }
46    }
47    /// Returns the (non-empty) substring that was read via [`Text::read`].
48    pub fn as_str(&self) -> &'a str { self.content }
49}
50
51#[test]
52fn test_text() {
53    assert_eq!(Text::read("hello world ").map(|v| v.content), Some("hello world "));
54    assert_eq!(Text::read("hello world 7").map(|v| v.content), Some("hello world "));
55    assert_eq!(Text::read("hello world  7").map(|v| v.content), Some("hello world  "));
56    assert_eq!(Text::read("hello world -7").map(|v| v.content), Some("hello world "));
57    assert_eq!(Text::read("hello world  -7").map(|v| v.content), Some("hello world  "));
58    assert_eq!(Text::read("hello world-").map(|v| v.content), Some("hello world-"));
59    assert_eq!(Text::read("hello world-4").map(|v| v.content), Some("hello world-"));
60    assert_eq!(Text::read("hello world--4").map(|v| v.content), Some("hello world-"));
61    assert_eq!(Text::read("hello world---4").map(|v| v.content), Some("hello world--"));
62    assert_eq!(Text::read("hello world----4").map(|v| v.content), Some("hello world---"));
63    assert_eq!(Text::read("hello world").map(|v| v.content), Some("hello world"));
64    assert_eq!(Text::read("hello wor4ld").map(|v| v.content), Some("hello wor"));
65    assert_eq!(Text::read("안영하세요 wor4ld").map(|v| v.content), Some("안영하세요 wor"));
66    assert_eq!(Text::read("h2ell wor4ld").map(|v| v.content), Some("h"));
67    assert_eq!(Text::read("34hello wor4ld").map(|v| v.content), None);
68    assert_eq!(Text::read("-34hello wor4ld").map(|v| v.content), None);
69    assert_eq!(Text::read("--34hello wor4ld").map(|v| v.content), Some("-"));
70    assert_eq!(Text::read("+-34hello wor4ld").map(|v| v.content), Some("+"));
71    assert_eq!(Text::read("+34hello wor4ld").map(|v| v.content), None);
72    assert_eq!(Text::read("-+34hello wor4ld").map(|v| v.content), Some("-"));
73    assert_eq!(Text::read("++34hello wor4ld").map(|v| v.content), Some("+"));
74    assert_eq!(Text::read("").map(|v| v.content), None);
75
76    for p in "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~".chars() {
77        assert_eq!(Text::read(&format!("hello world{p}-4")).map(|v| v.content), Some(format!("hello world{p}").as_str()));
78    }
79
80    fn get(v: &str) -> &str { Text::read(v).unwrap().as_str() }
81    assert_eq!(get("hello world"), "hello world");
82    assert_eq!(get("hello wor4ld"), "hello wor");
83    assert_eq!(get("h2ell wor4ld"), "h");
84    assert_eq!(get(" h2ell wor4ld"), " h");
85    assert_eq!(get(" h 2ell wor4ld"), " h ");
86}
87
88/// A reference to a substring of one or more (decimal) digits.
89/// 
90/// [`Ord`] and [`Eq`] are implemented for this type, which behave as if using arbitrary-precision integers, but performs no allocations.
91/// Note that this means that leading zeros on a number will be ignored for the purpose of comparison.
92#[derive(Debug, Clone, Copy)]
93pub struct Number<'a> {
94    sign: Option<Sign>,
95    leading_zeros: usize,
96    content: &'a str,
97}
98impl<'a> Number<'a> {
99    /// Greedily reads a (non-empty) [`Number`] segment from the beginning of the string.
100    /// Returns [`None`] if the string does not start with a (decimal) digit.
101    pub fn read(src: &'a str) -> Option<Self> {
102        let (sign, tail) = match src.chars().next() {
103            Some('+') => (Some(Sign::Positive), &src[1..]),
104            Some('-') => (Some(Sign::Negative), &src[1..]),
105            _ => (None, src),
106        };
107
108        match tail.chars().position(|ch| !ch.is_digit(10)).unwrap_or(tail.len()) {
109            0 => None,
110            stop => {
111                let leading_zeros = tail.chars().position(|ch| ch != '0').unwrap_or(tail.len());
112                Some(Self { sign, leading_zeros, content: &src[..stop + (src.len() - tail.len())] }) // ascii digits are 1 byte in utf8, so this is safe (otherwise we'd need char_indices())
113            }
114        }
115    }
116    /// Returns the (non-empty) substring that was read via [`Number::read`].
117    pub fn as_str(&self) -> &'a str { self.content }
118}
119impl Ord for Number<'_> {
120    fn cmp(&self, other: &Self) -> Ordering {
121        let t = |x: &Self| (x.content.len() - if x.sign.is_some() { 1 } else { 0 } - x.leading_zeros, &x.content[if x.sign.is_some() { 1 } else { 0 } + x.leading_zeros..]);
122
123        match (self.sign.unwrap_or(Sign::Positive), other.sign.unwrap_or(Sign::Positive)) {
124            (Sign::Positive, Sign::Positive) => t(self).cmp(&t(other)),
125            (Sign::Positive, Sign::Negative) => if t(self).0 == 0 && t(other).0 == 0 { Ordering::Equal } else { Ordering::Greater },
126            (Sign::Negative, Sign::Positive) => if t(self).0 == 0 && t(other).0 == 0 { Ordering::Equal } else { Ordering::Less },
127            (Sign::Negative, Sign::Negative) => t(other).cmp(&t(self)),
128        }
129    }
130}
131impl PartialOrd for Number<'_> { fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) } }
132impl PartialEq for Number<'_> { fn eq(&self, other: &Self) -> bool { self.cmp(other) == Ordering::Equal } }
133impl Eq for Number<'_> {}
134
135#[test]
136fn test_number() {
137    assert_eq!(Number::read("0").map(|v| (v.content, v.leading_zeros)), Some(("0", 1)));
138    assert_eq!(Number::read("+0").map(|v| (v.content, v.leading_zeros)), Some(("+0", 1)));
139    assert_eq!(Number::read("-0").map(|v| (v.content, v.leading_zeros)), Some(("-0", 1)));
140    assert_eq!(Number::read("00").map(|v| (v.content, v.leading_zeros)), Some(("00", 2)));
141    assert_eq!(Number::read("+00").map(|v| (v.content, v.leading_zeros)), Some(("+00", 2)));
142    assert_eq!(Number::read("-00").map(|v| (v.content, v.leading_zeros)), Some(("-00", 2)));
143    assert_eq!(Number::read("53426").map(|v| (v.content, v.leading_zeros)), Some(("53426", 0)));
144    assert_eq!(Number::read("-53426").map(|v| (v.content, v.leading_zeros)), Some(("-53426", 0)));
145    assert_eq!(Number::read("-53426g").map(|v| (v.content, v.leading_zeros)), Some(("-53426", 0)));
146    assert_eq!(Number::read("-053426").map(|v| (v.content, v.leading_zeros)), Some(("-053426", 1)));
147    assert_eq!(Number::read("-053426f").map(|v| (v.content, v.leading_zeros)), Some(("-053426", 1)));
148    assert_eq!(Number::read("+53426").map(|v| (v.content, v.leading_zeros)), Some(("+53426", 0)));
149    assert_eq!(Number::read("+53426g").map(|v| (v.content, v.leading_zeros)), Some(("+53426", 0)));
150    assert_eq!(Number::read("+053426").map(|v| (v.content, v.leading_zeros)), Some(("+053426", 1)));
151    assert_eq!(Number::read("+053426f").map(|v| (v.content, v.leading_zeros)), Some(("+053426", 1)));
152    assert_eq!(Number::read("053426").map(|v| (v.content, v.leading_zeros)), Some(("053426", 1)));
153    assert_eq!(Number::read("00053426").map(|v| (v.content, v.leading_zeros)), Some(("00053426", 3)));
154    assert_eq!(Number::read("00053d426").map(|v| (v.content, v.leading_zeros)), Some(("00053", 3)));
155    assert_eq!(Number::read("000g53d426").map(|v| (v.content, v.leading_zeros)), Some(("000", 3)));
156    assert_eq!(Number::read("00g53d426").map(|v| (v.content, v.leading_zeros)), Some(("00", 2)));
157    assert_eq!(Number::read("g53d426").map(|v| (v.content, v.leading_zeros)), None);
158
159    assert_eq!(Number::read("2345").unwrap().cmp(&Number::read("2346").unwrap()), Ordering::Less);
160    assert_eq!(Number::read("-2345").unwrap().cmp(&Number::read("-2346").unwrap()), Ordering::Greater);
161    assert_eq!(Number::read("-2345").unwrap().cmp(&Number::read("-2345").unwrap()), Ordering::Equal);
162    assert_eq!(Number::read("-2345").unwrap().cmp(&Number::read("+2345").unwrap()), Ordering::Less);
163    assert_eq!(Number::read("-2345").unwrap().cmp(&Number::read("2345").unwrap()), Ordering::Less);
164    assert_eq!(Number::read("+2345").unwrap().cmp(&Number::read("-2345").unwrap()), Ordering::Greater);
165    assert_eq!(Number::read("2345").unwrap().cmp(&Number::read("-2345").unwrap()), Ordering::Greater);
166    assert_eq!(Number::read("+2345").unwrap().cmp(&Number::read("+2345").unwrap()), Ordering::Equal);
167    assert_eq!(Number::read("+2345").unwrap().cmp(&Number::read("2345").unwrap()), Ordering::Equal);
168    assert_eq!(Number::read("2345").unwrap().cmp(&Number::read("+2345").unwrap()), Ordering::Equal);
169    assert_eq!(Number::read("2345").unwrap().cmp(&Number::read("2345").unwrap()), Ordering::Equal);
170    assert_eq!(Number::read("2345").unwrap().cmp(&Number::read("0002345").unwrap()), Ordering::Equal);
171    assert_eq!(Number::read("234").unwrap().cmp(&Number::read("2345").unwrap()), Ordering::Less);
172    assert_eq!(Number::read("0").unwrap().cmp(&Number::read("0").unwrap()), Ordering::Equal);
173    assert_eq!(Number::read("0").unwrap().cmp(&Number::read("+0").unwrap()), Ordering::Equal);
174    assert_eq!(Number::read("0").unwrap().cmp(&Number::read("-0").unwrap()), Ordering::Equal);
175    assert_eq!(Number::read("+0").unwrap().cmp(&Number::read("0").unwrap()), Ordering::Equal);
176    assert_eq!(Number::read("+0").unwrap().cmp(&Number::read("+0").unwrap()), Ordering::Equal);
177    assert_eq!(Number::read("+0").unwrap().cmp(&Number::read("-0").unwrap()), Ordering::Equal);
178    assert_eq!(Number::read("-0").unwrap().cmp(&Number::read("0").unwrap()), Ordering::Equal);
179    assert_eq!(Number::read("-0").unwrap().cmp(&Number::read("+0").unwrap()), Ordering::Equal);
180    assert_eq!(Number::read("-0").unwrap().cmp(&Number::read("-0").unwrap()), Ordering::Equal);
181    assert_eq!(Number::read("000000234").unwrap().cmp(&Number::read("2345").unwrap()), Ordering::Less);
182    assert_eq!(Number::read("000000234").unwrap().cmp(&Number::read("236521548").unwrap()), Ordering::Less);
183    assert_eq!(Number::read("000000234").unwrap().cmp(&Number::read("000000236521548").unwrap()), Ordering::Less);
184    assert_eq!(Number::read("01000000").unwrap().cmp(&Number::read("000000236521548").unwrap()), Ordering::Less);
185    assert_eq!(Number::read("123").unwrap().cmp(&Number::read("101").unwrap()), Ordering::Greater);
186
187    assert_eq!(Number::read("2345").unwrap(), Number::read("2345").unwrap());
188    assert_eq!(Number::read("2345").unwrap(), Number::read("02345").unwrap());
189    assert_eq!(Number::read("002345").unwrap(), Number::read("2345").unwrap());
190
191    assert_eq!(Number::read(""), None);
192    assert_eq!(Number::read("help"), None);
193    assert_eq!(Number::read("-"), None);
194    assert_eq!(Number::read("+"), None);
195
196    fn get(v: &str) -> &str { Number::read(v).unwrap().as_str() }
197    assert_eq!(get("2345"), "2345");
198    assert_eq!(get("002345"), "002345");
199    assert_eq!(get("00000"), "00000");
200    assert_eq!(get("0"), "0");
201
202    for a in -128..=128 {
203        for b in -128..=128 {
204            assert_eq!(Number::read(&format!("{a}")).unwrap().cmp(&Number::read(&format!("{b}")).unwrap()), a.cmp(&b));
205            assert_eq!(Number::read(&format!("{a}")).unwrap().cmp(&Number::read(&format!("{b:+}")).unwrap()), a.cmp(&b));
206            assert_eq!(Number::read(&format!("{a:+}")).unwrap().cmp(&Number::read(&format!("{b}")).unwrap()), a.cmp(&b));
207            assert_eq!(Number::read(&format!("{a:+}")).unwrap().cmp(&Number::read(&format!("{b:+}")).unwrap()), a.cmp(&b));
208        }
209
210        assert_eq!(Number::read("0").unwrap().cmp(&Number::read(&format!("{a:+}")).unwrap()), 0.cmp(&a));
211        assert_eq!(Number::read("+0").unwrap().cmp(&Number::read(&format!("{a:+}")).unwrap()), 0.cmp(&a));
212        assert_eq!(Number::read("-0").unwrap().cmp(&Number::read(&format!("{a:+}")).unwrap()), 0.cmp(&a));
213
214        assert_eq!(Number::read(&format!("{a:+}")).unwrap().cmp(&Number::read("0").unwrap()), a.cmp(&0));
215        assert_eq!(Number::read(&format!("{a:+}")).unwrap().cmp(&Number::read("+0").unwrap()), a.cmp(&0));
216        assert_eq!(Number::read(&format!("{a:+}")).unwrap().cmp(&Number::read("-0").unwrap()), a.cmp(&0));
217    }
218}
219
220/// A reference to a homogenous segment of text in a string.
221/// 
222/// [`Ord`] and [`Eq`] are implemented for this type, which delegate to their respective types for same variant comparison,
223/// and otherwise considers every [`Segment::Number`] to come before every [`Segment::Text`].
224#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq)]
225pub enum Segment<'a> {
226    Number(Number<'a>),
227    Text(Text<'a>),
228}
229impl<'a> Segment<'a> {
230    /// Greedily reads a (non-empty) [`Text`] or [`Number`] segment from the beginning of the string.
231    /// Returns [`None`] if the string is empty.
232    pub fn read(src: &'a str) -> Option<Self> {
233        if let Some(x) = Number::read(src) { return Some(Segment::Number(x)) }
234        if let Some(x) = Text::read(src) { return Some(Segment::Text(x)) }
235        debug_assert_eq!(src, "");
236        None
237    }
238    /// Returns the (non-empty) substring that was read via [`Segment::read`].
239    pub fn as_str(&self) -> &'a str {
240        match self {
241            Segment::Text(x) => x.as_str(),
242            Segment::Number(x) => x.as_str(),
243        }
244    }
245    /// Retrieves the value of the [`Segment::Number`] variant, or [`None`] if that is not the current variant.
246    pub fn as_number(&self) -> Option<Number<'a>> {
247        if let Segment::Number(x) = self { Some(*x) } else { None }
248    }
249    /// Retrieves the value of the [`Segment::Text`] variant, or [`None`] if that is not the current variant.
250    pub fn as_text(&self) -> Option<Text<'a>> {
251        if let Segment::Text(x) = self { Some(*x) } else { None }
252    }
253}
254
255#[test]
256fn test_segment() {
257    assert_eq!(Segment::read("00453hello").unwrap().as_number().unwrap().as_str(), "00453");
258    assert_eq!(Segment::read("453hello").unwrap().as_number().unwrap().as_str(), "453");
259    assert_eq!(Segment::read("000hello").unwrap().as_number().unwrap().as_str(), "000");
260    assert_eq!(Segment::read("hello 453").unwrap().as_text().unwrap().as_str(), "hello ");
261    assert_eq!(Segment::read(" ").unwrap().as_text().unwrap().as_str(), " ");
262    assert_eq!(Segment::read(""), None);
263
264    fn get(v: &str) -> &str { Segment::read(v).unwrap().as_str() }
265    assert_eq!(get("hello 69"), "hello ");
266    assert_eq!(get("453 hello 69"), "453");
267    assert_eq!(get("00453 hello 69"), "00453");
268    assert_eq!(get("000"), "000");
269    assert_eq!(get("abc"), "abc");
270
271    assert_eq!(Segment::read("00000").unwrap().cmp(&Segment::read("aaaaa").unwrap()), Ordering::Less);
272    assert_eq!(Segment::read("aaaaa").unwrap().cmp(&Segment::read("00000").unwrap()), Ordering::Greater);
273    assert_eq!(Segment::read("0000").unwrap().cmp(&Segment::read("aaaaa").unwrap()), Ordering::Less);
274    assert_eq!(Segment::read("aaaa").unwrap().cmp(&Segment::read("00000").unwrap()), Ordering::Greater);
275    assert_eq!(Segment::read("00000").unwrap().cmp(&Segment::read("aaaa").unwrap()), Ordering::Less);
276    assert_eq!(Segment::read("aaaaa").unwrap().cmp(&Segment::read("0000").unwrap()), Ordering::Greater);
277}
278
279/// An iterator over the [`Segment`] values within a string.
280/// 
281/// This is a potentially-empty sequence of alternating [`Segment::Number`] and [`Segment::Text`] values (not necessarily in that order).
282#[derive(Clone, Copy)]
283pub struct SegmentIter<'a>(&'a str);
284impl<'a> SegmentIter<'a> {
285    /// Constructs a new [`SegmentIter`] that iterates over the segments of the string.
286    /// If the string is empty, the resulting iterator is likewise an empty sequence.
287    pub fn new(src: &'a str) -> Self {
288        Self(src)
289    }
290    /// Returns the remaining portion of the original string that has not yet been iterated.
291    /// Returns an empty string if the iterator has been exhausted.
292    pub fn as_str(&self) -> &'a str {
293        self.0
294    }
295}
296impl<'a> Iterator for SegmentIter<'a> {
297    type Item = Segment<'a>;
298    fn next(&mut self) -> Option<Self::Item> {
299        let res = Segment::read(self.0)?;
300        self.0 = &self.0[res.as_str().len()..];
301        Some(res)
302    }
303}
304impl FusedIterator for SegmentIter<'_> {}
305
306#[test]
307fn test_segment_iter() {
308    let mut seq = SegmentIter::new("34543hello this is 00343 2 a test");
309    assert_eq!(seq.as_str(), "34543hello this is 00343 2 a test");
310    assert_eq!(seq.next().unwrap().as_number().unwrap().as_str(), "34543");
311    assert_eq!(seq.as_str(), "hello this is 00343 2 a test");
312    assert_eq!(seq.next().unwrap().as_text().unwrap().as_str(), "hello this is ");
313    assert_eq!(seq.as_str(), "00343 2 a test");
314    assert_eq!(seq.next().unwrap().as_number().unwrap().as_str(), "00343");
315    assert_eq!(seq.as_str(), " 2 a test");
316    assert_eq!(seq.next().unwrap().as_text().unwrap().as_str(), " ");
317    assert_eq!(seq.as_str(), "2 a test");
318    assert_eq!(seq.next().unwrap().as_number().unwrap().as_str(), "2");
319    assert_eq!(seq.as_str(), " a test");
320    assert_eq!(seq.next().unwrap().as_text().unwrap().as_str(), " a test");
321    assert_eq!(seq.as_str(), "");
322    for _ in 0..16 {
323        assert!(seq.next().is_none());
324        assert_eq!(seq.as_str(), "");
325    }
326
327    fn get(v: &str) -> &str { SegmentIter::new(v).as_str() }
328    assert_eq!(get("hello world"), "hello world");
329}
330
331/// Performs a lexicographic comparison of the [`Segment`] sequences of two strings.
332/// 
333/// This has the effect of ordering the strings with respect to [`Number`] and [`Text`] substrings.
334/// 
335/// ```
336/// # use numeric_sort::cmp;
337/// # use std::cmp::Ordering;
338/// assert_eq!(cmp("apple", "cable"), Ordering::Less);
339/// assert_eq!(cmp("32454", "hello"), Ordering::Less);
340/// assert_eq!(cmp("file-10", "file-3"), Ordering::Greater);
341/// assert_eq!(cmp("test-v1.10.25", "test-v1.9.2"), Ordering::Greater);
342/// assert_eq!(cmp("agent-007", "agent-7"), Ordering::Equal);
343/// ```
344pub fn cmp(a: &str, b: &str) -> Ordering {
345    SegmentIter::new(a).cmp(SegmentIter::new(b))
346}
347
348#[test]
349fn test_cmp() {
350    assert_eq!(cmp("hello-456", "hello-0999"), Ordering::Less);
351    assert_eq!(cmp("hellos-456", "hello-0999"), Ordering::Greater);
352    assert_eq!(cmp("hello--456", "hello-0999"), Ordering::Less);
353    assert_eq!(cmp("v1.4.12.3", "v1.4.4.3"), Ordering::Greater);
354    assert_eq!(cmp("val[-1]", "val[0]"), Ordering::Less);
355    assert_eq!(cmp("val[-1]", "val[2]"), Ordering::Less);
356    assert_eq!(cmp("val -1", "val 0"), Ordering::Less);
357    assert_eq!(cmp("val -1", "val 2"), Ordering::Less);
358}
359
360/// Sorts an array via the [`cmp`] ordering.
361/// 
362/// Because this function performs a stable sort, it must be allocating and so is only enabled with the `alloc` (default) feature.
363/// If `alloc` is not enabled or you do not require a stable sort, you may instead consider using [`sort_unstable`].
364/// 
365/// ```
366/// # use numeric_sort::sort;
367/// let mut arr = ["file-1", "file-10", "file-2"];
368/// sort(&mut arr);
369/// assert_eq!(&arr, &["file-1", "file-2", "file-10"]);
370/// ```
371#[cfg(feature = "alloc")]
372pub fn sort<T: AsRef<str>>(arr: &mut [T]) {
373    arr.sort_by(|a, b| cmp(a.as_ref(), b.as_ref())) // [T]::sort_by is stable and so requires alloc
374}
375
376/// Equivalent to [`sort`], but performs an unstable sort.
377/// 
378/// Because this function works in-place, it is available even when the default `alloc` feature is disabled.
379pub fn sort_unstable<T: AsRef<str>>(arr: &mut [T]) {
380    arr.sort_unstable_by(|a, b| cmp(a.as_ref(), b.as_ref()))
381}
382
383#[test]
384#[cfg(feature = "alloc")]
385fn test_sort() {
386    use alloc::borrow::ToOwned;
387
388    macro_rules! sorted { ($in:expr) => {{ let mut v = $in; sort(&mut v); v }} }
389    assert_eq!(&sorted!(["file-1", "file-10", "file-2"]), &["file-1", "file-2", "file-10"]);
390    assert_eq!(&sorted!(["file-1".to_owned(), "file-10".to_owned(), "file-2".to_owned()]), &["file-1", "file-2", "file-10"]);
391
392    macro_rules! sorted_unstable { ($in:expr) => {{ let mut v = $in; sort_unstable(&mut v); v }} }
393    assert_eq!(&sorted_unstable!(["file-1", "file-10", "file-2"]), &["file-1", "file-2", "file-10"]);
394    assert_eq!(&sorted_unstable!(["file-1".to_owned(), "file-10".to_owned(), "file-2".to_owned()]), &["file-1", "file-2", "file-10"]);
395}
396
397#[test]
398fn test_sort_unstable() {
399    macro_rules! sorted_unstable { ($in:expr) => {{ let mut v = $in; sort_unstable(&mut v); v }} }
400    assert_eq!(&sorted_unstable!(["file-1", "file-10", "file-2"]), &["file-1", "file-2", "file-10"]);
401}