#[cfg(feature = "wasm")]
use tsify::Tsify;
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct SendPasswordCredentials {
pub password_hash_b64: String,
}
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct SendEmailCredentials {
pub email: String,
}
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct SendEmailOtpCredentials {
pub email: String,
pub otp: String,
}
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
#[serde(untagged)]
pub enum SendAccessCredentials {
#[allow(missing_docs)]
Password(SendPasswordCredentials),
#[allow(missing_docs)]
EmailOtp(SendEmailOtpCredentials),
#[allow(missing_docs)]
Email(SendEmailCredentials),
}
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct SendAccessTokenRequest {
pub send_id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[cfg_attr(feature = "wasm", tsify(optional))]
pub send_access_credentials: Option<SendAccessCredentials>,
}
#[cfg(test)]
mod tests {
use super::*;
mod send_access_token_request_tests {
use serde_json::{from_str, to_string};
use super::*;
#[test]
fn deserialize_camelcase_request() {
let json = r#"
{
"sendId": "abc123",
"sendAccessCredentials": { "passwordHashB64": "ha$h" }
}"#;
let req: SendAccessTokenRequest = from_str(json).unwrap();
assert_eq!(req.send_id, "abc123");
let creds = req.send_access_credentials.expect("expected Some");
match creds {
SendAccessCredentials::Password(p) => assert_eq!(p.password_hash_b64, "ha$h"),
_ => panic!("expected Password variant"),
}
}
#[test]
fn serialize_camelcase_request_with_credentials() {
let req = SendAccessTokenRequest {
send_id: "abc123".into(),
send_access_credentials: Some(SendAccessCredentials::Password(
SendPasswordCredentials {
password_hash_b64: "ha$h".into(),
},
)),
};
let json = to_string(&req).unwrap();
assert_eq!(
json,
r#"{"sendId":"abc123","sendAccessCredentials":{"passwordHashB64":"ha$h"}}"#
);
}
#[test]
fn serialize_omits_optional_credentials_when_none() {
let req = SendAccessTokenRequest {
send_id: "abc123".into(),
send_access_credentials: None,
};
let json = to_string(&req).unwrap();
assert_eq!(json, r#"{"sendId":"abc123"}"#);
}
#[test]
fn roundtrip_camel_in_to_camel_out() {
let in_json = r#"
{
"sendId": "abc123",
"sendAccessCredentials": { "passwordHashB64": "ha$h" }
}"#;
let req: SendAccessTokenRequest = from_str(in_json).unwrap();
let out_json = to_string(&req).unwrap();
assert_eq!(
out_json,
r#"{"sendId":"abc123","sendAccessCredentials":{"passwordHashB64":"ha$h"}}"#
);
}
#[test]
fn snakecase_top_level_keys_are_rejected() {
let json = r#"
{
"send_id": "abc123",
"sendAccessCredentials": { "passwordHashB64": "ha$h" }
}"#;
let err = from_str::<SendAccessTokenRequest>(json).unwrap_err();
let msg = err.to_string();
assert!(
msg.contains("unknown field") && msg.contains("send_id"),
"unexpected: {msg}"
);
}
#[test]
fn extra_top_level_key_is_rejected() {
let json = r#"
{
"sendId": "abc123",
"sendAccessCredentials": { "passwordHashB64": "ha$h" },
"extra": "nope"
}"#;
let err = from_str::<SendAccessTokenRequest>(json).unwrap_err();
let msg = err.to_string();
assert!(
msg.contains("unknown field") && msg.contains("extra"),
"unexpected: {msg}"
);
}
#[test]
fn snakecase_nested_keys_are_rejected() {
let json = r#"
{
"sendId": "abc123",
"sendAccessCredentials": { "password_hash_b64": "ha$h" }
}"#;
let err = serde_json::from_str::<SendAccessTokenRequest>(json).unwrap_err();
let msg = err.to_string();
assert!(
msg.contains("did not match any variant"),
"unexpected: {msg}"
);
}
#[test]
fn extra_nested_key_is_rejected() {
let json = r#"
{
"sendId": "abc123",
"sendAccessCredentials": {
"passwordHashB64": "ha$h",
"extra": "nope"
}
}"#;
let err = from_str::<SendAccessTokenRequest>(json).unwrap_err();
let msg = err.to_string();
assert!(
msg.contains("did not match any variant"),
"unexpected: {msg}"
);
}
}
mod send_access_credentials_tests {
use super::*;
mod send_access_password_credentials_tests {
use serde_json::{from_str, to_string};
use super::*;
#[test]
fn deserialize_struct_camelcase_from_ts() {
let json = r#"{ "passwordHashB64": "ha$h" }"#;
let s: SendPasswordCredentials = from_str(json).unwrap();
assert_eq!(s.password_hash_b64, "ha$h");
}
#[test]
fn serialize_struct_camelcase_to_wire() {
let s = SendPasswordCredentials {
password_hash_b64: "ha$h".into(),
};
let json = to_string(&s).unwrap();
assert_eq!(json, r#"{"passwordHashB64":"ha$h"}"#);
}
#[test]
fn roundtrip_struct_camel_in_to_camel_out() {
let in_json = r#"{ "passwordHashB64": "ha$h" }"#;
let parsed: SendPasswordCredentials = from_str(in_json).unwrap();
let out_json = to_string(&parsed).unwrap();
assert_eq!(out_json, r#"{"passwordHashB64":"ha$h"}"#);
}
#[test]
fn deserialize_enum_variant_from_json() {
let json = r#"{"passwordHashB64":"ha$h"}"#;
let creds: SendAccessCredentials = from_str(json).unwrap();
match creds {
SendAccessCredentials::Password(password_creds) => {
assert_eq!(password_creds.password_hash_b64, "ha$h");
}
_ => panic!("Expected Password variant"),
}
}
#[test]
fn serialize_enum_variant_to_json() {
let creds = SendAccessCredentials::Password(SendPasswordCredentials {
password_hash_b64: "ha$h".into(),
});
let json = to_string(&creds).unwrap();
assert_eq!(json, r#"{"passwordHashB64":"ha$h"}"#);
}
#[test]
fn roundtrip_enum_variant_through_json() {
let in_json = r#"{"passwordHashB64":"ha$h"}"#;
let creds: SendAccessCredentials = from_str(in_json).unwrap();
let out_json = to_string(&creds).unwrap();
assert_eq!(out_json, r#"{"passwordHashB64":"ha$h"}"#);
}
}
mod send_access_email_credentials_tests {
use serde_json::{from_str, to_string};
use super::*;
#[test]
fn deserialize_struct_camelcase_from_ts() {
let json = r#"{ "email": "user@example.com" }"#;
let s: SendEmailCredentials = from_str(json).unwrap();
assert_eq!(s.email, "user@example.com");
}
#[test]
fn serialize_struct_camelcase_to_wire() {
let s = SendEmailCredentials {
email: "user@example.com".into(),
};
let json = to_string(&s).unwrap();
assert_eq!(json, r#"{"email":"user@example.com"}"#);
}
#[test]
fn roundtrip_struct_camel_in_to_camel_out() {
let in_json = r#"{ "email": "user@example.com" }"#;
let parsed: SendEmailCredentials = from_str(in_json).unwrap();
let out_json = to_string(&parsed).unwrap();
assert_eq!(out_json, r#"{"email":"user@example.com"}"#);
}
#[test]
fn deserialize_enum_variant_from_json() {
let json = r#"{"email":"user@example.com"}"#;
let creds: SendAccessCredentials = from_str(json).unwrap();
match creds {
SendAccessCredentials::Email(email_creds) => {
assert_eq!(email_creds.email, "user@example.com");
}
_ => panic!("Expected Email variant"),
}
}
#[test]
fn serialize_enum_variant_to_json() {
let creds = SendAccessCredentials::Email(SendEmailCredentials {
email: "user@example.com".into(),
});
let json = to_string(&creds).unwrap();
assert_eq!(json, r#"{"email":"user@example.com"}"#);
}
#[test]
fn roundtrip_enum_variant_through_json() {
let in_json = r#"{"email":"user@example.com"}"#;
let creds: SendAccessCredentials = from_str(in_json).unwrap();
let out_json = to_string(&creds).unwrap();
assert_eq!(out_json, r#"{"email":"user@example.com"}"#);
}
}
mod send_access_email_otp_credentials_tests {
use serde_json::{from_str, to_string};
use super::*;
#[test]
fn deserialize_struct_camelcase_from_ts() {
let json = r#"{ "email": "user@example.com", "otp": "123456" }"#;
let s: SendEmailOtpCredentials = from_str(json).unwrap();
assert_eq!(s.email, "user@example.com");
assert_eq!(s.otp, "123456");
}
#[test]
fn serialize_struct_camelcase_to_wire() {
let s = SendEmailOtpCredentials {
email: "user@example.com".into(),
otp: "123456".into(),
};
let json = to_string(&s).unwrap();
assert_eq!(json, r#"{"email":"user@example.com","otp":"123456"}"#);
}
#[test]
fn roundtrip_struct_camel_in_to_camel_out() {
let in_json = r#"{ "email": "user@example.com", "otp": "123456" }"#;
let parsed: SendEmailOtpCredentials = from_str(in_json).unwrap();
let out_json = to_string(&parsed).unwrap();
assert_eq!(out_json, r#"{"email":"user@example.com","otp":"123456"}"#);
}
#[test]
fn deserialize_enum_variant_from_json() {
let json = r#"{"email":"user@example.com","otp":"123456"}"#;
let creds: SendAccessCredentials = from_str(json).unwrap();
match creds {
SendAccessCredentials::EmailOtp(otp_creds) => {
assert_eq!(otp_creds.email, "user@example.com");
assert_eq!(otp_creds.otp, "123456");
}
_ => panic!("Expected EmailOtp variant"),
}
}
#[test]
fn serialize_enum_variant_to_json() {
let creds = SendAccessCredentials::EmailOtp(SendEmailOtpCredentials {
email: "user@example.com".into(),
otp: "123456".into(),
});
let json = to_string(&creds).unwrap();
assert_eq!(json, r#"{"email":"user@example.com","otp":"123456"}"#);
}
#[test]
fn roundtrip_enum_variant_through_json() {
let in_json = r#"{"email":"user@example.com","otp":"123456"}"#;
let creds: SendAccessCredentials = from_str(in_json).unwrap();
let out_json = to_string(&creds).unwrap();
assert_eq!(out_json, r#"{"email":"user@example.com","otp":"123456"}"#);
}
}
}
}