Skip to content

Commit 8893db7

Browse files
authored
Implement total ord for Key (#586)
Fixes potential crashes on Rust 1.81.
1 parent a4231cc commit 8893db7

File tree

2 files changed

+52
-5
lines changed

2 files changed

+52
-5
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ All notable changes to insta and cargo-insta are documented here.
3939

4040
- Enable Filters to be created from `IntoIterator` types, rather than just `Vec`s. #570
4141

42+
- Implemented total sort order for an internal `Key` type correctly. This prevents potential
43+
crashes introduced by the new sort algorithm in Rust 1.81. #586
44+
4245
## 1.39.0
4346

4447
- Fixed a bug in `require_full_match`. #485

insta/src/content/serialization.rs

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::content::Content;
55

66
use serde::{ser, Serialize, Serializer};
77

8-
#[derive(PartialEq, PartialOrd, Debug)]
8+
#[derive(PartialEq, Debug)]
99
pub enum Key<'a> {
1010
Bool(bool),
1111
U64(u64),
@@ -18,17 +18,61 @@ pub enum Key<'a> {
1818
Other,
1919
}
2020

21+
impl<'a> Key<'a> {
22+
/// Needed because std::mem::discriminant is not Ord
23+
fn discriminant(&self) -> usize {
24+
match self {
25+
Key::Bool(_) => 1,
26+
Key::U64(_) => 2,
27+
Key::I64(_) => 3,
28+
Key::F64(_) => 4,
29+
Key::U128(_) => 5,
30+
Key::I128(_) => 6,
31+
Key::Str(_) => 7,
32+
Key::Bytes(_) => 8,
33+
Key::Other => 9,
34+
}
35+
}
36+
}
37+
2138
impl<'a> Eq for Key<'a> {}
2239

23-
// We're making a deliberate choice to just "round down" here, so ignoring the
24-
// clippy lint
25-
#[allow(clippy::derive_ord_xor_partial_ord)]
2640
impl<'a> Ord for Key<'a> {
2741
fn cmp(&self, other: &Self) -> Ordering {
28-
self.partial_cmp(other).unwrap_or(Ordering::Less)
42+
let self_discriminant = self.discriminant();
43+
let other_discriminant = other.discriminant();
44+
match Ord::cmp(&self_discriminant, &other_discriminant) {
45+
Ordering::Equal => match (self, other) {
46+
(Key::Bool(a), Key::Bool(b)) => Ord::cmp(a, b),
47+
(Key::U64(a), Key::U64(b)) => Ord::cmp(a, b),
48+
(Key::I64(a), Key::I64(b)) => Ord::cmp(a, b),
49+
(Key::F64(a), Key::F64(b)) => f64_total_cmp(*a, *b),
50+
(Key::U128(a), Key::U128(b)) => Ord::cmp(a, b),
51+
(Key::I128(a), Key::I128(b)) => Ord::cmp(a, b),
52+
(Key::Str(a), Key::Str(b)) => Ord::cmp(a, b),
53+
(Key::Bytes(a), Key::Bytes(b)) => Ord::cmp(a, b),
54+
_ => Ordering::Equal,
55+
},
56+
cmp => cmp,
57+
}
2958
}
3059
}
3160

61+
impl<'a> PartialOrd for Key<'a> {
62+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
63+
Some(self.cmp(other))
64+
}
65+
}
66+
67+
fn f64_total_cmp(left: f64, right: f64) -> Ordering {
68+
// this is taken from f64::total_cmp on newer rust versions
69+
let mut left = left.to_bits() as i64;
70+
let mut right = right.to_bits() as i64;
71+
left ^= (((left >> 63) as u64) >> 1) as i64;
72+
right ^= (((right >> 63) as u64) >> 1) as i64;
73+
left.cmp(&right)
74+
}
75+
3276
impl Content {
3377
pub(crate) fn as_key(&self) -> Key<'_> {
3478
match *self.resolve_inner() {

0 commit comments

Comments
 (0)