Skip to content

Commit 60fa259

Browse files
authored
Merge pull request #78 from innoave/refactor/make-traits-for-methods-on-spec-used-by-assertion-implementations
Refactor: move some methods of `Spec` to new traits
2 parents 872c922 + 3783704 commit 60fa259

4 files changed

Lines changed: 152 additions & 108 deletions

File tree

src/iterator/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ use crate::expectations::{
2121
};
2222
use crate::properties::DefinedOrderProperty;
2323
use crate::spec::{
24-
DiffFormat, Expectation, Expression, FailingStrategy, Invertible, PanicOnFail, Spec,
24+
DiffFormat, Expectation, Expression, FailingStrategy, GetFailures, Invertible, PanicOnFail,
25+
Spec,
2526
};
2627
use crate::std::cmp::Ordering;
2728
use crate::std::fmt::Debug;

src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@
245245
//! expected: <= 41
246246
//! ```
247247
//!
248-
//! For more details see [`Spec::soft_panic()`].
248+
//! For more details see [`SoftPanic::soft_panic()`].
249249
//!
250250
//! ## Asserting custom types
251251
//!
@@ -751,13 +751,13 @@
751751
//! [`Spec::each_element()`]: spec::Spec::each_element
752752
//! [`Spec::expecting()`]: spec::Spec::expecting
753753
//! [`Spec::satisfies()`]: spec::Spec::satisfies
754-
//! [`Spec::soft_panic()`]: spec::Spec::soft_panic
754+
//! [`SoftPanic::soft_panic()`]: spec::SoftPanic::soft_panic
755755
//! [`assert_that`]: spec::assert_that
756756
//! [`assert_that_code`]: spec::assert_that_code
757757
//! [`verify_that`]: spec::verify_that
758758
//! [`verify_that_code`]: spec::verify_that_code
759759
//! [`display_failures()`]: spec::Spec::display_failures
760-
//! [`failures()`]: spec::Spec::failures
760+
//! [`failures()`]: spec::GetFailures::failures
761761
//! [`named()`]: spec::Spec::named
762762
//! [`located_at()`]: spec::Spec::located_at
763763

src/prelude.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ pub use super::{
1919
assertions::*,
2020
colored::{DEFAULT_DIFF_FORMAT, DIFF_FORMAT_NO_HIGHLIGHT},
2121
properties::*,
22-
spec::{assert_that, verify_that, CollectFailures, Location, PanicOnFail},
22+
spec::{
23+
assert_that, verify_that, CollectFailures, DoFail, GetFailures, Location, PanicOnFail,
24+
SoftPanic,
25+
},
2326
verify_that,
2427
};
2528

src/spec/mod.rs

