use core::fmt;
use core::ops::Deref;
use bstr::BStr;
use musli::compat::Bytes;
use musli::de::{DecodeUnsized, Decoder};
use musli::{Decode, Encode};
#[derive(Debug, Encode, Decode, PartialEq, Eq)]
struct FieldVariantTag<'a> {
name: &'a str,
}
impl fmt::Display for FieldVariantTag<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.name.fmt(f)
}
}
#[derive(Debug, PartialEq, Eq, Encode, Decode)]
#[musli(transparent)]
struct CustomBytesVec(Vec<u8>);
impl fmt::Display for CustomBytesVec {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
BStr::new(&self.0).fmt(f)
}
}
const CUSTOM_TAG1: FieldVariantTag = FieldVariantTag { name: "field1" };
const CUSTOM_TAG2: FieldVariantTag = FieldVariantTag { name: "field2" };
#[derive(Debug, PartialEq, Encode, Decode)]
#[musli(name(type = FieldVariantTag))]
pub struct StructCustomFieldAsStruct {
#[musli(name = CUSTOM_TAG1)]
field1: u32,
#[musli(name = CUSTOM_TAG2)]
field2: u32,
}
#[test]
fn bytes_tag_vec() {
musli::macros::assert_roundtrip_eq!(full, CustomBytesVec(b"hello world".to_vec()));
}
#[test]
fn custom_struct_tag() {
musli::macros::assert_roundtrip_eq!(
no_json,
StructCustomFieldAsStruct {
field1: 42,
field2: 84,
}
);
}
#[derive(Debug, PartialEq, Encode, Decode)]
#[musli(name(type = Bytes<[u8; 4]>, format_with = BStr::new))]
pub struct BytesName {
#[musli(name = Bytes([1, 2, 3, 4]))]
field1: u32,
#[musli(name = Bytes([2, 3, 4, 5]))]
field2: u32,
}
#[test]
fn bytes_name() {
musli::macros::assert_roundtrip_eq!(
no_json,
BytesName {
field1: 42,
field2: 84,
}
);
}
#[derive(Encode)]
#[repr(transparent)]
#[musli(transparent)]
struct UnsizedBytes(#[musli(bytes)] [u8]);
impl<'de, M> DecodeUnsized<'de, M> for UnsizedBytes {
fn decode_unsized<D, F, O>(decoder: D, f: F) -> Result<O, D::Error>
where
D: Decoder<'de, Mode = M>,
F: FnOnce(&Self) -> Result<O, D::Error>,
{
decoder.decode_unsized_bytes(|bytes: &[u8]| f(UnsizedBytes::new(bytes)))
}
}
impl UnsizedBytes {
const fn new(data: &[u8]) -> &Self {
unsafe { &*(data as *const [u8] as *const UnsizedBytes) }
}
}
impl Deref for UnsizedBytes {
type Target = [u8];
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl fmt::Display for UnsizedBytes {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
BStr::new(&self.0).fmt(f)
}
}
impl fmt::Debug for UnsizedBytes {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
BStr::new(&self.0).fmt(f)
}
}
#[derive(Debug, PartialEq, Encode, Decode)]
#[musli(name(type = UnsizedBytes, method = "unsized"))]
pub struct StructUnsizedBytes {
#[musli(name = UnsizedBytes::new(&[1, 2, 3, 4]), pattern = UnsizedBytes([1, 2, 3, 4]))]
field1: u32,
#[musli(name = UnsizedBytes::new(&[2, 3, 4, 5]), pattern = UnsizedBytes([2, 3, 4, 5]))]
field2: u32,
}
#[test]
fn struct_unsized_bytes() {
musli::macros::assert_roundtrip_eq!(
no_json,
StructUnsizedBytes {
field1: 42,
field2: 84,
}
);
}
#[derive(Debug, PartialEq, Encode, Decode)]
#[musli(name(type = UnsizedBytes, method = "unsized"))]
pub enum EnumUnsizedBytes {
#[musli(name = UnsizedBytes::new(&[1, 2, 3, 4]), pattern = UnsizedBytes([1, 2, 3, 4]))]
Variant1 { field1: u32 },
#[musli(name = UnsizedBytes::new(&[2, 3, 4, 5]), pattern = UnsizedBytes([2, 3, 4, 5]))]
Variant2 { field2: u32 },
}
#[test]
fn enum_unsized_bytes() {
musli::macros::assert_roundtrip_eq!(no_json, EnumUnsizedBytes::Variant1 { field1: 42 });
musli::macros::assert_roundtrip_eq!(no_json, EnumUnsizedBytes::Variant2 { field2: 84 });
}
#[derive(Debug, PartialEq, Eq, Encode, Decode)]
#[musli(transparent)]
struct CustomBytes<'a>(#[musli(bytes)] &'a [u8]);
impl fmt::Display for CustomBytes<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
BStr::new(self.0).fmt(f)
}
}
#[derive(Debug, PartialEq, Encode, Decode)]
#[musli(name(type = CustomBytes))]
struct StructWithCustomBytes {
#[musli(name = CustomBytes(b"name in bytes"))]
string: String,
}
#[test]
fn struct_with_bytes_name() {
musli::macros::assert_roundtrip_eq!(
no_json,
StructWithCustomBytes {
string: String::from("Some String"),
}
);
}
#[derive(Debug, PartialEq, Encode, Decode)]
#[musli(name(type = CustomBytes))]
enum EnumWithCustomBytes {
#[musli(name = CustomBytes(b"a"))]
Variant1 { string: String },
#[musli(name = CustomBytes(b"b"), name(type = CustomBytes))]
Variant2 {
#[musli(name = CustomBytes(b"c"))]
string: String,
},
}
#[test]
fn bytes_tag_in_enum() {
musli::macros::assert_roundtrip_eq!(
no_json,
EnumWithCustomBytes::Variant1 {
string: String::from("st"),
}
);
musli::macros::assert_roundtrip_eq!(
no_json,
EnumWithCustomBytes::Variant2 {
string: String::from("st"),
}
);
}
#[test]
fn decode_from_multiple() {
#[derive(Debug, PartialEq, Encode, Decode)]
pub struct StructDecodeFrom {
#[musli(Text, name = "secondary")]
primary: u32,
other: u32,
}
#[derive(Debug, PartialEq, Encode, Decode)]
pub struct StructDecodeTo {
#[musli(Text, name = "primary", pattern = ("primary" | "secondary"))]
primary: u32,
other: u32,
}
musli::macros::assert_decode_eq!(
text_mode,
StructDecodeFrom {
primary: 10,
other: 20,
},
StructDecodeTo {
primary: 10,
other: 20,
},
json = r#"{"secondary":10,"other":20}"#,
);
musli::macros::assert_roundtrip_eq!(
full,
StructDecodeTo {
primary: 10,
other: 20,
},
json = r#"{"primary":10,"other":20}"#,
);
}