sokoban-elements 0.2.1

Parsing and writing Sokoban, Hexoban, Trioban and Octoban file formats.
Documentation
use std::error::Error;
use std::fmt::{Display, Formatter};
use std::num::ParseIntError;

use crate::sokoban_plus::BoxID;

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub(super) enum SokobanPlusInputKind {
    Boxorder,
    Goalorder,
}

/// Variants of input for [`SokobanPlusSyntaxError`]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum SokobanPlusSyntaxErrorKind {
    ParseIntError,
    TooManyPlusIDs,
}

#[derive(Debug, Clone, PartialEq, Eq)]
struct ErrorPayload {
    pos:        usize,
    input_kind: SokobanPlusInputKind,
    kind:       SokobanPlusSyntaxErrorKind,
    source:     Option<ParseIntError>,
}

/// [`std::Error`](std::error::Error) when parsing [`SokobanPlus`](crate::SokobanPlus).
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SokobanPlusSyntaxError(Box<ErrorPayload>);

impl SokobanPlusSyntaxError {
    pub(super) fn new(
        pos: usize, input_kind: SokobanPlusInputKind, kind: SokobanPlusSyntaxErrorKind,
        source: Option<ParseIntError>,
    ) -> Self {
        Self(Box::new(ErrorPayload {
            pos,
            input_kind,
            kind,
            source,
        }))
    }

    pub fn kind(&self) -> SokobanPlusSyntaxErrorKind { self.0.kind }
}

impl Display for SokobanPlusSyntaxError {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        let input_kind_s = match self.0.input_kind {
            SokobanPlusInputKind::Boxorder => "boxorder",
            SokobanPlusInputKind::Goalorder => "goalorder",
        };

        match self.0.kind {
            SokobanPlusSyntaxErrorKind::ParseIntError => {
                write!(
                    f,
                    "failed parsing Sokoban+ IDs from {} at {}",
                    input_kind_s, self.0.pos
                )
            }

            SokobanPlusSyntaxErrorKind::TooManyPlusIDs => {
                write!(
                    f,
                    "Sokoban+ {} can't assign more Sokoban+ IDs than maximum allowed number of {} ({})",
                    input_kind_s,
                    match self.0.input_kind {
                        SokobanPlusInputKind::Boxorder => "boxes",
                        SokobanPlusInputKind::Goalorder => "goals",
                    },
                    BoxID::MAX
                )
            }
        }
    }
}

impl Error for SokobanPlusSyntaxError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        match &self.0.source {
            Some(ref_box_dyn) => Some(ref_box_dyn),
            None => None,
        }
    }
}