Repository: mitsuhiko/insta (Rust)

Issue:
"u128 is not supported"
## Description

While attempting to snapshot test a value of a type I don't fully control I bumped into this error:

```
thread 'tests::u128_max_snapshot' panicked at 'called `Result::unwrap()` on an `Err` value: Error("u128 is not supported")', /home/sage/.cargo/registry/src/github.com-1ecc6299db9ec823/insta-1.6.3/src/serialization.rs:98:55
```

## Reproduction steps

Observe that the first test below works without issue, whereas after removing the `ignore` the second causes the above error:

```rust
use insta::{assert_snapshot, assert_json_snapshot};

#[test]
fn u64_max_json_snapshot() {
    assert_json_snapshot!(u64::MAX);
}

#[test]
#[ignore = "u128 is not supported"]
fn u128_max_json_snapshot() {
   assert_json_snapshot!(u128::MAX);
}                                                                                                               
```

Additional helpful information:

- Version of insta: `1.6.3`
- Version of rustc: `rustc 1.50.0 (cb75ad5db 2021-02-10)`
- Operating system and version: `Pop!_OS 20.10`

## What did you expect

I expected JSON snapshots to work with values containing `u128`s. Admittedly, I may be missing some nuance here related to JSON, but this workaround suggests it's not an issue with `serde_json` preventing `u128` serialization.

```rust
#[test]                                                                                                                    
fn u128_max_snapshot() {                                                                                                   
    assert_snapshot!(serde_json::to_string_pretty(&u128::MAX).unwrap());                                                   
}                                                                                                                          
```


Discussion:
This is currently intentional. `u128` support would have to be implemented conditionally based on this: https://docs.serde.rs/serde/macro.serde_if_integer128.html
Ah, that makes a lot of sense. Thanks!

Would you be interested in a PR for this? I can spend some time on it this weekend.



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 63320eeb..c28f5d16 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
 
 All notable changes to insta and cargo-insta are documented here.
 
