//! Definitions of the tags used in the binary encoding.

use std::convert::{From, TryFrom};
use std::io;

/// Rust representation of tags used in the binary encoding.
#[derive(Debug, PartialEq, Eq)]
pub enum Tag {
    False,
    True,
    End,
    Annotation,
    Embedded,
    Ieee754,
    SignedInteger,
    String,
    ByteString,
    Symbol,
    Record,
    Sequence,
    Set,
    Dictionary,
}

/// Error value representing failure to decode a byte into a [Tag].
#[derive(Debug, PartialEq, Eq)]
pub struct InvalidTag(pub u8);

impl From<InvalidTag> for io::Error {
    fn from(v: InvalidTag) -> Self {
        io::Error::new(
            io::ErrorKind::InvalidData,
            format!("Invalid Preserves tag {}", v.0),
        )
    }
}

impl From<InvalidTag> for crate::error::Error {
    fn from(v: InvalidTag) -> Self {
        crate::error::Error::Io(v.into())
    }
}

impl TryFrom<u8> for Tag {
    type Error = InvalidTag;
    fn try_from(v: u8) -> Result<Self, Self::Error> {
        match v {
            0x80 => Ok(Self::False),
            0x81 => Ok(Self::True),
            0x84 => Ok(Self::End),
            0x85 => Ok(Self::Annotation),
            0x86 => Ok(Self::Embedded),
            0x87 => Ok(Self::Ieee754),
            0xb0 => Ok(Self::SignedInteger),
            0xb1 => Ok(Self::String),
            0xb2 => Ok(Self::ByteString),
            0xb3 => Ok(Self::Symbol),
            0xb4 => Ok(Self::Record),
            0xb5 => Ok(Self::Sequence),
            0xb6 => Ok(Self::Set),
            0xb7 => Ok(Self::Dictionary),
            _ => Err(InvalidTag(v)),
        }
    }
}

impl From<Tag> for u8 {
    fn from(v: Tag) -> Self {
        match v {
            Tag::False => 0x80,
            Tag::True => 0x81,
            Tag::End => 0x84,
            Tag::Annotation => 0x85,
            Tag::Embedded => 0x86,
            Tag::Ieee754 => 0x87,
            Tag::SignedInteger => 0xb0,
            Tag::String => 0xb1,
            Tag::ByteString => 0xb2,
            Tag::Symbol => 0xb3,
            Tag::Record => 0xb4,
            Tag::Sequence => 0xb5,
            Tag::Set => 0xb6,
            Tag::Dictionary => 0xb7,
        }
    }
}
