From 78e88cb602534c5bcade5c71b8f20abac89dfa23 Mon Sep 17 00:00:00 2001 From: Mikhail Katychev Date: Sat, 4 May 2024 00:04:30 +0200 Subject: [PATCH] Added `attachment::Dbg` (#12) Approved by: @gshuflin, @jkleinknox, @jsgoyette ============ Introduced `attachment::Dbg` to avoid prematurely allocating `String`s through early `format!` calls. * Moved most `Reportable` method implementations out of `reportable!` and into blanket implementations under the trait proper. * Removed `ResultAttachExt` --- src/attachment.rs | 98 ++++++++++-------- src/context.rs | 4 +- src/lib.rs | 251 ++++++++++++++-------------------------------- 3 files changed, 136 insertions(+), 217 deletions(-) diff --git a/src/attachment.rs b/src/attachment.rs index b1fdb77..cdb48a8 100644 --- a/src/attachment.rs +++ b/src/attachment.rs @@ -1,4 +1,4 @@ -use std::time::Duration; +use std::{any, fmt, time::Duration}; use tracing::error; @@ -7,22 +7,37 @@ pub use thiserror; use crate::reportable; -pub trait Display: std::fmt::Display + std::fmt::Debug + Send + Sync + 'static {} +pub trait Display: fmt::Display + fmt::Debug + Send + Sync + 'static {} -impl Display for A where A: std::fmt::Display + std::fmt::Debug + Send + Sync + 'static {} +impl Display for A where A: fmt::Display + fmt::Debug + Send + Sync + 'static {} -pub trait Debug: std::fmt::Debug + Send + Sync + 'static {} +pub trait Debug: fmt::Debug + Send + Sync + 'static {} -impl Debug for A where A: std::fmt::Debug + Send + Sync + 'static {} +impl Debug for A where A: fmt::Debug + Send + Sync + 'static {} + +// used to wrap types that only implement `std::fmt::Debug` +#[derive(Debug)] +pub struct Dbg(pub A); + +impl fmt::Display for Dbg { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self.0) + } +} // simple key-value pair attachment -#[derive(Debug, PartialEq, thiserror::Error)] -#[error("{0}: {1}")] +#[derive(Debug, PartialEq)] pub struct KeyValue(pub K, pub V); -impl KeyValue { - pub fn dbg(key: impl Debug, value: impl Debug) -> Self { - Self(format!("{key:?}"), format!("{value:?}")) +impl fmt::Display for KeyValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}: {}", self.0, self.1) + } +} + +impl KeyValue> { + pub fn dbg(key: K, value: V) -> Self { + Self(key, Dbg(value)) } } @@ -48,8 +63,8 @@ pub struct Field { status: S, } -impl std::fmt::Display for Field { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl fmt::Display for Field { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}: {}", self.id, self.status) } } @@ -67,22 +82,22 @@ pub struct Type(&'static str); impl Type { // const fn when type_name is const fn in stable pub fn of() -> Self { - Self(std::any::type_name::()) + Self(any::type_name::()) } pub fn of_val(_val: &T) -> Self { - Self(std::any::type_name::()) + Self(any::type_name::()) } } -impl std::fmt::Display for Type { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl fmt::Display for Type { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "<{}>", self.0) } } -impl std::fmt::Debug for Type { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl fmt::Debug for Type { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("Type").field(&self.0).finish() } } @@ -132,8 +147,8 @@ enum Symbol { Space, } -impl std::fmt::Display for Symbol { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl fmt::Display for Symbol { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let utf8 = match self { Self::Vertical => "\u{2502}", // │ Self::VerticalRight => "\u{251c}", // ├ @@ -144,36 +159,37 @@ impl std::fmt::Display for Symbol { Self::CurveRight => "\u{2570}", // ╰ Self::Space => " ", }; - write!(f, "{}", utf8) + write!(f, "{utf8}") } } -impl std::fmt::Display for Expectation { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let cr = Symbol::CurveRight; - let hl = Symbol::HorizontalLeft; - write!( - f, - "{}\n{cr}{hl}{}", - KeyValue("expected", &self.expected), - KeyValue("actual", &self.actual) - ) +impl fmt::Display for Expectation { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let curve_right = Symbol::CurveRight; + let horizontal_left = Symbol::HorizontalLeft; + let expected = KeyValue("expected", &self.expected); + let actual = KeyValue("actual", &self.actual); + // "expected": expected + // ╰╴"actual": actual + write!(f, "{expected}\n{curve_right}{horizontal_left}{actual}") } } -impl std::fmt::Display for FromTo { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let cr = Symbol::CurveRight; - let hl = Symbol::HorizontalLeft; +impl fmt::Display for FromTo { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let curve_right = Symbol::CurveRight; + let horizontal_left = Symbol::HorizontalLeft; let from = KeyValue("from", &self.0); let to = KeyValue("to", &self.1); - write!(f, "{from}\n{cr}{hl}{to}",) + // "from": from + // ╰╴"to": to + write!(f, "{from}\n{curve_right}{horizontal_left}{to}") } } #[derive(Debug)] pub struct DisplayDuration(pub Duration); -impl std::fmt::Display for DisplayDuration { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl fmt::Display for DisplayDuration { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", hms_string(self.0)) } } @@ -221,11 +237,11 @@ pub fn hms_string(duration: Duration) -> String { // this is meant to explicitly indicate // that the underlying `A` is being -// used as an index key for getter methods +// used as an index key for getter methods in a collection // such as `HashMap` keys and `Vec` indices #[derive(Debug, thiserror::Error)] -#[error("Index[{0}]")] -pub struct Index(pub I); +#[error("idx [{0}: {}]", std::any::type_name::())] +pub struct Index(pub I); #[cfg(test)] mod test { diff --git a/src/context.rs b/src/context.rs index 76d06e5..e466494 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,4 +1,4 @@ -use std::{path::Path, time::Duration}; +use std::{any, path::Path, time::Duration}; use error_stack::Context; @@ -163,7 +163,7 @@ impl InvalidInput { #[track_caller] pub fn type_name() -> Report { - let type_name = std::any::type_name::(); + let type_name = any::type_name::(); Report::new(Self).attach_printable(format!("type: {type_name}")) } diff --git a/src/lib.rs b/src/lib.rs index 4054687..78dfe30 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,7 +12,7 @@ pub mod grpc; pub use attachment::{Expectation, Field, Index, KeyValue, Type}; -use attachment::{Debug, Display}; +use attachment::{Dbg, Debug, Display}; pub use context::*; // TODO we'll have to do a builder pattern here at @@ -35,27 +35,47 @@ pub trait Reportable where Self: Sized + Context, { - fn report(ctx: C) -> Report; + fn value() -> Self; + fn report(ctx: C) -> Report { + Report::new(ctx).change_context(Self::value()) + } // TODO // fn report_dyn_err(err: impl std::error::Error + 'static + Send + Sync) // -> Report; + #[track_caller] fn attach(value: A) -> Report where - A: Display; + A: Display, + { + Report::new(Self::value()).attach_printable(value) + } + #[track_caller] fn attach_dbg(value: A) -> Report where - A: Debug; + A: Debug, + { + Self::attach(Dbg(value)) + } + #[track_caller] fn with_kv(key: K, value: V) -> Report where K: Display, - V: Display; + V: Display, + { + Self::attach(KeyValue(key, value)) + } + #[track_caller] fn with_kv_dbg(key: K, value: V) -> Report where - K: Debug, - V: Debug; - fn with_field_status(key: &'static str, status: S) -> Report; - - fn value() -> Self; + K: Display, + V: Debug, + { + Self::attach(KeyValue::dbg(key, value)) + } + #[track_caller] + fn with_field_status(key: &'static str, status: S) -> Report { + Self::attach(Field::new(key, status)) + } #[track_caller] fn expected_actual(expected: A, actual: A) -> Report { @@ -102,22 +122,6 @@ impl ReportAs for Result { } } -impl ReportAs for &'static str { - #[inline] - #[track_caller] - fn report_as(self) -> Result> { - Err(Report::new(C::value()).attach_printable(self)) - } -} - -impl ReportAs for String { - #[inline] - #[track_caller] - fn report_as(self) -> Result> { - Err(Report::new(C::value()).attach_printable(self)) - } -} - pub trait IntoContext { fn into_ctx(self) -> Report; } @@ -191,49 +195,6 @@ macro_rules! reportable { $crate::Report::new(ctx).change_context(Self) } - #[track_caller] - fn attach(value: A) -> $crate::Report - where - A: $crate::attachment::Display, - { - $crate::Report::new(Self).attach_printable(value) - } - - #[track_caller] - fn attach_dbg(value: A) -> $crate::Report - where - A: $crate::attachment::Debug, - { - $crate::Report::new(Self).attach_printable(format!("{value:?}")) - } - - #[track_caller] - fn with_kv(key: K, value: V) -> $crate::Report - where - K: $crate::attachment::Display, - V: $crate::attachment::Display, - { - use $crate::AttachExt; - $crate::Report::new(Self).attach_kv(key, value) - } - #[track_caller] - fn with_kv_dbg(key: K, value: V) -> $crate::Report - where - K: $crate::attachment::Debug, - V: $crate::attachment::Debug, - { - use $crate::AttachExt; - $crate::Report::new(Self).attach_kv_dbg(key, value) - } - #[track_caller] - fn with_field_status(key: &'static str, status: S) -> $crate::Report - where - S: $crate::attachment::Display, - { - $crate::Report::new(Self) - .attach_printable($crate::attachment::Field::new(key, status)) - } - fn value() -> Self { $context } @@ -248,7 +209,7 @@ pub trait AttachExt { V: Display; fn attach_kv_dbg(self, key: K, value: V) -> Self where - K: Debug, + K: Display, V: Debug; fn attach_field_status(self, name: &'static str, status: S) -> Self @@ -281,7 +242,7 @@ impl AttachExt for Report { #[track_caller] fn attach_kv_dbg(self, key: K, value: V) -> Self where - K: Debug, + K: Display, V: Debug, { self.attach_printable(KeyValue::dbg(key, value)) @@ -302,7 +263,7 @@ impl AttachExt for Report { where A: Debug, { - self.attach_printable(format!("{value:?}")) + self.attach_printable(Dbg(value)) } } @@ -324,7 +285,7 @@ impl AttachExt for Result> { #[track_caller] fn attach_kv_dbg(self, key: K, value: V) -> Self where - K: Debug, + K: Display, V: Debug, { match self { @@ -353,82 +314,7 @@ impl AttachExt for Result> { { match self { Ok(ok) => Ok(ok), - Err(report) => Err(report.attach_printable(format!("{value:?}"))), - } - } -} - -pub trait ResultAttachExt: ResultExt { - fn attach_kv(self, key: K, value: V) -> Result> - where - K: Display, - V: Display; - fn attach_kv_dbg(self, key: K, value: V) -> Result> - where - K: Debug, - V: Debug; - - fn attach_field_status( - self, - name: &'static str, - status: S, - ) -> Result> - where - S: Display; - fn attach_dbg(self, value: A) -> Result> - where - A: Debug; -} - -impl ResultAttachExt for Result -where - C: Context, -{ - #[inline] - #[track_caller] - fn attach_kv(self, key: K, value: V) -> Result> - where - K: Display, - V: Display, - { - match self { - Ok(ok) => Ok(ok), - Err(e) => Err(Report::from(e).attach_printable(KeyValue(key, value))), - } - } - #[inline] - #[track_caller] - fn attach_kv_dbg(self, key: K, value: V) -> Result> - where - K: Debug, - V: Debug, - { - match self { - Ok(ok) => Ok(ok), - Err(e) => Err(Report::from(e).attach_printable(KeyValue::dbg(key, value))), - } - } - - #[inline] - #[track_caller] - fn attach_field_status(self, name: &'static str, status: S) -> Result> - where - S: Display, - { - match self { - Ok(ok) => Ok(ok), - Err(e) => Err(Report::from(e).attach_printable(Field::new(name, status))), - } - } - #[inline] - #[track_caller] - fn attach_dbg(self, value: A) -> Result> - where - A: Debug, - { - match self { - Ok(ok) => Ok(ok), - Err(e) => Err(Report::from(e).attach_printable(format!("{value:?}"))), + Err(report) => Err(report.attach_printable(Dbg(value))), } } } @@ -463,7 +349,7 @@ where #[track_caller] fn log_err(self) { if let Err(e) = self { - error!(err = ?e); + error!(message = ?e); } } @@ -479,7 +365,7 @@ where } #[inline] #[track_caller] - fn and_log(self, level: Level) -> Result { + fn and_log(self, level: Level) -> Self { if let Err(err) = &self { match level { Level::TRACE => trace!(?err), @@ -494,16 +380,16 @@ where #[inline] #[track_caller] - fn and_log_err(self) -> Result { + fn and_log_err(self) -> Self { if let Err(e) = &self { - error!(err = ?e); + error!(message = ?e); } self } #[inline] #[track_caller] - fn and_attached_err(self, attachment: A) -> Result + fn and_attached_err(self, attachment: A) -> Self where A: fmt::Debug + Send + Sync + 'static, { @@ -515,7 +401,7 @@ where #[inline] #[track_caller] - fn on_err(self, op: impl FnOnce()) -> Result { + fn on_err(self, op: impl FnOnce()) -> Self { op(); self } @@ -558,7 +444,7 @@ pub trait FromReport { /// USAGE: /// * `impl From $(as $context:path)*> for OurError::Report(OurReportError)` /// - Implements `From where E: ToReport<_>` for errors that implement [`ToReport`] -/// * impl From>> for OurError::Report(TransactionError) +/// * `impl From>> for OurError::Report(TransactionError)` /// - Implements `From>` for `Report` /// * `impl From for OurError::Report(TransactionError)` /// - Implements `From` for `Report` @@ -747,26 +633,42 @@ where } impl ToReport for Report { - fn to_report(self) -> Report { + fn to_report(self) -> Self { self } } /// Used to produce [`NotFound`] reports from an [`Option`] -pub trait OptionReport { - type Some; - fn expect_or(self) -> Result>; - fn expect_kv(self, key: K, value: V) -> Result> +pub trait OptionReport +where + Self: Sized, +{ + fn expect_or(self) -> Result>; + fn expect_kv(self, key: K, value: V) -> Result> where K: Display, V: Display; - fn expect_field(self, field: &'static str) -> Result>; - fn expect_by(self, key: K) -> Result>; -} + fn expect_field(self, field: &'static str) -> Result>; -impl OptionReport for Option { - type Some = T; + #[inline] + #[track_caller] + fn expect_kv_dbg(self, key: K, value: V) -> Result> + where + K: Display, + V: Debug, + { + self.expect_kv(key, Dbg(value)) + } + #[inline] + #[track_caller] + fn expect_by(self, key: K) -> Result> { + self.expect_kv(Index(key), ty!(T)) + } +} + +impl OptionReport for Option { + #[inline] #[track_caller] fn expect_or(self) -> Result> { // TODO #[track_caller] on closure @@ -778,6 +680,7 @@ impl OptionReport for Option { } } + #[inline] #[track_caller] fn expect_kv(self, key: K, value: V) -> Result> where @@ -790,6 +693,7 @@ impl OptionReport for Option { } } + #[inline] #[track_caller] fn expect_field(self, field: &'static str) -> Result> { match self { @@ -797,14 +701,6 @@ impl OptionReport for Option { None => Err(NotFound::with_field(field)), } } - - #[track_caller] - fn expect_by(self, key: K) -> Result> { - match self { - Some(v) => Ok(v), - None => Err(NotFound::with_kv(Index(key), attachment::Type::of::())), - } - } } #[macro_export] @@ -1083,4 +979,11 @@ mod test { } assert_err!(compare(my_number, other_number)); } + + #[test] + fn expect_by() { + let arr = ["a", "b"]; + let get_oob = arr.get(2).expect_by(2); + assert_err!(get_oob); + } }