use forma_core::de::Deserialize as _;
use forma_derive::{Deserialize, Serialize};
use forma_json::{from_str, to_string};
#[derive(Debug, Clone, PartialEq, Serialize)]
#[forma(into = "String")]
struct Email(String);
impl From<Email> for String {
fn from(e: Email) -> String {
e.0
}
}
#[test]
fn test_into_serialize() {
let email = Email("test@example.com".into());
assert_eq!(to_string(&email).unwrap(), "\"test@example.com\"");
}
#[derive(Debug, PartialEq, Deserialize)]
#[forma(from = "String")]
struct Username(String);
impl From<String> for Username {
fn from(s: String) -> Self {
Username(s.to_lowercase())
}
}
#[test]
fn test_from_deserialize() {
let u: Username = from_str("\"ADMIN\"").unwrap();
assert_eq!(u.0, "admin");
}
#[derive(Debug, PartialEq, Deserialize)]
#[forma(try_from = "u64")]
struct Port(u16);
impl TryFrom<u64> for Port {
type Error = String;
fn try_from(v: u64) -> Result<Self, String> {
if v > 65535 {
Err(format!("port {v} out of range"))
} else {
Ok(Port(v as u16))
}
}
}
#[test]
fn test_try_from_deserialize() {
let p: Port = from_str("8080").unwrap();
assert_eq!(p.0, 8080);
}
#[test]
fn test_try_from_deserialize_error() {
let err = from_str::<Port>("99999").unwrap_err();
assert!(err.to_string().contains("out of range"));
}
#[derive(Debug, PartialEq, Deserialize)]
#[forma(deny_unknown_fields)]
struct Strict {
x: i32,
y: i32,
}
#[test]
fn test_deny_unknown_fields() {
let s: Strict = from_str("{\"x\":1,\"y\":2}").unwrap();
assert_eq!(s, Strict { x: 1, y: 2 });
}
#[test]
fn test_deny_unknown_fields_error() {
let err = from_str::<Strict>("{\"x\":1,\"y\":2,\"z\":3}").unwrap_err();
let msg = err.to_string();
assert!(msg.contains("unknown field"), "got: {msg}");
}
use std::fmt;
trait Printable: fmt::Display {}
impl Printable for String {}
#[derive(Debug, PartialEq, Serialize)]
#[forma(bound = "T: fmt::Display")]
struct Labeled<T> {
#[forma(serialize_with = "serialize_display")]
label: T,
}
fn serialize_display<T: fmt::Display, S: forma_core::ser::Serializer>(
value: &T,
serializer: S,
) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&value.to_string())
}
#[test]
fn test_bound() {
let l = Labeled {
label: 42i32,
};
assert_eq!(to_string(&l).unwrap(), "{\"label\":\"42\"}");
}
#[derive(Debug, PartialEq, Deserialize, Default)]
#[forma(default)]
struct Settings {
width: u32,
height: u32,
fullscreen: bool,
}
#[test]
fn test_container_default_partial() {
let s: Settings = from_str("{\"width\":1920}").unwrap();
assert_eq!(s.width, 1920);
assert_eq!(s.height, 0);
assert_eq!(s.fullscreen, false);
}
#[test]
fn test_container_default_empty() {
let s: Settings = from_str("{}").unwrap();
assert_eq!(s, Settings::default());
}
fn serialize_uppercase<S: forma_core::ser::Serializer>(
value: &str,
serializer: S,
) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&value.to_uppercase())
}
fn deserialize_lowercase<'de, D: forma_core::de::Deserializer<'de>>(
deserializer: D,
) -> Result<String, D::Error> {
let s = String::deserialize(deserializer)?;
Ok(s.to_lowercase())
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct NormalizedName {
#[forma(serialize_with = "serialize_uppercase")]
#[forma(deserialize_with = "deserialize_lowercase")]
name: String,
}
#[test]
fn test_serialize_with() {
let n = NormalizedName {
name: "Alice".into(),
};
assert_eq!(to_string(&n).unwrap(), "{\"name\":\"ALICE\"}");
}
#[test]
fn test_deserialize_with() {
let n: NormalizedName = from_str("{\"name\":\"ALICE\"}").unwrap();
assert_eq!(n.name, "alice");
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Wrapper<T> {
inner: T,
}
#[test]
fn test_generics() {
let w = Wrapper { inner: 42i32 };
let json = to_string(&w).unwrap();
assert_eq!(json, "{\"inner\":42}");
let back: Wrapper<i32> = from_str(&json).unwrap();
assert_eq!(back, w);
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Pair<A, B> {
first: A,
second: B,
}
#[test]
fn test_multi_generic() {
let p = Pair {
first: "hello".to_string(),
second: 42i32,
};
let json = to_string(&p).unwrap();
let back: Pair<String, i32> = from_str(&json).unwrap();
assert_eq!(back, p);
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
enum Animal {
Dog { name: String },
Cat { name: String, indoor: bool },
}
#[test]
fn test_vec_of_enums() {
let animals = vec![
Animal::Dog { name: "Rex".into() },
Animal::Cat {
name: "Whiskers".into(),
indoor: true,
},
];
let json = to_string(&animals).unwrap();
let back: Vec<Animal> = from_str(&json).unwrap();
assert_eq!(back, animals);
}
#[test]
fn test_nested_option() {
let v: Option<Option<i32>> = Some(Some(42));
assert_eq!(to_string(&v).unwrap(), "42");
let back: Option<Option<i32>> = from_str("42").unwrap();
assert_eq!(back, v);
let v: Option<Option<i32>> = Some(None);
assert_eq!(to_string(&v).unwrap(), "null");
let v: Option<Option<i32>> = None;
assert_eq!(to_string(&v).unwrap(), "null");
}
#[test]
fn test_i128() {
let v: i128 = i128::MAX;
let json = to_string(&v).unwrap();
let back: i128 = from_str(&json).unwrap();
assert_eq!(back, v);
}
#[test]
fn test_u128() {
let v: u128 = u128::MAX;
let json = to_string(&v).unwrap();
let back: u128 = from_str(&json).unwrap();
assert_eq!(back, v);
}