mod alert;
mod builder;
pub mod check;
mod info;
mod reply;
pub use alert::*;
pub(crate) use builder::ResponseBuilder;
pub use info::*;
pub use reply::*;
use crate::error::AsciiCheckError;
use crate::ascii::{command::Target, packet};
pub trait Response:
std::fmt::Display
+ std::fmt::Debug
+ std::convert::TryFrom<AnyResponse>
+ std::convert::Into<AnyResponse>
+ private::Sealed
{
fn target(&self) -> Target;
fn id(&self) -> Option<u8>;
fn data(&self) -> &str;
}
pub trait SpecificResponse:
Response + std::convert::TryFrom<AnyResponse, Error = AnyResponse>
{
}
pub trait ResponseWithWarning: Response {
fn warning(&self) -> Warning;
}
impl ResponseWithWarning for Reply {
fn warning(&self) -> Warning {
self.warning()
}
}
impl ResponseWithWarning for Alert {
fn warning(&self) -> Warning {
self.warning()
}
}
pub trait ResponseWithStatus: Response {
fn status(&self) -> Status;
}
impl ResponseWithStatus for Reply {
fn status(&self) -> Status {
self.status()
}
}
impl ResponseWithStatus for Alert {
fn status(&self) -> Status {
self.status()
}
}
pub trait ResponseWithFlag: Response {
fn flag(&self) -> Flag;
}
impl ResponseWithFlag for Reply {
fn flag(&self) -> Flag {
self.flag()
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum Status {
Busy,
Idle,
}
impl Status {
pub(crate) const BUSY_STR: &'static str = "BUSY";
pub(crate) const IDLE_STR: &'static str = "IDLE";
}
impl std::fmt::Display for Status {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Status::Busy => write!(f, "{}", Status::BUSY_STR),
Status::Idle => write!(f, "{}", Status::IDLE_STR),
}
}
}
#[derive(Copy, Clone, Eq, Hash)]
pub struct Warning([u8; 2]);
impl std::fmt::Debug for Warning {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("Warning")
.field(&std::str::from_utf8(&self.0).unwrap())
.finish()
}
}
impl Warning {
pub const NONE: Warning = Warning(*b"--");
pub fn is_none(&self) -> bool {
*self == Warning::NONE
}
pub fn is_some(&self) -> bool {
!self.is_none()
}
pub fn is_fault(&self) -> bool {
self.0[0] == b'F'
}
pub fn is_warning(&self) -> bool {
self.0[0] == b'W'
}
pub fn is_notice(&self) -> bool {
self.0[0] == b'N'
}
}
impl AsRef<[u8]> for Warning {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl std::convert::From<[u8; 2]> for Warning {
fn from(bytes: [u8; 2]) -> Self {
Warning(bytes)
}
}
impl std::convert::From<&[u8; 2]> for Warning {
fn from(bytes: &[u8; 2]) -> Self {
Warning(*bytes)
}
}
impl<'a> std::convert::TryFrom<&'a [u8]> for Warning {
type Error = &'a [u8];
fn try_from(bytes: &'a [u8]) -> Result<Self, Self::Error> {
use std::convert::TryInto as _;
if bytes.len() == 2 {
Ok(Warning(bytes.try_into().unwrap()))
} else {
Err(bytes)
}
}
}
impl<'a> std::convert::TryFrom<&'a str> for Warning {
type Error = &'a str;
fn try_from(s: &'a str) -> Result<Self, Self::Error> {
TryFrom::<&'a [u8]>::try_from(s.as_bytes()).map_err(|_| s)
}
}
impl std::fmt::Display for Warning {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", std::str::from_utf8(&self.0).unwrap())
}
}
impl PartialEq<[u8]> for Warning {
fn eq(&self, other: &[u8]) -> bool {
&self.0[..] == other
}
}
impl<T> PartialEq<T> for Warning
where
T: AsRef<[u8]>,
{
fn eq(&self, other: &T) -> bool {
&self.0[..] == other.as_ref()
}
}
#[derive(Debug, Clone, PartialEq)]
struct Header {
pub address: u8,
pub axis: u8,
pub id: Option<u8>,
}
impl std::fmt::Display for Header {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:02} {}", self.address, self.axis)?;
if let Some(id) = self.id {
write!(f, " {id:02}")?;
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum AnyResponse {
Reply(Reply),
Info(Info),
Alert(Alert),
}
impl AnyResponse {
pub(crate) fn try_from_packet<T>(packet: &packet::Packet<T>) -> Result<Self, &packet::Packet<T>>
where
T: AsRef<[u8]>,
{
Reply::try_from_packet(packet)
.map(From::from)
.or_else(|packet| Info::try_from_packet(packet).map(From::from))
.or_else(|packet| Alert::try_from_packet(packet).map(From::from))
}
pub fn kind(&self) -> Kind {
match self {
AnyResponse::Reply(..) => Kind::Reply,
AnyResponse::Info(..) => Kind::Info,
AnyResponse::Alert(..) => Kind::Alert,
}
}
pub fn data(&self) -> &str {
match self {
AnyResponse::Reply(reply) => reply.data(),
AnyResponse::Info(info) => info.data(),
AnyResponse::Alert(alert) => alert.data(),
}
}
pub fn status(&self) -> Option<Status> {
match self {
AnyResponse::Reply(reply) => Some(reply.status()),
AnyResponse::Info(..) => None,
AnyResponse::Alert(alert) => Some(alert.status()),
}
}
}
impl std::convert::From<std::convert::Infallible> for AnyResponse {
fn from(_: std::convert::Infallible) -> Self {
unreachable!();
}
}
impl std::convert::From<Reply> for AnyResponse {
fn from(item: Reply) -> Self {
AnyResponse::Reply(item)
}
}
impl std::convert::From<Info> for AnyResponse {
fn from(item: Info) -> Self {
AnyResponse::Info(item)
}
}
impl std::convert::From<Alert> for AnyResponse {
fn from(item: Alert) -> Self {
AnyResponse::Alert(item)
}
}
impl std::fmt::Display for AnyResponse {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AnyResponse::Reply(reply) => reply.fmt(f),
AnyResponse::Info(info) => info.fmt(f),
AnyResponse::Alert(alert) => alert.fmt(f),
}
}
}
impl Response for AnyResponse {
fn target(&self) -> Target {
match self {
AnyResponse::Reply(reply) => reply.target(),
AnyResponse::Info(info) => info.target(),
AnyResponse::Alert(alert) => alert.target(),
}
}
fn id(&self) -> Option<u8> {
match self {
AnyResponse::Reply(reply) => reply.id(),
AnyResponse::Info(info) => info.id(),
AnyResponse::Alert(alert) => alert.id(),
}
}
fn data(&self) -> &str {
match self {
AnyResponse::Reply(reply) => reply.data(),
AnyResponse::Info(info) => info.data(),
AnyResponse::Alert(alert) => alert.data(),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum Kind {
Reply,
Info,
Alert,
}
impl std::fmt::Display for Kind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Kind::Reply => write!(f, "Reply"),
Kind::Info => write!(f, "Info"),
Kind::Alert => write!(f, "Alert"),
}
}
}
impl TryFrom<packet::PacketKind> for Kind {
type Error = packet::PacketKind;
fn try_from(other: packet::PacketKind) -> Result<Self, Self::Error> {
use packet::PacketKind as PK;
match other {
PK::Command => Err(other),
PK::Reply => Ok(Kind::Reply),
PK::Info => Ok(Kind::Info),
PK::Alert => Ok(Kind::Alert),
}
}
}
mod private {
use super::{
check::{self, Check as _},
Alert, AnyResponse, AsciiCheckError, Info, Reply,
};
pub trait Sealed: DataMut + CommonChecks {}
impl Sealed for Reply {}
impl Sealed for Alert {}
impl Sealed for Info {}
impl Sealed for AnyResponse {}
pub trait DataMut {
fn data_mut(&mut self) -> &mut String;
}
impl DataMut for AnyResponse {
fn data_mut(&mut self) -> &mut String {
match self {
AnyResponse::Reply(reply) => reply.data_mut(),
AnyResponse::Info(info) => info.data_mut(),
AnyResponse::Alert(alert) => alert.data_mut(),
}
}
}
impl DataMut for Reply {
fn data_mut(&mut self) -> &mut String {
&mut self.0.data
}
}
impl DataMut for Info {
fn data_mut(&mut self) -> &mut String {
&mut self.0.data
}
}
impl DataMut for Alert {
fn data_mut(&mut self) -> &mut String {
&mut self.0.data
}
}
pub trait CommonChecks: Sized {
fn strict() -> fn(Self) -> Result<Self, AsciiCheckError<Self>>;
fn minimal() -> fn(Self) -> Result<Self, AsciiCheckError<Self>>;
}
impl CommonChecks for AnyResponse {
fn strict() -> fn(Self) -> Result<Self, AsciiCheckError<Self>> {
|response| match response {
AnyResponse::Reply(reply) => {
Reply::strict()(reply).map(From::from).map_err(From::from)
}
AnyResponse::Info(info) => Info::strict()(info).map(From::from).map_err(From::from),
AnyResponse::Alert(alert) => {
Alert::strict()(alert).map(From::from).map_err(From::from)
}
}
}
fn minimal() -> fn(Self) -> Result<Self, AsciiCheckError<Self>> {
|response| match response {
AnyResponse::Reply(reply) => {
Reply::minimal()(reply).map(From::from).map_err(From::from)
}
AnyResponse::Info(info) => {
Info::minimal()(info).map(From::from).map_err(From::from)
}
AnyResponse::Alert(alert) => {
Alert::minimal()(alert).map(From::from).map_err(From::from)
}
}
}
}
impl CommonChecks for Reply {
fn strict() -> fn(Self) -> Result<Self, AsciiCheckError<Self>> {
|reply| check::flag_ok_and(check::warning_is_none()).check(reply)
}
fn minimal() -> fn(Self) -> Result<Self, AsciiCheckError<Self>> {
|reply| check::flag_ok_and(check::warning_below_fault()).check(reply)
}
}
impl CommonChecks for Info {
fn strict() -> fn(Self) -> Result<Self, AsciiCheckError<Self>> {
Ok
}
fn minimal() -> fn(Self) -> Result<Self, AsciiCheckError<Self>> {
Ok
}
}
impl CommonChecks for Alert {
fn strict() -> fn(Self) -> Result<Self, AsciiCheckError<Self>> {
|alert| check::warning_is_none().check(alert)
}
fn minimal() -> fn(Self) -> Result<Self, AsciiCheckError<Self>> {
|alert| check::warning_below_fault().check(alert)
}
}
}