Skip to content

Commit

Permalink
Implement serde::Serialize and serde::Deserialize for Ksuid structs. (#4
Browse files Browse the repository at this point in the history
)

Motivation
1. As a rust-ksuid lib user I want to serialize to JSON structs that use Ksuid type
2. As a rust-ksuid lib user I want to deserialize from JSON to structs that use Ksuid type
  • Loading branch information
mrl5 committed Oct 11, 2022
1 parent c02aa06 commit 1646800
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 4 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ Cargo.lock
Session.vim

.env

*.swp
10 changes: 8 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,18 @@ categories = ["data-structures", "concurrency", "database", "encoding"]
[badges]
maintenance = {status="actively-developed"}

[features]
# Include nothing by default
default = []
serde = ["dep:serde"]

[dependencies]
base-encode = "^0.3.1"
byteorder = "^1.4.3"
getrandom = "0.2.4"
time = "0.3.7"
serde = { version = "^1.0.145", optional = true, features = ["derive"] }

[dev-dependencies]
serde = { version = "^1.0.133", features = ["derive"] }
serde_json = "^1.0.74"
serde = { version = "^1.0.145", features = ["derive"] }
serde_json = "^1.0.85"
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Add the dependency:

```toml
[dependencies]
svix-ksuid = "^0.5.0"
svix-ksuid = "^0.6.0"
```

```rust
Expand All @@ -62,6 +62,16 @@ let ksuid = KsuidMs::new(None, None);

And they both implement the same `KsuidLike` trait.

### Opt-in features
* `serde` - adds the ability to serialize and deserialize `Ksuid` and `KsuidMs`
using serde.

Make sure to enable like this:
```toml
[dependencies]
svix-ksuid = { version = "^0.6.0", features = ["serde"] }
```

## Examples

### Converting Ksuids
Expand Down
101 changes: 100 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
//!
//! ```toml
//! [dependencies]
//! svix-ksuid = "^0.5.0"
//! svix-ksuid = "^0.6.0"
//! ```
//!
//! ```
Expand All @@ -43,6 +43,16 @@
//!
//! And they both implement the same `KsuidLike` trait.
//!
//! ### Opt-in features
//! * `serde` - adds the ability to serialize and deserialize `Ksuid` and `KsuidMs`
//! using serde.
//!
//! Make sure to enable like this:
//! ```toml
//! [dependencies]
//! svix-ksuid = { version = "^0.6.0", features = ["serde"] }
//! ```
//!
//! ### License
//!
//! ksuid source code is available under an MIT [License](./LICENSE).
Expand All @@ -55,6 +65,11 @@ use std::{error, str::FromStr};
use byteorder::{BigEndian, ByteOrder};
use time::OffsetDateTime;

#[cfg(feature = "serde")]
use serde::de::{self, Deserialize, Deserializer, Visitor};
#[cfg(feature = "serde")]
use serde::ser::{Serialize, Serializer};

pub const KSUID_EPOCH: i64 = 1_400_000_000;

const BASE_62_CHARS: &[u8; 62] = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
Expand Down Expand Up @@ -477,3 +492,87 @@ impl fmt::Display for KsuidMs {
write!(f, "{}", self.to_base62())
}
}

#[cfg(feature = "serde")]
impl Serialize for Ksuid {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.to_base62())
}
}

#[cfg(feature = "serde")]
struct KsuidVisitor;

#[cfg(feature = "serde")]
impl<'de> Visitor<'de> for KsuidVisitor {
type Value = Ksuid;

fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a valid ksuid in base62")
}

fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
match Ksuid::from_base62(value) {
Ok(k) => Ok(k),
Err(e) => Err(E::custom(format!("{}", e))),
}
}
}

#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for Ksuid {
fn deserialize<D>(deserializer: D) -> Result<Ksuid, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_str(KsuidVisitor)
}
}

#[cfg(feature = "serde")]
impl Serialize for KsuidMs {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(self.to_base62().as_str())
}
}

#[cfg(feature = "serde")]
struct KsuidMsVisitor;

#[cfg(feature = "serde")]
impl<'de> Visitor<'de> for KsuidMsVisitor {
type Value = KsuidMs;

fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a base62 &str")
}

fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
match KsuidMs::from_base62(value) {
Ok(k) => Ok(k),
Err(e) => Err(E::custom(format!("{}", e))),
}
}
}

#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for KsuidMs {
fn deserialize<D>(deserializer: D) -> Result<KsuidMs, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_str(KsuidMsVisitor)
}
}
49 changes: 49 additions & 0 deletions tests/integration.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use serde::Deserialize;
#[cfg(feature = "serde")]
use serde::Serialize;
use std::{
fs::File,
io::{self, BufRead},
Expand Down Expand Up @@ -100,3 +102,50 @@ fn test_ordering() -> Result<(), String> {
assert!(ksuid2 >= ksuid1);
Ok(())
}

#[cfg(feature = "serde")]
#[derive(Serialize, Deserialize)]
struct TestKsuid {
id: Ksuid,
}

#[cfg(feature = "serde")]
#[derive(Serialize, Deserialize)]
struct TestKsuidMs {
id: KsuidMs,
}

#[cfg(feature = "serde")]
#[test]
fn test_serialize_to_base62() {
let b62 = "1srOrx2ZWZBpBUvZwXKQmoEYga2";
let json = r#"{"id":"1srOrx2ZWZBpBUvZwXKQmoEYga2"}"#;
let ksuid_obj = TestKsuid {
id: Ksuid::from_base62(b62).unwrap(),
};
let ksuidms_obj = TestKsuidMs {
id: KsuidMs::from_base62(b62).unwrap(),
};

let test_cases = vec![
serde_json::to_string(&ksuid_obj),
serde_json::to_string(&ksuidms_obj),
];
for serialized in test_cases {
assert!(serialized.is_ok());
let serialized = serialized.unwrap();
assert_eq!(serialized, json);
}
}

#[cfg(feature = "serde")]
#[test]
fn test_deserialize_from_base62() {
let b62 = "1srOrx2ZWZBpBUvZwXKQmoEYga2";
let json = r#"{"id":"1srOrx2ZWZBpBUvZwXKQmoEYga2"}"#;

let ksuid_obj: TestKsuid = serde_json::from_str(json).unwrap();
let ksuidms_obj: TestKsuidMs = serde_json::from_str(json).unwrap();
assert_eq!(ksuid_obj.id.to_string(), b62);
assert_eq!(ksuidms_obj.id.to_string(), b62);
}

0 comments on commit 1646800

Please sign in to comment.