Documentation
use std::{error::Error, fmt};

pub type Result<T> = std::result::Result<T, EmapiError>;

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum EmapiError {
  Range {
    name: &'static str,
    min: u64,
    max: u64,
    value: u64,
  },
  NotEnoughData {
    offset: usize,
    length: usize,
    data_len: usize,
  },
  FrameTooShort,
  MissingFrameHead,
  MissingFrameTail,
  FrameLengthMismatch {
    expected: usize,
    actual: usize,
  },
  CommandDataTooShort,
  BadFrameCrc {
    expected: u32,
    actual: u32,
  },
  PayloadLengthMismatch {
    expected: usize,
    actual: usize,
  },
  InvalidFrameDataLength,
  TruncatedTlvHeader,
  TruncatedTlvValue,
  InvalidTlvType {
    tag: u8,
    expected: &'static str,
    actual_len: usize,
  },
  InvalidUtf8,
  InvalidCapacity {
    name: &'static str,
  },
  Timeout {
    message: String,
  },
  Connection {
    message: String,
  },
  Protocol {
    message: String,
  },
}

impl fmt::Display for EmapiError {
  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    match self {
      Self::Range {
        name,
        min,
        max,
        value,
      } => write!(f, "{name} out of range: {value} is not in {min}..={max}"),
      Self::NotEnoughData {
        offset,
        length,
        data_len,
      } => write!(
        f,
        "not enough data: offset {offset}, length {length}, data length {data_len}"
      ),
      Self::FrameTooShort => f.write_str("frame is too short"),
      Self::MissingFrameHead => f.write_str("missing frame head"),
      Self::MissingFrameTail => f.write_str("missing frame tail"),
      Self::FrameLengthMismatch { expected, actual } => {
        write!(
          f,
          "frame length mismatch: expected {expected}, got {actual}"
        )
      }
      Self::CommandDataTooShort => f.write_str("command data is too short"),
      Self::BadFrameCrc { .. } => f.write_str("bad frame CRC"),
      Self::PayloadLengthMismatch { expected, actual } => {
        write!(
          f,
          "payload length mismatch: expected {expected}, got {actual}"
        )
      }
      Self::InvalidFrameDataLength => f.write_str("invalid frame data length"),
      Self::TruncatedTlvHeader => f.write_str("truncated TLV header"),
      Self::TruncatedTlvValue => f.write_str("truncated TLV value"),
      Self::InvalidTlvType {
        tag,
        expected,
        actual_len,
      } => write!(f, "tag 0x{tag:x} is not {expected}: length {actual_len}"),
      Self::InvalidUtf8 => f.write_str("invalid UTF-8 string"),
      Self::InvalidCapacity { name } => write!(f, "invalid {name} capacity"),
      Self::Timeout { message } => f.write_str(message),
      Self::Connection { message } => write!(f, "connection error: {message}"),
      Self::Protocol { message } => write!(f, "{message}"),
    }
  }
}

impl Error for EmapiError {}