/*
 * SPDX-FileCopyrightText: 2023 Inria
 * SPDX-FileCopyrightText: 2023 Sebastiano Vigna
 *
 * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
 */

//! Example of an enum with variants some of which depend on a parameter, and
//! some don't. See in particular the failed check of type hash.
//!
//! Please compile with the "schema" feature to see the schema output.

use epserde::{deser::DeserType, prelude::*, ser::SerType};

#[derive(Epserde, Debug, Clone, Copy)]
enum Data<T = Vec<i32>> {
    A,                        // Unit type
    B { a: usize, b: usize }, // Struct variant with two fields
    C(T),                     // Tuple variant with one parametric field
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    println!("Serializing type: {}", core::any::type_name::<Data>());
    println!(
        "Associated serialization type: {}",
        core::any::type_name::<SerType<Data>>()
    );
    println!();

    // Note that we need an explicit type annotation here, as the type of the
    // enum is not fully determined by the value--we need to know the type of
    // the parameter, which is assumed to be Vec<i32> by default.
    let data: Data = Data::A;
    let mut cursor = <AlignedCursor<Aligned16>>::new();

    // Serialize
    #[cfg(feature = "schema")]
    {
        let schema = unsafe { data.serialize_with_schema(&mut cursor)? };
        println!("{}", schema.to_csv_with_data(cursor.as_bytes()));
        println!();
    }
    #[cfg(not(feature = "schema"))]
    let _bytes_written = unsafe { data.serialize(&mut cursor)? };

    // Do a full-copy deserialization
    cursor.set_position(0);
    let full = unsafe { <Data>::deserialize_full(&mut cursor)? };
    println!(
        "Full-copy deserialization: returns the deserializing type {}",
        core::any::type_name::<Data>(),
    );
    println!("Value: {:x?}", full);

    println!();

    // Do an ε-copy deserialization
    let eps = unsafe { <Data>::deserialize_eps(cursor.as_bytes())? };
    println!(
        "ε-copy deserialization: returns the associated deserialization type {}",
        core::any::type_name::<DeserType<'_, Data>>(),
    );
    println!("Value: {:x?}", eps);

    // Now we give to the parameter a type different from the default one.
    let data: Data<Vec<usize>> = Data::A;
    let mut cursor = <AlignedCursor<Aligned16>>::new();

    // Serialize
    #[cfg(feature = "schema")]
    {
        let schema = unsafe { data.serialize_with_schema(&mut cursor)? };
        println!("{}", schema.to_csv_with_data(cursor.as_bytes()));
        println!();
    }
    #[cfg(not(feature = "schema"))]
    let _bytes_written = unsafe { data.serialize(&mut cursor)? };

    println!();

    println!("Deserializing with a different parameter type (will generate a type-hash error)...");
    println!();
    // When we try to deserialize without specifying again
    // the type, we get an error even if we just serialized
    // Data::A because the default value of the parameter
    // is different from the one we used.
    cursor.set_position(0);
    println!("Error in full-copy deserialization: {}", unsafe {
        <Data>::deserialize_full(&mut cursor).err().unwrap()
    });

    #[cfg(not(feature = "schema"))]
    println!("\nPlease compile with the \"schema\" feature to see the schema output");
    Ok(())
}
