use facet::Facet;
use facet_testhelpers::test;
use crate::from_str;
#[derive(Facet, Debug, PartialEq)]
#[facet(rename_all = "lowercase")]
#[repr(u8)]
enum Schema {
Object { fields: Vec<String> },
Seq { item: Box<Schema> },
#[facet(other)]
Type {
#[facet(tag)]
name: String,
},
}
#[derive(Facet, Debug, PartialEq)]
struct SchemaDoc {
v: Schema,
}
#[test]
fn test_known_variant_object() {
let input = r#"v @object{fields (a b c)}"#;
let result: SchemaDoc = from_str(input).unwrap();
assert_eq!(
result.v,
Schema::Object {
fields: vec!["a".into(), "b".into(), "c".into()]
}
);
}
#[test]
fn test_known_variant_seq() {
let input = r#"v @seq{item @string}"#;
let result: SchemaDoc = from_str(input).unwrap();
assert_eq!(
result.v,
Schema::Seq {
item: Box::new(Schema::Type {
name: "string".into()
})
}
);
}
#[test]
fn test_other_variant_captures_tag_name() {
let input = r#"v @string"#;
let result: SchemaDoc = from_str(input).unwrap();
assert_eq!(
result.v,
Schema::Type {
name: "string".into()
}
);
}
#[test]
fn test_other_variant_unit_tag() {
let input = r#"v @unit"#;
let result: SchemaDoc = from_str(input).unwrap();
assert_eq!(
result.v,
Schema::Type {
name: "unit".into()
}
);
}
#[test]
fn test_other_variant_custom_type() {
let input = r#"v @MyCustomType"#;
let result: SchemaDoc = from_str(input).unwrap();
assert_eq!(
result.v,
Schema::Type {
name: "MyCustomType".into()
}
);
}
#[derive(Facet, Debug, PartialEq)]
#[facet(rename_all = "lowercase")]
#[repr(u8)]
enum Value {
Null,
Bool(bool),
#[facet(other)]
Tagged {
#[facet(tag)]
tag: String,
#[facet(content)]
payload: Vec<Value>,
},
}
#[derive(Facet, Debug, PartialEq)]
struct Doc {
v: Value,
}
#[test]
fn test_known_variant_null() {
let input = r#"v @null"#;
let result: Doc = from_str(input).unwrap();
assert_eq!(result.v, Value::Null);
}
#[test]
fn test_known_variant_bool_not_representable() {
let input = r#"v @bool{@ true}"#;
let result: Result<Doc, _> = from_str(input);
assert!(
result.is_err(),
"newtype variants can't be represented with struct payload syntax"
);
}
#[test]
fn test_known_variant_bool_unhappy() {
let input = r#"v @bool(true)"#;
let result: Result<Doc, _> = from_str(input);
assert!(
result.is_err(),
"parens create a sequence, not a scalar payload"
);
}
#[test]
fn test_other_variant_with_content() {
let input = r#"v @custom(@null)"#;
let result: Doc = from_str(input).unwrap();
assert_eq!(
result.v,
Value::Tagged {
tag: "custom".into(),
payload: vec![Value::Null],
}
);
}
#[test]
fn test_other_variant_nested() {
let input = r#"v @wrapper(@inner(@null))"#;
let result: Doc = from_str(input).unwrap();
assert_eq!(
result.v,
Value::Tagged {
tag: "wrapper".into(),
payload: vec![Value::Tagged {
tag: "inner".into(),
payload: vec![Value::Null],
}],
}
);
}
#[derive(Facet, Debug, PartialEq)]
#[facet(rename_all = "snake_case")]
#[repr(u8)]
enum EventPattern {
DiscoverStart { executor: String },
DiscoverEnd,
ExecStart,
}
#[derive(Facet, Debug, PartialEq)]
#[facet(rename_all = "snake_case")]
#[repr(u8)]
enum EventAssertion {
MustEmit(EventPattern),
MustNotEmit(EventPattern),
Before {
first: EventPattern,
then: EventPattern,
},
}
#[derive(Facet, Debug, PartialEq)]
struct EventDoc {
events: Vec<EventAssertion>,
}
#[test]
fn test_chained_tags_deserialize_nested_newtypes() {
let input = r#"
events (
@must_emit/@discover_start{executor default}
@must_emit/@discover_end
@must_not_emit/@exec_start
@before{
first @discover_start{executor default}
then @discover_end
}
)
"#;
let result: EventDoc = from_str(input).unwrap();
assert_eq!(
result,
EventDoc {
events: vec![
EventAssertion::MustEmit(EventPattern::DiscoverStart {
executor: "default".into(),
}),
EventAssertion::MustEmit(EventPattern::DiscoverEnd),
EventAssertion::MustNotEmit(EventPattern::ExecStart),
EventAssertion::Before {
first: EventPattern::DiscoverStart {
executor: "default".into(),
},
then: EventPattern::DiscoverEnd,
},
],
}
);
}
#[derive(Facet, Debug, PartialEq)]
#[facet(rename_all = "snake_case")]
#[repr(u8)]
enum C {
Done,
}
#[derive(Facet, Debug, PartialEq)]
#[facet(rename_all = "snake_case")]
#[repr(u8)]
enum B {
Inner(C),
}
#[derive(Facet, Debug, PartialEq)]
#[facet(rename_all = "snake_case")]
#[repr(u8)]
enum A {
Outer(B),
}
#[test]
fn test_chained_tags_deserialize_three_nested_newtypes() {
let result: A = crate::from_str_expr("@outer/@inner/@done").unwrap();
assert_eq!(result, A::Outer(B::Inner(C::Done)));
}
#[derive(Facet, Debug, PartialEq)]
#[facet(rename_all = "snake_case")]
#[repr(u8)]
enum MessagePattern {
Message(String),
}
#[derive(Facet, Debug, PartialEq)]
#[facet(rename_all = "snake_case")]
#[repr(u8)]
enum MessageAssertion {
MustEmit(MessagePattern),
}
#[test]
fn test_chained_tags_deserialize_scalar_leaf_newtype() {
let result: MessageAssertion =
crate::from_str_expr(r#"@must_emit/@message"hello world""#).unwrap();
assert_eq!(
result,
MessageAssertion::MustEmit(MessagePattern::Message("hello world".into()))
);
}
#[test]
fn test_chained_tags_deserialize_heredoc_leaf_newtype() {
let result: MessageAssertion =
crate::from_str_expr("@must_emit/@message<<EOF\nhello\nworld\nEOF").unwrap();
assert_eq!(
result,
MessageAssertion::MustEmit(MessagePattern::Message("hello\nworld\n".into()))
);
}
use crate::from_str_expr;
#[derive(Facet, Debug, PartialEq)]
#[facet(rename_all = "kebab-case")]
#[repr(u8)]
enum SimpleFilter {
Null,
Gt(Vec<String>),
#[facet(other)]
EqBare(Option<String>),
}
#[test]
fn test_other_variant_serializes_untagged() {
let value = SimpleFilter::EqBare(Some("$id".to_string()));
let serialized = crate::to_string_compact(&value).unwrap();
eprintln!("Serialized EqBare: {serialized}");
assert!(
!serialized.contains("eq-bare"),
"Expected untagged serialization, but got: {serialized}"
);
}
#[test]
fn test_other_variant_roundtrip_via_expr() {
let original = SimpleFilter::EqBare(Some("$id".to_string()));
let serialized = crate::to_string_compact(&original).unwrap();
eprintln!("Serialized: {serialized}");
let deserialized: SimpleFilter = from_str_expr(&serialized).unwrap();
assert_eq!(original, deserialized);
}
#[test]
fn test_known_variant_still_tagged() {
let value = SimpleFilter::Gt(vec!["$value".to_string()]);
let serialized = crate::to_string_compact(&value).unwrap();
eprintln!("Serialized Gt: {serialized}");
assert!(
serialized.contains("gt"),
"Expected tagged serialization, but got: {serialized}"
);
}