Lines changed: 143 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ macro_rules! assert_that {
6363
///
6464
/// Assertions started with `verify_that!` will collect [`AssertFailure`]s for
6565
/// all failing assertions. The collected failures can be queried by calling one
66-
/// of the methods [`failures`](Spec::failures) or
67-
/// [`display_failures`](Spec::display_failures) on the [`Spec`].
66+
/// of the methods [`failures`](GetFailures::failures) or
67+
/// [`display_failures`](GetFailures::display_failures) on the [`Spec`].
6868
///
6969
/// # Example
7070
///
@@ -150,8 +150,8 @@ macro_rules! assert_that_code {
150150
///
151151
/// Assertions started with `verify_that_code!` will collect [`AssertFailure`]s
152152
/// for all failing assertions. The collected failures can be queried by calling
153-
/// one of the methods [`failures`](Spec::failures) or
154-
/// [`display_failures`](Spec::display_failures) on the [`Spec`].
153+
/// one of the methods [`failures`](GetFailures::failures) or
154+
/// [`display_failures`](GetFailures::display_failures) on the [`Spec`].
155155
///
156156
/// # Examples
157157
///
@@ -259,8 +259,8 @@ pub fn assert_that<'a, S>(subject: S) -> Spec<'a, S, PanicOnFail> {
259259
///
260260
/// Assertions started with `verify_that()` will collect [`AssertFailure`]s
261261
/// for all failing assertions. The collected failures can be queried by calling
262-
/// one of the methods [`failures`](Spec::failures) or the
263-
/// [`display_failures`](Spec::display_failures) on the [`Spec`].
262+
/// one of the methods [`failures`](GetFailures::failures) or the
263+
/// [`display_failures`](GetFailures::display_failures) on the [`Spec`].
264264
///
265265
/// In comparison to using the macro [`verify_that!`](crate::verify_that) calling
266266
/// this function does not set a name for the expression and does not set the
@@ -364,7 +364,7 @@ where
364364
///
365365
/// Assertions started with `verify_that_code()` will collect [`AssertFailure`]s
366366
/// for all failing assertions. The collected failures can be queried by calling
367-
/// one of the methods [`failures`](Spec::failures) or
367+
/// one of the methods [`failures`](GetFailures::failures) or
368368
/// [`display_failures`](Spec::display_failures) on the [`Spec`].
369369
///
370370
/// In comparison to using the macro [`verify_that_code!`](crate::verify_that_code)
@@ -664,25 +664,10 @@ impl<S, R> Spec<'_, S, R> {
664664
&self.diff_format
665665
}
666666

667-
/// Returns the failing strategy that is used in case an assertion fails.
667+
/// Returns the failing strategy used in case an assertion fails.
668668
pub fn failing_strategy(&self) -> &R {
669669
&self.failing_strategy
670670
}
671-
672-
/// Returns whether there are assertion failures collected so far.
673-
pub fn has_failures(&self) -> bool {
674-
!self.failures.is_empty()
675-
}
676-
677-
/// Returns the assertion failures that have been collected so far.
678-
pub fn failures(&self) -> Vec<AssertFailure> {
679-
self.failures.clone()
680-
}
681-
682-
/// Returns the assertion failures collected so far as formatted text.
683-
pub fn display_failures(&self) -> Vec<String> {
684-
self.failures.iter().map(ToString::to_string).collect()
685-
}
686671
}
687672

688673
impl<'a, S, R> Spec<'a, S, R> {
@@ -971,86 +956,6 @@ where
971956
{
972957
self.expecting(satisfies(predicate).with_message(message))
973958
}
974-
975-
/// Fails the assertion according to the current failing strategy of this
976-
/// `Spec`.
977-
#[track_caller]
978-
pub fn do_fail_with_message(&mut self, message: impl Into<String>) {
979-
let message = message.into();
980-
let failure = AssertFailure {
981-
description: self.description.clone().map(String::from),
982-
message,
983-
location: self.location.map(OwnedLocation::from),
984-
};
985-
self.failures.push(failure);
986-
self.failing_strategy.do_fail_with(&self.failures);
987-
}
988-
}
989-
990-
impl<S> Spec<'_, S, CollectFailures> {
991-
/// Turns assertions into "soft assertions".
992-
///
993-
/// It executes all specified assertions on a `Spec` and if at least one
994-
/// assertion fails, it panics. The panic message contains the messages of
995-
/// all assertions that have failed.
996-
///
997-
/// This method is only available on `Spec`s with the
998-
/// [`CollectFailures`]-[`FailingStrategy`]. That is any `Spec` contructed
999-
/// by the macros [`verify_that!`] and [`verify_that_code!`] or by the
1000-
/// functions [`verify_that()`] and [`verify_that_code()`].
1001-
///
1002-
/// On a `Spec` with the [`PanicOnFail`]-[`FailingStrategy`] it would not
1003-
/// work as the very first failing assertion panics immediately, and later
1004-
/// assertions never get executed.
1005-
///
1006-
/// # Examples
1007-
///
1008-
/// Running the following two assertions in "soft" mode:
1009-
///
1010-
/// ```should_panic
1011-
/// use asserting::prelude::*;
1012-
///
1013-
/// verify_that!("the answer to all important questions is 42")
1014-
/// .contains("unimportant")
1015-
/// .has_at_most_length(41)
1016-
/// .soft_panic();
1017-
/// ```
1018-
///
1019-
/// executes both assertions and prints the messages of both failing
1020-
/// assertions in the panic message:
1021-
///
1022-
/// ```console
1023-
/// expected subject to contain "unimportant"
1024-
/// but was: "the answer to all important questions is 42"
1025-
/// expected: "unimportant"
1026-
///
1027-
/// expected subject to have at most a length of 41
1028-
/// but was: 43
1029-
/// expected: <= 41
1030-
/// ```
1031-
///
1032-
/// To highlight differences in failure messages of soft assertions use
1033-
/// the `with_configured_diff_format()` method, like so:
1034-
///
1035-
/// ```
1036-
/// # #[cfg(not(feature = "colored"))]
1037-
/// # fn main() {}
1038-
/// # #[cfg(feature = "colored")]
1039-
/// # fn main() {
1040-
/// use asserting::prelude::*;
1041-
///
1042-
/// verify_that!("the answer to all important questions is 42")
1043-
/// .with_configured_diff_format()
1044-
/// .contains("important")
1045-
/// .has_at_most_length(43)
1046-
/// .soft_panic();
1047-
/// # }
1048-
/// ```
1049-
pub fn soft_panic(&self) {
1050-
if !self.failures.is_empty() {
1051-
PanicOnFail.do_fail_with(&self.failures);
1052-
}
1053-
}
1054959
}
1055960

1056961
impl<'a, I, R> Spec<'a, I, R> {
@@ -1221,6 +1126,141 @@ impl<'a, I, R> Spec<'a, I, R> {
12211126
}
12221127
}
12231128

