// SPDX-License-Identifier: CC0-1.0

//! Example of creating an encoder that encodes a slice of encodable objects.

use bitcoin_consensus_encoding as encoding;
use encoding::{ArrayEncoder, BytesEncoder, CompactSizeEncoder, Encode, Encoder2, SliceEncoder};

fn main() {
    let v = vec![Inner::new(0xcafe_babe), Inner::new(0xdead_beef)];
    let b = vec![0xab, 0xcd];

    let adt = Adt::new(v, b);
    let encoded = encoding::encode_to_vec(&adt);

    let want = [0x02, 0xca, 0xfe, 0xba, 0xbe, 0xde, 0xad, 0xbe, 0xef, 0xab, 0xcd];
    assert_eq!(encoded, want);
}

/// Some abstract data type.
struct Adt {
    v: Vec<Inner>,
    b: Vec<u8>,
}

impl Adt {
    /// Constructs a new `Adt`.
    pub fn new(v: Vec<Inner>, b: Vec<u8>) -> Self { Self { v, b } }
}

encoding::encoder_newtype! {
    /// The encoder for the [`Adt`] type.
    pub struct AdtEncoder<'e>(Encoder2<Encoder2<CompactSizeEncoder, SliceEncoder<'e, Inner>>, BytesEncoder<'e>>);
}

impl Encode for Adt {
    type Encoder<'e>
        = AdtEncoder<'e>
    where
        Self: 'e;

    fn encoder(&self) -> Self::Encoder<'_> {
        let a = Encoder2::new(
            CompactSizeEncoder::new(self.v.len()),
            SliceEncoder::without_length_prefix(&self.v),
        );
        let b = BytesEncoder::without_length_prefix(self.b.as_ref());

        AdtEncoder::new(Encoder2::new(a, b))
    }
}

/// A simple data type to use as list item.
#[derive(Debug, Default, Clone)]
pub struct Inner(u32);

impl Inner {
    /// Constructs a new `Inner`.
    pub fn new(x: u32) -> Self { Self(x) }

    /// Returns some meaningful 4 byte array for this type.
    pub fn to_array(&self) -> [u8; 4] { self.0.to_be_bytes() }
}

encoding::encoder_newtype_exact! {
    /// The encoder for the [`Inner`] type.
    pub struct InnerEncoder<'e>(ArrayEncoder<4>);
}

impl Encode for Inner {
    type Encoder<'e> = InnerEncoder<'e>;
    fn encoder(&self) -> Self::Encoder<'_> {
        InnerEncoder::new(ArrayEncoder::without_length_prefix(self.to_array()))
    }
}
