serde-reflection 0.3.2

Extract representations of Serde data formats
Documentation
// Copyright (c) Facebook, Inc. and its affiliates
// SPDX-License-Identifier: MIT OR Apache-2.0

use serde_reflection::{ContainerFormat, Error, Format, FormatHolder, Named, VariantFormat};
use std::collections::HashSet;
use std::ops::Deref;

#[test]
fn test_format_visiting() {
    use Format::*;

    let format = ContainerFormat::Enum(
        vec![(
            0,
            Named {
                name: "foo".into(),
                value: VariantFormat::Tuple(vec![
                    TypeName("foo".into()),
                    TypeName("bar".into()),
                    Seq(Box::new(TypeName("foo".into()))),
                ]),
            },
        )]
        .into_iter()
        .collect(),
    );
    let mut names = HashSet::new();
    format
        .visit(&mut |f| {
            if let TypeName(x) = f {
                // Insert a &str borrowed from `format`.
                names.insert(x.as_str());
            }
            Ok(())
        })
        .unwrap();
    assert_eq!(names.len(), 2);

    assert!(VariantFormat::unknown().visit(&mut |_| Ok(())).is_err());
    assert!(Format::unknown().visit(&mut |_| Ok(())).is_err());
}

// Note: this does not test pointer equality, only referenced content.
fn assert_variable_contains_value(format: &Format, value: &Format) {
    match format {
        Format::Variable(variable) => {
            assert_eq!(
                variable
                    .borrow()
                    .deref()
                    .as_ref()
                    .expect("must contain a value"),
                value
            );
        }
        _ => panic!(),
    }
}

#[test]
fn test_variable_unification() {
    let mut x = Format::unknown();
    let mut y = Format::unknown();
    let mut z = Format::unknown();
    x.unify(y.clone()).unwrap();
    // x is untouched when unifying with y.
    // We chose to assign y to (a clone of the RC pointer to the refcell of) x.
    assert_eq!(x, Format::unknown());
    assert_variable_contains_value(&y, &x);

    x.unify(Format::U8).unwrap();
    // y is untouched when assigning x alone.
    assert_variable_contains_value(&y, &x);

    y.unify(z.clone()).unwrap();
    // We chose to assign z.
    assert_variable_contains_value(&y, &x);
    assert_variable_contains_value(&z, &y);

    z.unify(Format::U8).unwrap();
    // x is unchanged
    assert_variable_contains_value(&x, &Format::U8);
    // The clone of y used in z was simplified but not y itself.
    assert_variable_contains_value(&y, &x);
    // z was simplified to use the same refcell as x.
    assert_variable_contains_value(&z, &Format::U8);

    // Re-assigning manually x to confirm.
    match &x {
        Format::Variable(variable) => {
            *variable.borrow_mut() = Some(Format::U16);
        }
        _ => panic!(),
    }
    assert_variable_contains_value(&z, &Format::U16);

    z.reduce();
    // We copied out the value of x into z, which is no longer a variable.
    assert_eq!(z, Format::U16);
    // Not touching x.
    assert_ne!(x, Format::U16);

    y.reduce();
    assert_eq!(y, Format::U16);
    assert_ne!(x, Format::U16);

    x.reduce();
    assert_eq!(x, Format::U16);
}

#[test]
fn test_format_unification() {
    use Format::*;

    let mut x = Format::unknown();
    assert!(x.unify(U8).is_ok());
    x.reduce();
    assert_eq!(x, U8);
    assert_eq!(
        x.unify(U16).unwrap_err(),
        Error::Incompatible("U8".into(), "U16".into())
    );

    let mut x = Tuple(vec![Format::unknown(), U32]);
    x.unify(Tuple(vec![U16, Format::unknown()])).unwrap();
    x.reduce();
    assert_eq!(x, Tuple(vec![U16, U32]));

    for x in vec![
        Unit,
        Bool,
        I8,
        I16,
        I32,
        I64,
        I128,
        U8,
        U16,
        U32,
        U64,
        U128,
        F32,
        F64,
        Char,
        Str,
        Bytes,
        TypeName("foo".into()),
        Option(Box::new(Unit)),
        Seq(Box::new(Unit)),
        Map {
            key: Box::new(Unit),
            value: Box::new(Unit),
        },
        Tuple(vec![Format::unknown()]),
    ]
    .iter_mut()
    {
        assert!(x.unify(TypeName("bar".into())).is_err());
        assert!(x.unify(Option(Box::new(U32))).is_err());
        assert!(x.unify(Seq(Box::new(U32))).is_err());
        assert!(x.unify(Tuple(vec![])).is_err());
    }
}

#[test]
fn test_container_format_unification() {
    use ContainerFormat::*;
    use Format::*;

    let mut x = TupleStruct(vec![Format::unknown(), U32]);
    x.unify(TupleStruct(vec![U16, Format::unknown()])).unwrap();
    x.reduce();
    assert_eq!(x, TupleStruct(vec![U16, U32]));

    let mut x = Enum(
        vec![(
            0,
            Named {
                name: "foo".into(),
                value: VariantFormat::Tuple(vec![Format::unknown()]),
            },
        )]
        .into_iter()
        .collect(),
    );
    assert!(x
        .unify(Enum(
            vec![(
                0,
                Named {
                    name: "foo".into(),
                    value: VariantFormat::Unit,
                }
            )]
            .into_iter()
            .collect()
        ))
        .is_err());
    assert!(x
        .unify(Enum(
            vec![(
                0,
                Named {
                    name: "foo".into(),
                    value: VariantFormat::Tuple(vec![U8]),
                }
            )]
            .into_iter()
            .collect()
        ))
        .is_ok());

    for x in vec![
        UnitStruct,
        NewTypeStruct(Box::new(Unit)),
        TupleStruct(vec![Format::unknown()]),
        Struct(vec![Named {
            name: "foo".into(),
            value: Format::unknown(),
        }]),
        Enum(
            vec![(
                0,
                Named {
                    name: "foo".into(),
                    value: VariantFormat::unknown(),
                },
            )]
            .into_iter()
            .collect(),
        ),
    ]
    .iter_mut()
    {
        assert!(x.unify(NewTypeStruct(Box::new(U8))).is_err());
        assert!(x.unify(TupleStruct(vec![])).is_err());
        assert!(x
            .unify(Struct(vec![Named {
                name: "bar".into(),
                value: Format::unknown()
            }]))
            .is_err());
        assert!(x
            .unify(Enum(
                vec![(
                    0,
                    Named {
                        name: "bar".into(),
                        value: VariantFormat::unknown()
                    }
                )]
                .into_iter()
                .collect()
            ))
            .is_err());
    }
}