1129+
/// Trigger failing of an assertion according to the failing strategy of its
1130+
/// implementing spec-like struct.
1131+
pub trait DoFail {
1132+
/// Fails the assertion with the given [`AssertFailure`]s according to the
1133+
/// current failing strategy of the `Spec` or other implementing
1134+
/// spec-like struct.
1135+
fn do_fail_with(&mut self, failures: impl IntoIterator<Item = AssertFailure>);
1136+
1137+
/// Fails the assertion with the given failure message according to the
1138+
/// current failing strategy of the `Spec` or other implementing
1139+
/// spec-like struct.
1140+
fn do_fail_with_message(&mut self, message: impl Into<String>);
1141+
}
1142+
1143+
impl<S, R> DoFail for Spec<'_, S, R>
1144+
where
1145+
R: FailingStrategy,
1146+
{
1147+
fn do_fail_with(&mut self, failures: impl IntoIterator<Item = AssertFailure>) {
1148+
self.failures.extend(failures);
1149+
self.failing_strategy.do_fail_with(&self.failures);
1150+
}
1151+
1152+
fn do_fail_with_message(&mut self, message: impl Into<String>) {
1153+
let message = message.into();
1154+
let failure = AssertFailure {
1155+
description: self.description.clone().map(String::from),
1156+
message,
1157+
location: self.location.map(OwnedLocation::from),
1158+
};
1159+
self.failures.push(failure);
1160+
self.failing_strategy.do_fail_with(&self.failures);
1161+
}
1162+
}
1163+
1164+
/// Turns assertions into "soft assertions".
1165+
///
1166+
/// See method [`soft_panic()`](SoftPanic::soft_panic) for details and how to
1167+
/// use it.
1168+
pub trait SoftPanic {
1169+
/// Turns assertions into "soft assertions".
1170+
///
1171+
/// It executes all specified assertions on a `Spec` and if at least one
1172+
/// assertion fails, it panics. The panic message contains the messages of
1173+
/// all assertions that have failed.
1174+
///
1175+
/// This method is only available on `Spec`s with the
1176+
/// [`CollectFailures`]-[`FailingStrategy`]. That is any `Spec` contructed
1177+
/// by the macros [`verify_that!`] and [`verify_that_code!`] or by the
1178+
/// functions [`verify_that()`] and [`verify_that_code()`].
1179+
///
1180+
/// On a `Spec` with the [`PanicOnFail`]-[`FailingStrategy`] it would not
1181+
/// work as the very first failing assertion panics immediately, and later
1182+
/// assertions never get executed.
1183+
///
1184+
/// # Examples
1185+
///
1186+
/// Running the following two assertions in "soft" mode:
1187+
///
1188+
/// ```should_panic
1189+
/// use asserting::prelude::*;
1190+
///
1191+
/// verify_that!("the answer to all important questions is 42")
1192+
/// .contains("unimportant")
1193+
/// .has_at_most_length(41)
1194+
/// .soft_panic();
1195+
/// ```
1196+
///
1197+
/// executes both assertions and prints the messages of both failing
1198+
/// assertions in the panic message:
1199+
///
1200+
/// ```console
1201+
/// expected subject to contain "unimportant"
1202+
/// but was: "the answer to all important questions is 42"
1203+
/// expected: "unimportant"
1204+
///
1205+
/// expected subject to have at most a length of 41
1206+
/// but was: 43
1207+
/// expected: <= 41
1208+
/// ```
1209+
///
1210+
/// To highlight differences in failure messages of soft assertions use
1211+
/// the `with_configured_diff_format()` method, like so:
1212+
///
1213+
/// ```
1214+
/// # #[cfg(not(feature = "colored"))]
1215+
/// # fn main() {}
1216+
/// # #[cfg(feature = "colored")]
1217+
/// # fn main() {
1218+
/// use asserting::prelude::*;
1219+
///
1220+
/// verify_that!("the answer to all important questions is 42")
1221+
/// .with_configured_diff_format()
1222+
/// .contains("important")
1223+
/// .has_at_most_length(43)
1224+
/// .soft_panic();
1225+
/// # }
1226+
/// ```
1227+
fn soft_panic(&self);
1228+
}
1229+
1230+
impl<S> SoftPanic for Spec<'_, S, CollectFailures> {
1231+
fn soft_panic(&self) {
1232+
if !self.failures.is_empty() {
1233+
PanicOnFail.do_fail_with(&self.failures);
1234+
}
1235+
}
1236+
}
1237+
1238+
/// Access the assertion-failures collected by a `Spec` or spec-like struct.
1239+
pub trait GetFailures {
1240+
/// Returns whether there are assertion failures collected so far.
1241+
fn has_failures(&self) -> bool;
1242+
1243+
/// Returns the assertion failures that have been collected so far.
1244+
fn failures(&self) -> Vec<AssertFailure>;
1245+
1246+
/// Returns the assertion failures collected so far as formatted text.
1247+
fn display_failures(&self) -> Vec<String>;
1248+
}
1249+
1250+
impl<S, R> GetFailures for Spec<'_, S, R> {
1251+
fn has_failures(&self) -> bool {
1252+
!self.failures.is_empty()
1253+
}
1254+
1255+
fn failures(&self) -> Vec<AssertFailure> {
1256+
self.failures.clone()
1257+
}
1258+
1259+
fn display_failures(&self) -> Vec<String> {
1260+
self.failures.iter().map(ToString::to_string).collect()
1261+
}
1262+
}
1263+
12241264
/// An error describing a failed assertion.
12251265
///
12261266
/// This struct implements the [`std::error::Error`] trait.

0 commit comments

Comments
 (0)