rustdoc-types 0.57.3

Types for rustdoc's json output
Documentation
use super::*;

#[test]
fn test_struct_info_roundtrip() {
    let s = ItemEnum::Struct(Struct {
        generics: Generics { params: vec![], where_predicates: vec![] },
        kind: StructKind::Plain { fields: vec![], has_stripped_fields: false },
        impls: vec![],
    });

    // JSON
    let struct_json = serde_json::to_string(&s).unwrap();
    let de_s = serde_json::from_str(&struct_json).unwrap();
    assert_eq!(s, de_s);

    // Bincode
    let encoded: Vec<u8> = bincode::serialize(&s).unwrap();
    let decoded: ItemEnum = bincode::deserialize(&encoded).unwrap();
    assert_eq!(s, decoded);
}

#[test]
fn test_union_info_roundtrip() {
    let u = ItemEnum::Union(Union {
        generics: Generics { params: vec![], where_predicates: vec![] },
        has_stripped_fields: false,
        fields: vec![],
        impls: vec![],
    });

    // JSON
    let union_json = serde_json::to_string(&u).unwrap();
    let de_u = serde_json::from_str(&union_json).unwrap();
    assert_eq!(u, de_u);

    // Bincode
    let encoded: Vec<u8> = bincode::serialize(&u).unwrap();
    let decoded: ItemEnum = bincode::deserialize(&encoded).unwrap();
    assert_eq!(u, decoded);
}

#[cfg(feature = "rkyv_0_8")]
mod rkyv {
    use std::fmt::Debug;

    use rkyv::Archive;
    use rkyv::api::high::{HighDeserializer, HighSerializer};
    use rkyv::bytecheck::CheckBytes;
    use rkyv::rancor::Strategy;
    use rkyv::ser::allocator::ArenaHandle;
    use rkyv::util::AlignedVec;
    use rkyv::validation::Validator;
    use rkyv::validation::archive::ArchiveValidator;
    use rkyv::validation::shared::SharedValidator;

    use crate::*;

    #[test]
    /// A test to exercise the (de)serialization roundtrip for a representative selection of types,
    /// covering most of the rkyv-specific attributes we had to had.
    fn test_rkyv_roundtrip() {
        // Standard derives: a plain struct and union, mirroring the existing serde/bincode tests.
        let s = ItemEnum::Struct(Struct {
            generics: Generics { params: vec![], where_predicates: vec![] },
            kind: StructKind::Plain { fields: vec![Id(1), Id(2)], has_stripped_fields: false },
            impls: vec![Id(3)],
        });
        rkyv_roundtrip(&s);

        let u = ItemEnum::Union(Union {
            generics: Generics { params: vec![], where_predicates: vec![] },
            has_stripped_fields: false,
            fields: vec![Id(1)],
            impls: vec![],
        });
        rkyv_roundtrip(&u);

        // Extra trait derives, via rkyv(derive(PartialEq, Eq, PartialOrd, Ord, Hash)), on the archived type.
        rkyv_roundtrip(&Id(99));

        // Recursive cycle-breaking: `BorrowedRef` has omit_bounds on its `Box<Type>` field.
        let ty = Type::BorrowedRef {
            lifetime: Some("'a".to_string()),
            is_mutable: false,
            type_: Box::new(Type::Primitive("str".to_string())),
        };
        rkyv_roundtrip(&ty);

        // `Slice` and `Tuple` are tuple-variant fields with omit_bounds on the unnamed field,
        // which required special syntax (attribute inside the parentheses) to compile.
        let ty = Type::Slice(Box::new(Type::Tuple(vec![
            Type::Primitive("u32".to_string()),
            Type::Generic("T".to_string()),
        ])));
        rkyv_roundtrip(&ty);

        // `Path` has serialize_bounds/deserialize_bounds and omit_bounds on its `args` field.
        // `GenericArgs::AngleBracketed` exercises the full recursive chain: `Path` -> `GenericArgs` -> `GenericArg` -> `Type`.
        let path = Path {
            path: "std::option::Option".to_string(),
            id: Id(42),
            args: Some(Box::new(GenericArgs::AngleBracketed {
                args: vec![GenericArg::Type(Type::Primitive("u32".to_string()))],
                constraints: vec![],
            })),
        };
        rkyv_roundtrip(&path);

        // `FunctionPointer` is a `Box<FunctionPointer>` behind `omit_bounds` in `Type`.
        // It transitively contains `Type` via `FunctionSignature`, exercising the cycle from the other direction.
        let fp = Type::FunctionPointer(Box::new(FunctionPointer {
            sig: FunctionSignature {
                inputs: vec![("x".to_string(), Type::Primitive("i32".to_string()))],
                output: Some(Type::Primitive("bool".to_string())),
                is_c_variadic: false,
            },
            generic_params: vec![],
            header: FunctionHeader {
                is_const: false,
                is_unsafe: false,
                is_async: false,
                abi: Abi::Rust,
            },
        }));
        rkyv_roundtrip(&fp);
    }

    /// A helper function for roundtrip testing of rkyv-powered deserialization.
    fn rkyv_roundtrip<T>(value: &T)
    where
        T: PartialEq
            + Debug
            + Archive
            + for<'a> rkyv::Serialize<
                HighSerializer<AlignedVec, ArenaHandle<'a>, rkyv::rancor::Error>,
            >,
        T::Archived: rkyv::Deserialize<T, HighDeserializer<rkyv::rancor::Error>>
            + Debug
            + for<'a> CheckBytes<
                Strategy<Validator<ArchiveValidator<'a>, SharedValidator>, rkyv::rancor::Error>,
            >,
    {
        let bytes =
            rkyv::api::high::to_bytes_in::<_, rkyv::rancor::Error>(value, AlignedVec::new())
                .unwrap();
        let archived = rkyv::api::high::access::<T::Archived, rkyv::rancor::Error>(&bytes)
            .expect("Failed to access archived data");
        let deserialized: T = rkyv::api::deserialize_using::<_, _, rkyv::rancor::Error>(
            archived,
            &mut rkyv::de::Pool::new(),
        )
        .unwrap();
        assert_eq!(value, &deserialized);
    }
}