Documentation
use std::io::Error;
use std::io::Read;
use std::io::Write;

use crate::Decode;
use crate::Encode;
use crate::FixedSize;

impl<T: FixedSize> FixedSize for Option<T> {
    fn encoded_size() -> usize {
        1 + T::encoded_size()
    }
}

impl<T: Encode> Encode for Option<T> {
    fn encode<W: Write>(&self, mut w: W) -> Result<usize, Error> {
        match self {
            Some(v) => {
                let n = 1u8.encode(&mut w)? + v.encode(&mut w)?;
                Ok(n)
            }
            None => 0u8.encode(&mut w),
        }
    }
}

impl<T: Decode> Decode for Option<T> {
    fn decode<R: Read>(mut r: R) -> Result<Self, Error> {
        let tag = u8::decode(&mut r)?;
        match tag {
            0 => Ok(None),
            1 => {
                let v = T::decode(&mut r)?;
                Ok(Some(v))
            }
            _ => Err(Error::new(
                std::io::ErrorKind::InvalidData,
                format!("Invalid tag: {}", tag),
            )),
        }
    }
}

#[cfg(test)]
mod tests {
    use std::io;

    use crate::Decode;
    use crate::Encode;

    #[test]
    fn test_option_codec() -> Result<(), io::Error> {
        //
        {
            let a = Some("foo".to_string());

            let mut b = Vec::new();
            let n = a.encode(&mut b)?;
            assert_eq!(n, 8);
            assert_eq!(b, vec![1, 0, 0, 0, 3, 102, 111, 111]);

            let c = Option::<String>::decode(&mut b.as_slice())?;
            assert_eq!(c, a);
        }

        {
            let a = None::<String>;

            let mut b = Vec::new();
            let n = a.encode(&mut b)?;
            assert_eq!(n, 1);
            assert_eq!(b, vec![0]);

            let c = Option::<String>::decode(&mut b.as_slice())?;
            assert_eq!(c, a);
        }

        Ok(())
    }
}