#[macro_export]
macro_rules! pep_json {
(pseudonym($value:expr)) => {{
use $crate::data::padding::Padded;
let value = $value;
let s_str: &str = value.as_ref();
match $crate::data::simple::Pseudonym::from_string_padded(s_str) {
Ok(pseudo) => $crate::data::json::data::PEPJSONValue::Pseudonym(pseudo),
Err(_) => $crate::data::json::data::PEPJSONValue::LongPseudonym(
$crate::data::long::LongPseudonym::from_string_padded(s_str)
)
}
}};
({ $($tt:tt)* }) => {{
let builder = $crate::data::json::builder::PEPJSONBuilder::new();
pep_json!(@object builder, $($tt)*)
}};
(@object $builder:ident, ) => {
$builder.build()
};
(@object $builder:ident, $key:literal : pseudonym($value:expr)) => {{
let value = $value;
$builder.pseudonym($key, value.as_ref()).build()
}};
(@object $builder:ident, $key:literal : pseudonym($value:expr), $($rest:tt)*) => {{
let value = $value;
let builder = $builder.pseudonym($key, value.as_ref());
pep_json!(@object builder, $($rest)*)
}};
(@object $builder:ident, $key:literal : { $($inner:tt)* }) => {{
$builder.attribute($key, serde_json::json!({ $($inner)* })).build()
}};
(@object $builder:ident, $key:literal : { $($inner:tt)* }, $($rest:tt)*) => {{
let builder = $builder.attribute($key, serde_json::json!({ $($inner)* }));
pep_json!(@object builder, $($rest)*)
}};
(@object $builder:ident, $key:literal : [ $($inner:tt)* ]) => {{
$builder.attribute($key, serde_json::json!([ $($inner)* ])).build()
}};
(@object $builder:ident, $key:literal : [ $($inner:tt)* ], $($rest:tt)*) => {{
let builder = $builder.attribute($key, serde_json::json!([ $($inner)* ]));
pep_json!(@object builder, $($rest)*)
}};
(@object $builder:ident, $key:literal : $value:expr) => {{
$builder.attribute($key, serde_json::json!($value)).build()
}};
(@object $builder:ident, $key:literal : $value:expr, $($rest:tt)*) => {{
let builder = $builder.attribute($key, serde_json::json!($value));
pep_json!(@object builder, $($rest)*)
}};
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
use crate::client::{decrypt, encrypt};
use crate::factors::contexts::EncryptionContext;
use crate::factors::EncryptionSecret;
use crate::keys::{
make_attribute_global_keys, make_attribute_session_keys, make_pseudonym_global_keys,
make_pseudonym_session_keys, AttributeSessionKeys, PseudonymSessionKeys, SessionKeys,
};
use serde_json::json;
fn make_test_keys() -> SessionKeys {
let mut rng = rand::rng();
let (_, attr_global_secret) = make_attribute_global_keys(&mut rng);
let (_, pseudo_global_secret) = make_pseudonym_global_keys(&mut rng);
let enc_secret = EncryptionSecret::from("test-secret".as_bytes().to_vec());
let session = EncryptionContext::from("session-1");
let (attr_public, attr_secret) =
make_attribute_session_keys(&attr_global_secret, &session, &enc_secret);
let (pseudo_public, pseudo_secret) =
make_pseudonym_session_keys(&pseudo_global_secret, &session, &enc_secret);
SessionKeys {
attribute: AttributeSessionKeys {
public: attr_public,
secret: attr_secret,
},
pseudonym: PseudonymSessionKeys {
public: pseudo_public,
secret: pseudo_secret,
},
}
}
#[test]
fn macro_with_pseudonym_id() {
let mut rng = rand::rng();
let keys = make_test_keys();
let pep_value = pep_json!({
"id": pseudonym("user1@example.com"),
"age": 16,
"verified": true,
"scores": [88, 91, 85]
});
let encrypted = encrypt(&pep_value, &keys, &mut rng);
#[cfg(feature = "elgamal3")]
let decrypted = decrypt(&encrypted, &keys).unwrap();
#[cfg(not(feature = "elgamal3"))]
let decrypted = decrypt(&encrypted, &keys);
let expected = json!({
"id": "user1@example.com",
"age": 16,
"verified": true,
"scores": [88, 91, 85]
});
assert_eq!(expected, decrypted.to_value().unwrap());
}
#[test]
fn macro_only_attributes() {
let mut rng = rand::rng();
let keys = make_test_keys();
let pep_value = pep_json!({
"name": "Alice",
"age": 30
});
let encrypted = encrypt(&pep_value, &keys, &mut rng);
#[cfg(feature = "elgamal3")]
let decrypted = decrypt(&encrypted, &keys).unwrap();
#[cfg(not(feature = "elgamal3"))]
let decrypted = decrypt(&encrypted, &keys);
let expected = json!({
"name": "Alice",
"age": 30
});
assert_eq!(expected, decrypted.to_value().unwrap());
}
#[test]
fn macro_empty_object() {
let mut rng = rand::rng();
let keys = make_test_keys();
let pep_value = pep_json!({});
let encrypted = encrypt(&pep_value, &keys, &mut rng);
#[cfg(feature = "elgamal3")]
let decrypted = decrypt(&encrypted, &keys).unwrap();
#[cfg(not(feature = "elgamal3"))]
let decrypted = decrypt(&encrypted, &keys);
assert_eq!(json!({}), decrypted.to_value().unwrap());
}
#[test]
fn macro_multiple_pseudonyms() {
let mut rng = rand::rng();
let keys = make_test_keys();
let pep_value = pep_json!({
"id1": pseudonym("user1@example.com"),
"id2": pseudonym("user2@example.com")
});
let encrypted = encrypt(&pep_value, &keys, &mut rng);
#[cfg(feature = "elgamal3")]
let decrypted = decrypt(&encrypted, &keys).unwrap();
#[cfg(not(feature = "elgamal3"))]
let decrypted = decrypt(&encrypted, &keys);
let expected = json!({
"id1": "user1@example.com",
"id2": "user2@example.com"
});
assert_eq!(expected, decrypted.to_value().unwrap());
}
#[test]
fn macro_nested_values() {
let mut rng = rand::rng();
let keys = make_test_keys();
let pep_value = pep_json!({
"user": {"name": "Alice", "active": true},
"scores": [1, 2, 3]
});
let encrypted = encrypt(&pep_value, &keys, &mut rng);
#[cfg(feature = "elgamal3")]
let decrypted = decrypt(&encrypted, &keys).unwrap();
#[cfg(not(feature = "elgamal3"))]
let decrypted = decrypt(&encrypted, &keys);
let expected = json!({
"user": {
"name": "Alice",
"active": true
},
"scores": [1, 2, 3]
});
assert_eq!(expected, decrypted.to_value().unwrap());
}
#[test]
fn macro_with_string_variables() {
let mut rng = rand::rng();
let keys = make_test_keys();
let user_id = String::from("user@example.com");
let pep_value = pep_json!({
"id": pseudonym(user_id)
});
let encrypted = encrypt(&pep_value, &keys, &mut rng);
#[cfg(feature = "elgamal3")]
let decrypted = decrypt(&encrypted, &keys).unwrap();
#[cfg(not(feature = "elgamal3"))]
let decrypted = decrypt(&encrypted, &keys);
let expected = json!({
"id": "user@example.com"
});
assert_eq!(expected, decrypted.to_value().unwrap());
}
#[test]
fn macro_standalone_pseudonym_with_string() {
let user_id = String::from("test@example.com");
let pep_value = pep_json!(pseudonym(user_id));
match pep_value {
crate::data::json::data::PEPJSONValue::Pseudonym(_) => {
}
crate::data::json::data::PEPJSONValue::LongPseudonym(_) => {
}
_ => panic!("Expected Pseudonym or LongPseudonym variant"),
}
}
}