use std::{error::Error, num::NonZeroU32};
use crate::{AnyValueOrSpecific, IdentifierType};
#[derive(thiserror::Error, Debug, PartialEq)]
pub enum ParseError {
#[error("Terminal Response didn't start with '\\x1b_G' but instead '{0}'")]
NoStartSequence(String),
#[error("No final semicolon was provided to indicate end of options in the terminal response")]
NoFinalSemicolon,
#[error(
"A key-value option in a terminal response was unparseable; expected <char>=<char_or_number>"
)]
MalformedResponseOption(String),
#[error("An ID of type {ty:?} was sent, but no relevant ID was seen in the terminal response")]
NoResponseId {
ty: IdentifierType
},
#[error(
"The ID of type {ty:?} seen in the terminal response was different from the one we sent (was {found}, expected {expected})"
)]
DifferentIdInResponse {
ty: IdentifierType,
found: String,
expected: AnyValueOrSpecific<NonZeroU32>
},
#[error(
"The terminal response provided a id of type {ty:?}, but we didn't sent one as part of the request (with a value of '{value}')"
)]
IdInResponseButNotInRequest {
ty: IdentifierType,
value: String
},
#[error(
"We expected to receive an ID of type {ty:?} with value {val} in the response, but saw no id of that type"
)]
MissingId {
ty: IdentifierType,
val: AnyValueOrSpecific<NonZeroU32>
},
#[error("Unknown terminal response key '{0}'")]
UnknownResponseKey(String),
#[error(
"The terminal returned an error, but it was unparseable; was '{0}' but expected <code>:<reason>"
)]
MalformedError(String),
#[error(
"The terminal resported an error with an unknown error code '{code}' and accompanying reason '{reason}'"
)]
UnknownErrorCode {
code: String,
reason: String
}
}
#[non_exhaustive]
#[derive(thiserror::Error, Debug, PartialEq)]
pub enum TerminalError {
#[error("No such entity was found: {0}")]
NoEntity(String),
#[error("Invalid argument: {0}")]
InvalidArgument(String),
#[error("Bad file: {0}")]
BadFile(String),
#[error("No Data: {0}")]
NoData(String),
#[error("File Too Large: {0}")]
FileTooLarge(String)
}
pub struct UnknownErrorCode<'a>(pub(crate) &'a str);
impl<'a> TryFrom<(&'a str, &str)> for TerminalError {
type Error = UnknownErrorCode<'a>;
fn try_from(value: (&'a str, &str)) -> Result<Self, Self::Error> {
let s = value.1.to_owned();
Ok(match value.0 {
"ENOENT" => Self::NoEntity(s),
"EINVAL" => Self::InvalidArgument(s),
"EBADF" => Self::BadFile(s),
"ENODATA" => Self::NoData(s),
"EFBIG" => Self::FileTooLarge(s),
x => return Err(UnknownErrorCode(x))
})
}
}
#[derive(thiserror::Error, Debug)]
pub enum TransmitError<InputError: Error> {
#[error("Couldn't write to writer: {0}")]
Writing(std::io::Error),
#[error("Couldn't read input after transmitting: {0}")]
ReadingInput(InputError),
#[error("Couldn't parse response from the terminal: {0}")]
ParsingResponse(ParseError),
#[error("The backing terminal returned an error: {0}")]
Terminal(TerminalError)
}
impl<E: PartialEq + Error> PartialEq for TransmitError<E> {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Writing(e1), Self::Writing(e2)) => e1.to_string() == e2.to_string(),
(Self::ReadingInput(e1), Self::ReadingInput(e2)) => e1 == e2,
(Self::ParsingResponse(e1), Self::ParsingResponse(e2)) => e1 == e2,
(Self::Terminal(e1), Self::Terminal(e2)) => e1 == e2,
(
Self::Writing(_)
| Self::ReadingInput(_)
| Self::ParsingResponse(_)
| Self::Terminal(_),
_
) => false
}
}
}