use std::fmt; use crate::sharry; #[derive(Debug, thiserror::Error)] pub enum Parameter { #[error("given URI {0:?}")] Uri(sharry::Uri), #[error("given Alias ID {0:?}")] AliasID(sharry::AliasID), #[error("stored Share ID {0:?}")] ShareID(sharry::ShareID), #[error("stored {0:?}")] FileID(sharry::FileID), } // a helper to generate all the `From for Parameter` impls macro_rules! impl_param_from { // $typ: the source type; $var: the enum‐variant name ( $( $typ:path => $var:ident ),* $(,)? ) => { $( impl From<$typ> for Parameter { fn from(value: $typ) -> Self { Self::$var(value) } } )* }; } impl_param_from! { sharry::Uri => Uri, sharry::AliasID => AliasID, sharry::ShareID => ShareID, sharry::FileID => FileID, } impl Parameter { fn is_fatal(&self) -> bool { self.is_uri() || self.is_alias_id() } #[must_use] pub fn is_uri(&self) -> bool { matches!(self, Self::Uri(_)) } #[must_use] pub fn is_alias_id(&self) -> bool { matches!(self, Self::AliasID(_)) } #[must_use] pub fn is_share_id(&self) -> bool { matches!(self, Self::ShareID(_)) } #[must_use] pub fn is_file_id(&self) -> bool { matches!(self, Self::FileID(_)) } } #[derive(Debug, thiserror::Error)] pub enum Error { #[error(transparent)] StdIo(#[from] std::io::Error), #[error("Response error: {0}")] Response(String), #[error("Invalid {0}")] InvalidParameter(Parameter), #[error("Mismatch, expected {expected:?} but got {actual:?}")] Mismatch { expected: String, actual: String }, #[error("Unknown error: {0}")] Unknown(String), } pub type Result = std::result::Result; // a helper to generate all the `From for Error` impls macro_rules! impl_error_from { // $typ: the source type ( $( $typ:path ),* $(,)? ) => { $( // // implement for values // impl From<$typ> for Error { // fn from(value: $typ) -> Self { // Self::InvalidParameter(value.into()) // } // } // implement for references impl From<&$typ> for Error { fn from(value: &$typ) -> Self { Self::InvalidParameter(value.clone().into()) } } )* }; } impl_error_from! { sharry::Uri, sharry::AliasID, sharry::ShareID, sharry::FileID, } #[allow(clippy::needless_pass_by_value)] fn into_string(val: impl ToString) -> String { val.to_string() } impl Error { pub fn res_status_check(actual: T, expected: T) -> Result<()> where T: PartialEq + fmt::Display + Copy, { if actual == expected { Ok(()) } else { Err(Self::Response(format!( "unexpected status: {actual} (expected {expected})" ))) } } pub fn response(e: impl ToString) -> Self { Self::Response(into_string(e)) } pub fn mismatch(expected: impl ToString, actual: impl ToString) -> Result { Err(Self::Mismatch { expected: into_string(expected), actual: into_string(actual), }) } #[must_use] pub fn is_stdio_kind(&self, kind: std::io::ErrorKind) -> bool { if let Self::StdIo(e) = self { e.kind() == kind } else { false } } pub fn is_mismatch(&self, want_expected: E, want_actual: A) -> bool where E: AsRef, A: AsRef, { if let Self::Mismatch { expected, actual } = self { expected == want_expected.as_ref() && actual == want_actual.as_ref() } else { false } } #[must_use] pub fn response_contains(&self, pat: &str) -> bool { if let Self::Response(r) = self { r.contains(pat) } else { false } } #[must_use] pub fn get_invalid_param(&self) -> Option<&Parameter> { if let Self::InvalidParameter(p) = self { Some(p) } else { None } } #[must_use] pub fn is_fatal(&self) -> bool { match self { Self::InvalidParameter(p) => p.is_fatal(), Self::Mismatch { .. } | Self::Unknown(_) => true, _ => false, } } } #[macro_export] macro_rules! error_response { // Match a format string plus optional arguments ($fmt:expr $(, $arg:expr )* $(,)?) => { // Expand to constructing the Error::Response variant, // wrapping a `format!(...)` call Error::Response(format!($fmt $(, $arg )*)) }; }