use std::{marker::PhantomData, sync::Arc};
use crate::{
matcher::SpanMatcher,
state::{EntryState, State},
};
enum AssertionCriterion {
WasCreated,
WasEntered,
WasExited,
WasClosed,
WasNotCreated,
WasNotEntered,
WasNotExited,
WasNotClosed,
CreatedExactly(usize),
EnteredExactly(usize),
ExitedExactly(usize),
ClosedExactly(usize),
CreatedAtLeast(usize),
EnteredAtLeast(usize),
ExitedAtLeast(usize),
ClosedAtLeast(usize),
}
impl AssertionCriterion {
pub fn assert(&self, state: &Arc<EntryState>) {
match self {
AssertionCriterion::WasCreated => assert!(state.num_created() != 0),
AssertionCriterion::WasEntered => assert!(state.num_entered() != 0),
AssertionCriterion::WasExited => assert!(state.num_exited() != 0),
AssertionCriterion::WasClosed => assert!(state.num_closed() != 0),
AssertionCriterion::WasNotCreated => assert_eq!(0, state.num_created()),
AssertionCriterion::WasNotEntered => assert_eq!(0, state.num_entered()),
AssertionCriterion::WasNotExited => assert_eq!(0, state.num_exited()),
AssertionCriterion::WasNotClosed => assert_eq!(0, state.num_closed()),
AssertionCriterion::CreatedExactly(times) => assert_eq!(state.num_created(), *times),
AssertionCriterion::EnteredExactly(times) => assert_eq!(state.num_entered(), *times),
AssertionCriterion::ExitedExactly(times) => assert_eq!(state.num_exited(), *times),
AssertionCriterion::ClosedExactly(times) => assert_eq!(state.num_closed(), *times),
AssertionCriterion::CreatedAtLeast(times) => assert!(state.num_created() >= *times),
AssertionCriterion::EnteredAtLeast(times) => assert!(state.num_entered() >= *times),
AssertionCriterion::ExitedAtLeast(times) => assert!(state.num_exited() >= *times),
AssertionCriterion::ClosedAtLeast(times) => assert!(state.num_closed() >= *times),
}
}
pub fn try_assert(&self, state: &Arc<EntryState>) -> bool {
match self {
AssertionCriterion::WasCreated => state.num_created() != 0,
AssertionCriterion::WasEntered => state.num_entered() != 0,
AssertionCriterion::WasExited => state.num_exited() != 0,
AssertionCriterion::WasClosed => state.num_closed() != 0,
AssertionCriterion::WasNotCreated => state.num_created() == 0,
AssertionCriterion::WasNotEntered => state.num_entered() == 0,
AssertionCriterion::WasNotExited => state.num_exited() == 0,
AssertionCriterion::WasNotClosed => state.num_closed() == 0,
AssertionCriterion::CreatedExactly(times) => state.num_created() == *times,
AssertionCriterion::EnteredExactly(times) => state.num_entered() == *times,
AssertionCriterion::ExitedExactly(times) => state.num_exited() == *times,
AssertionCriterion::ClosedExactly(times) => state.num_closed() == *times,
AssertionCriterion::CreatedAtLeast(times) => state.num_created() >= *times,
AssertionCriterion::EnteredAtLeast(times) => state.num_entered() >= *times,
AssertionCriterion::ExitedAtLeast(times) => state.num_exited() >= *times,
AssertionCriterion::ClosedAtLeast(times) => state.num_closed() >= *times,
}
}
}
pub struct Assertion {
state: Arc<State>,
entry_state: Arc<EntryState>,
matcher: SpanMatcher,
criteria: Vec<AssertionCriterion>,
}
impl Assertion {
pub fn assert(&self) {
for criterion in &self.criteria {
criterion.assert(&self.entry_state);
}
}
pub fn try_assert(&self) -> bool {
for criterion in &self.criteria {
if !criterion.try_assert(&self.entry_state) {
return false;
}
}
true
}
}
impl Drop for Assertion {
fn drop(&mut self) {
self.state.remove_entry(&self.matcher);
}
}
pub struct NoMatcher {
_p: PhantomData<()>,
}
pub struct NoCriteria {
_p: PhantomData<()>,
}
pub struct Constrained {
_p: PhantomData<()>,
}
pub struct AssertionBuilder<S> {
state: Arc<State>,
matcher: Option<SpanMatcher>,
criteria: Vec<AssertionCriterion>,
_builder_state: PhantomData<fn(S)>,
}
impl AssertionBuilder<NoMatcher> {
pub fn with_name<S>(mut self, name: S) -> AssertionBuilder<NoCriteria>
where
S: Into<String>,
{
let matcher = self.matcher.get_or_insert_with(SpanMatcher::default);
matcher.set_name(name.into());
AssertionBuilder {
state: self.state,
matcher: self.matcher,
criteria: self.criteria,
_builder_state: PhantomData,
}
}
pub fn with_target<S>(mut self, target: S) -> AssertionBuilder<NoCriteria>
where
S: Into<String>,
{
let matcher = self.matcher.get_or_insert_with(SpanMatcher::default);
matcher.set_target(target.into());
AssertionBuilder {
state: self.state,
matcher: self.matcher,
criteria: self.criteria,
_builder_state: PhantomData,
}
}
}
impl AssertionBuilder<NoCriteria> {
pub fn with_name<S>(mut self, name: S) -> AssertionBuilder<NoCriteria>
where
S: Into<String>,
{
let matcher = self.matcher.get_or_insert_with(SpanMatcher::default);
matcher.set_name(name.into());
AssertionBuilder {
state: self.state,
matcher: self.matcher,
criteria: self.criteria,
_builder_state: PhantomData,
}
}
pub fn with_target<S>(mut self, target: S) -> AssertionBuilder<NoCriteria>
where
S: Into<String>,
{
let matcher = self.matcher.get_or_insert_with(SpanMatcher::default);
matcher.set_target(target.into());
AssertionBuilder {
state: self.state,
matcher: self.matcher,
criteria: self.criteria,
_builder_state: PhantomData,
}
}
pub fn with_parent_name<S>(mut self, name: S) -> AssertionBuilder<NoCriteria>
where
S: Into<String>,
{
let matcher = self.matcher.get_or_insert_with(SpanMatcher::default);
matcher.set_parent_name(name.into());
AssertionBuilder {
state: self.state,
matcher: self.matcher,
criteria: self.criteria,
_builder_state: PhantomData,
}
}
pub fn with_span_field<S>(mut self, field: S) -> AssertionBuilder<NoCriteria>
where
S: Into<String>,
{
if let Some(matcher) = self.matcher.as_mut() {
matcher.add_field_exists(field.into());
}
AssertionBuilder {
state: self.state,
matcher: self.matcher,
criteria: self.criteria,
_builder_state: PhantomData,
}
}
pub fn was_created(mut self) -> AssertionBuilder<Constrained> {
self.criteria.push(AssertionCriterion::WasCreated);
AssertionBuilder {
state: self.state,
matcher: self.matcher,
criteria: self.criteria,
_builder_state: PhantomData,
}
}
pub fn was_entered(mut self) -> AssertionBuilder<Constrained> {
self.criteria.push(AssertionCriterion::WasEntered);
AssertionBuilder {
state: self.state,
matcher: self.matcher,
criteria: self.criteria,
_builder_state: PhantomData,
}
}
pub fn was_exited(mut self) -> AssertionBuilder<Constrained> {
self.criteria.push(AssertionCriterion::WasExited);
AssertionBuilder {
state: self.state,
matcher: self.matcher,
criteria: self.criteria,
_builder_state: PhantomData,
}
}
pub fn was_closed(mut self) -> AssertionBuilder<Constrained> {
self.criteria.push(AssertionCriterion::WasClosed);
AssertionBuilder {
state: self.state,
matcher: self.matcher,
criteria: self.criteria,
_builder_state: PhantomData,
}
}
pub fn was_not_created(mut self) -> AssertionBuilder<Constrained> {
self.criteria.push(AssertionCriterion::WasNotCreated);
AssertionBuilder {
state: self.state,
matcher: self.matcher,
criteria: self.criteria,
_builder_state: PhantomData,
}
}
pub fn was_not_entered(mut self) -> AssertionBuilder<Constrained> {
self.criteria.push(AssertionCriterion::WasNotEntered);
AssertionBuilder {
state: self.state,
matcher: self.matcher,
criteria: self.criteria,
_builder_state: PhantomData,
}
}
pub fn was_not_exited(mut self) -> AssertionBuilder<Constrained> {
self.criteria.push(AssertionCriterion::WasNotExited);
AssertionBuilder {
state: self.state,
matcher: self.matcher,
criteria: self.criteria,
_builder_state: PhantomData,
}
}
pub fn was_not_closed(mut self) -> AssertionBuilder<Constrained> {
self.criteria.push(AssertionCriterion::WasNotClosed);
AssertionBuilder {
state: self.state,
matcher: self.matcher,
criteria: self.criteria,
_builder_state: PhantomData,
}
}
pub fn was_created_exactly(mut self, n: usize) -> AssertionBuilder<Constrained> {
self.criteria.push(AssertionCriterion::CreatedExactly(n));
AssertionBuilder {
state: self.state,
matcher: self.matcher,
criteria: self.criteria,
_builder_state: PhantomData,
}
}
pub fn was_entered_exactly(mut self, n: usize) -> AssertionBuilder<Constrained> {
self.criteria.push(AssertionCriterion::EnteredExactly(n));
AssertionBuilder {
state: self.state,
matcher: self.matcher,
criteria: self.criteria,
_builder_state: PhantomData,
}
}
pub fn was_exited_exactly(mut self, n: usize) -> AssertionBuilder<Constrained> {
self.criteria.push(AssertionCriterion::ExitedExactly(n));
AssertionBuilder {
state: self.state,
matcher: self.matcher,
criteria: self.criteria,
_builder_state: PhantomData,
}
}
pub fn was_closed_exactly(mut self, n: usize) -> AssertionBuilder<Constrained> {
self.criteria.push(AssertionCriterion::ClosedExactly(n));
AssertionBuilder {
state: self.state,
matcher: self.matcher,
criteria: self.criteria,
_builder_state: PhantomData,
}
}
pub fn was_created_at_least(mut self, n: usize) -> AssertionBuilder<Constrained> {
self.criteria.push(AssertionCriterion::CreatedAtLeast(n));
AssertionBuilder {
state: self.state,
matcher: self.matcher,
criteria: self.criteria,
_builder_state: PhantomData,
}
}
pub fn was_entered_at_least(mut self, n: usize) -> AssertionBuilder<Constrained> {
self.criteria.push(AssertionCriterion::EnteredAtLeast(n));
AssertionBuilder {
state: self.state,
matcher: self.matcher,
criteria: self.criteria,
_builder_state: PhantomData,
}
}
pub fn was_exited_at_least(mut self, n: usize) -> AssertionBuilder<Constrained> {
self.criteria.push(AssertionCriterion::ExitedAtLeast(n));
AssertionBuilder {
state: self.state,
matcher: self.matcher,
criteria: self.criteria,
_builder_state: PhantomData,
}
}
pub fn was_closed_at_least(mut self, n: usize) -> AssertionBuilder<Constrained> {
self.criteria.push(AssertionCriterion::ClosedAtLeast(n));
AssertionBuilder {
state: self.state,
matcher: self.matcher,
criteria: self.criteria,
_builder_state: PhantomData,
}
}
}
impl AssertionBuilder<Constrained> {
pub fn was_created(mut self) -> Self {
self.criteria.push(AssertionCriterion::WasCreated);
self
}
pub fn was_entered(mut self) -> Self {
self.criteria.push(AssertionCriterion::WasEntered);
self
}
pub fn was_exited(mut self) -> Self {
self.criteria.push(AssertionCriterion::WasExited);
self
}
pub fn was_closed(mut self) -> Self {
self.criteria.push(AssertionCriterion::WasClosed);
self
}
pub fn was_not_created(mut self) -> Self {
self.criteria.push(AssertionCriterion::WasNotCreated);
self
}
pub fn was_not_entered(mut self) -> Self {
self.criteria.push(AssertionCriterion::WasNotEntered);
self
}
pub fn was_not_exited(mut self) -> Self {
self.criteria.push(AssertionCriterion::WasNotExited);
self
}
pub fn was_not_closed(mut self) -> Self {
self.criteria.push(AssertionCriterion::WasNotClosed);
self
}
pub fn was_created_exactly(mut self, n: usize) -> Self {
self.criteria.push(AssertionCriterion::CreatedExactly(n));
self
}
pub fn was_entered_exactly(mut self, n: usize) -> Self {
self.criteria.push(AssertionCriterion::EnteredExactly(n));
self
}
pub fn was_exited_exactly(mut self, n: usize) -> Self {
self.criteria.push(AssertionCriterion::ExitedExactly(n));
self
}
pub fn was_closed_exactly(mut self, n: usize) -> Self {
self.criteria.push(AssertionCriterion::ClosedExactly(n));
self
}
pub fn was_created_at_least(mut self, n: usize) -> Self {
self.criteria.push(AssertionCriterion::CreatedAtLeast(n));
self
}
pub fn was_entered_at_least(mut self, n: usize) -> Self {
self.criteria.push(AssertionCriterion::EnteredAtLeast(n));
self
}
pub fn was_exited_at_least(mut self, n: usize) -> Self {
self.criteria.push(AssertionCriterion::ExitedAtLeast(n));
self
}
pub fn was_closed_at_least(mut self, n: usize) -> Self {
self.criteria.push(AssertionCriterion::ClosedAtLeast(n));
self
}
pub fn finalize(mut self) -> Assertion {
let matcher = self
.matcher
.take()
.expect("matcher must be present at this point");
let entry_state = self.state.create_entry(matcher.clone());
Assertion {
state: Arc::clone(&self.state),
entry_state,
matcher,
criteria: self.criteria,
}
}
}
#[derive(Clone, Default)]
pub struct AssertionRegistry {
state: Arc<State>,
}
impl AssertionRegistry {
pub(crate) fn state(&self) -> &Arc<State> {
&self.state
}
pub fn build(&self) -> AssertionBuilder<NoMatcher> {
AssertionBuilder {
state: Arc::clone(&self.state),
matcher: None,
criteria: Vec::new(),
_builder_state: PhantomData,
}
}
}