use serde::Deserialize; use std::collections::HashMap; use tvix_eval::builtin_macros::builtins; use crate::de::{from_str, from_str_with_config}; #[test] fn deserialize_none() { let result: Option = from_str("null").expect("should deserialize"); assert_eq!(None, result); } #[test] fn deserialize_some() { let result: Option = from_str("40 + 2").expect("should deserialize"); assert_eq!(Some(42), result); } #[test] fn deserialize_string() { let result: String = from_str( r#" let greeter = name: "Hello ${name}!"; in greeter "Slartibartfast" "#, ) .expect("should deserialize"); assert_eq!(result, "Hello Slartibartfast!"); } #[test] fn deserialize_empty_list() { let result: Vec = from_str("[ ]").expect("should deserialize"); assert!(result.is_empty()) } #[test] fn deserialize_integer_list() { let result: Vec = from_str("builtins.map (n: n + 2) [ 21 40 67 ]").expect("should deserialize"); assert_eq!(result, vec![23, 42, 69]); } #[test] fn deserialize_empty_map() { let result: HashMap = from_str("{ }").expect("should deserialize"); assert!(result.is_empty()); } #[test] fn deserialize_integer_map() { let result: HashMap = from_str("{ age = 40 + 2; }").expect("should deserialize"); assert_eq!(result.len(), 1); assert_eq!(*result.get("age").unwrap(), 42); } #[test] fn deserialize_struct() { #[derive(Debug, Deserialize, PartialEq)] struct Person { name: String, age: usize, } let result: Person = from_str( r#" { name = "Slartibartfast"; age = 42; } "#, ) .expect("should deserialize"); assert_eq!( result, Person { name: "Slartibartfast".into(), age: 42, } ); } #[test] fn deserialize_newtype() { #[derive(Debug, Deserialize, PartialEq)] struct Number(usize); let result: Number = from_str("42").expect("should deserialize"); assert_eq!(result, Number(42)); } #[test] fn deserialize_tuple() { let result: (String, usize) = from_str(r#" [ "foo" 42 ] "#).expect("should deserialize"); assert_eq!(result, ("foo".into(), 42)); } #[test] fn deserialize_unit_enum() { #[derive(Debug, Deserialize, PartialEq)] enum Foo { Bar, Baz, } let result: Foo = from_str("\"Baz\"").expect("should deserialize"); assert_eq!(result, Foo::Baz); } #[test] fn deserialize_tuple_enum() { #[derive(Debug, Deserialize, PartialEq)] enum Foo { Bar, Baz(String, usize), } let result: Foo = from_str( r#" { Baz = [ "Slartibartfast" 42 ]; } "#, ) .expect("should deserialize"); assert_eq!(result, Foo::Baz("Slartibartfast".into(), 42)); } #[test] fn deserialize_struct_enum() { #[derive(Debug, Deserialize, PartialEq)] enum Foo { Bar, Baz { name: String, age: usize }, } let result: Foo = from_str( r#" { Baz.name = "Slartibartfast"; Baz.age = 42; } "#, ) .expect("should deserialize"); assert_eq!( result, Foo::Baz { name: "Slartibartfast".into(), age: 42 } ); } #[test] fn deserialize_enum_all() { #[derive(Debug, Deserialize, PartialEq)] #[serde(rename_all = "snake_case")] enum TestEnum { Unit, Tuple(String, String), Struct { name: String, age: usize }, } let result: Vec = from_str( r#" let mkTuple = country: drink: { tuple = [ country drink ]; }; in [ (mkTuple "UK" "cask ale") "unit" { struct.name = "Slartibartfast"; struct.age = 42; } (mkTuple "Russia" "квас") ] "#, ) .expect("should deserialize"); let expected = vec![ TestEnum::Tuple("UK".into(), "cask ale".into()), TestEnum::Unit, TestEnum::Struct { name: "Slartibartfast".into(), age: 42, }, TestEnum::Tuple("Russia".into(), "квас".into()), ]; assert_eq!(result, expected); } #[test] fn deserialize_with_config() { let result: String = from_str_with_config("builtins.testWithConfig", |eval| { // Add a literal string builtin that just returns `"ok"`. eval.src_builtins.push(("testWithConfig", "\"ok\"")); }) .expect("should deserialize"); assert_eq!(result, "ok"); } #[builtins] mod test_builtins { use bstr::ByteSlice; use tvix_eval::generators::{Gen, GenCo}; use tvix_eval::{ErrorKind, NixString, Value}; #[builtin("prependHello")] pub async fn builtin_prepend_hello(co: GenCo, x: Value) -> Result { match x { Value::String(s) => { let new_string = NixString::from(format!("hello {}", s.to_str().unwrap())); Ok(Value::String(new_string)) } _ => Err(ErrorKind::TypeError { expected: "string", actual: "not string", }), } } } #[test] fn deserialize_with_extra_builtin() { let code = "builtins.prependHello \"world\""; let result: String = from_str_with_config(code, |eval| { eval.builtins.append(&mut test_builtins::builtins()); }) .expect("should deserialize"); assert_eq!(result, "hello world"); }