+## 1.7.0
+
+* Added support for u128/i128.  (#169)
+
 ## 1.6.3
 
 * Fix a bug with empty lines in inline snapshots.  (#166)
diff --git a/src/content.rs b/src/content.rs
index f0b12e6f..2d265de6 100644
--- a/src/content.rs
+++ b/src/content.rs
@@ -32,11 +32,13 @@ pub enum Content {
     U16(u16),
     U32(u32),
     U64(u64),
+    U128(u128),
 
     I8(i8),
     I16(i16),
     I32(i32),
     I64(i64),
+    I128(i128),
 
     F32(f32),
     F64(f64),
@@ -86,6 +88,8 @@ pub enum Key<'a> {
     U64(u64),
     I64(i64),
     F64(f64),
+    U128(u128),
+    I128(i128),
     Str(&'a str),
     Bytes(&'a [u8]),
     Other,
@@ -114,10 +118,12 @@ impl_from!(u8, U8);
 impl_from!(u16, U16);
 impl_from!(u32, U32);
 impl_from!(u64, U64);
+impl_from!(u128, U128);
 impl_from!(i8, I8);
 impl_from!(i16, I16);
 impl_from!(i32, I32);
 impl_from!(i64, I64);
+impl_from!(i128, I128);
 impl_from!(f32, F32);
 impl_from!(f64, F64);
 impl_from!(char, Char);
@@ -206,9 +212,11 @@ impl Content {
             Content::U16(val) => Key::U64(val.into()),
             Content::U32(val) => Key::U64(val.into()),
             Content::U64(val) => Key::U64(val),
+            Content::U128(val) => Key::U128(val),
             Content::I16(val) => Key::I64(val.into()),
             Content::I32(val) => Key::I64(val.into()),
             Content::I64(val) => Key::I64(val),
+            Content::I128(val) => Key::I128(val),
             Content::F32(val) => Key::F64(val.into()),
             Content::F64(val) => Key::F64(val),
             Content::String(ref val) => Key::Str(&val.as_str()),
@@ -232,14 +240,39 @@ impl Content {
             Content::U16(v) => Some(u64::from(v)),
             Content::U32(v) => Some(u64::from(v)),
             Content::U64(v) => Some(v),
+            Content::U128(v) => {
+                let rv = v as u64;
+                if rv as u128 == v {
+                    Some(rv)
+                } else {
+                    None
+                }
+            }
             Content::I8(v) if v >= 0 => Some(v as u64),
             Content::I16(v) if v >= 0 => Some(v as u64),
             Content::I32(v) if v >= 0 => Some(v as u64),
             Content::I64(v) if v >= 0 => Some(v as u64),
+            Content::I128(v) => {
+                let rv = v as u64;
+                if rv as i128 == v {
+                    Some(rv)
+                } else {
+                    None
+                }
+            }
             _ => None,
         }
     }
 
+    /// Returns the value as u128
+    pub fn as_u128(&self) -> Option<u128> {
+        match *self.resolve_inner() {
+            Content::U128(v) => Some(v),
+            Content::I128(v) if v >= 0 => Some(v as u128),
+            _ => self.as_u64().map(u128::from),
+        }
+    }
+
     /// Returns the value as i64
     pub fn as_i64(&self) -> Option<i64> {
         match *self.resolve_inner() {
@@ -254,14 +287,46 @@ impl Content {
                     None
                 }
             }
+            Content::U128(v) => {
+                let rv = v as i64;
+                if rv as u128 == v {
+                    Some(rv)
+                } else {
+                    None
+                }
+            }
             Content::I8(v) => Some(i64::from(v)),
             Content::I16(v) => Some(i64::from(v)),
             Content::I32(v) => Some(i64::from(v)),
             Content::I64(v) => Some(v),
+            Content::I128(v) => {
+                let rv = v as i64;
+                if rv as i128 == v {
+                    Some(rv)
+                } else {
+                    None
+                }
+            }
             _ => None,
         }
     }
 
+    /// Returns the value as i128
+    pub fn as_i128(&self) -> Option<i128> {
+        match *self.resolve_inner() {
+            Content::U128(v) => {
+                let rv = v as i128;
+                if rv as u128 == v {
+                    Some(rv)
+                } else {
+                    None
+                }
+            }
+            Content::I128(v) => Some(v),
+            _ => self.as_i64().map(i128::from),
+        }
+    }
+
     /// Returns the value as f64
     pub fn as_f64(&self) -> Option<f64> {
         match *self.resolve_inner() {
@@ -350,10 +415,12 @@ impl Serialize for Content {
             Content::U16(u) => serializer.serialize_u16(u),
             Content::U32(u) => serializer.serialize_u32(u),
             Content::U64(u) => serializer.serialize_u64(u),
+            Content::U128(u) => serializer.serialize_u128(u),
             Content::I8(i) => serializer.serialize_i8(i),
             Content::I16(i) => serializer.serialize_i16(i),
             Content::I32(i) => serializer.serialize_i32(i),
             Content::I64(i) => serializer.serialize_i64(i),
+            Content::I128(i) => serializer.serialize_i128(i),
             Content::F32(f) => serializer.serialize_f32(f),
             Content::F64(f) => serializer.serialize_f64(f),
             Content::Char(c) => serializer.serialize_char(c),
@@ -466,6 +533,10 @@ where
         Ok(Content::I64(v))
     }
 
+    fn serialize_i128(self, v: i128) -> Result<Content, E> {
+        Ok(Content::I128(v))
+    }
+
     fn serialize_u8(self, v: u8) -> Result<Content, E> {
         Ok(Content::U8(v))
     }
@@ -482,6 +553,10 @@ where
         Ok(Content::U64(v))
     }
 
+    fn serialize_u128(self, v: u128) -> Result<Content, E> {
+        Ok(Content::U128(v))
+    }
+
     fn serialize_f32(self, v: f32) -> Result<Content, E> {
         Ok(Content::F32(v))
     }


Test patch:
diff --git a/tests/test_basic.rs b/tests/test_basic.rs
index 8376e080..14c1759d 100644
--- a/tests/test_basic.rs
+++ b/tests/test_basic.rs
@@ -66,3 +66,9 @@ fn test_unnamed_display() {
     assert_display_snapshot!(td);
     assert_display_snapshot!("whatever");
 }
+
+#[test]
+fn test_u128_json() {
+    let x: u128 = 42;
+    assert_json_snapshot!(&x, @"42");
+}