1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
use crate::MatchArg;
use std::fmt::Debug;
use std::marker::PhantomData;

pub trait MatchArgExt<T: Debug, M: MatchArg<T>> {
    fn with_custom_msg<F: Fn(&T) -> String>(self, msg_fn: F) -> WithMessageFn<T, M, F>;
    fn with_description_fn<F: Fn() -> String>(
        self,
        description_fn: F,
    ) -> WithDescriptionFn<T, M, F>;
}

impl<T: Debug, M: MatchArg<T>> MatchArgExt<T, M> for M {
    fn with_custom_msg<F: Fn(&T) -> String>(self, msg_fn: F) -> WithMessageFn<T, M, F> {
        WithMessageFn::new(self, msg_fn)
    }
    fn with_description_fn<F: Fn() -> String>(
        self,
        description_fn: F,
    ) -> WithDescriptionFn<T, M, F> {
        WithDescriptionFn::new(self, description_fn)
    }
}

pub struct WithDescriptionFn<T: Debug, M: MatchArg<T>, F: Fn() -> String> {
    matcher: M,
    description_fn: F,
    _phantom: PhantomData<T>,
}
impl<T: Debug, M: MatchArg<T>, F: Fn() -> String> WithDescriptionFn<T, M, F> {
    pub fn new(matcher: M, description_fn: F) -> Self {
        WithDescriptionFn { matcher, description_fn, _phantom: PhantomData }
    }
    fn description(&self) -> String {
        let fun = &self.description_fn;
        fun()
    }
}
impl<T: Debug, M: MatchArg<T>, F: Fn() -> String> MatchArg<T> for WithDescriptionFn<T, M, F> {
    fn matches(&self, arg: &T) -> Result<(), String> {
        self.matcher.matches(arg)
    }
    fn describe(&self) -> String {
        self.description()
    }
}

pub struct WithMessageFn<T: Debug, M: MatchArg<T>, F: Fn(&T) -> String> {
    matcher: M,
    msg_fn: F,
    _phantom: PhantomData<T>,
}
impl<T: Debug, M: MatchArg<T>, F: Fn(&T) -> String> WithMessageFn<T, M, F> {
    pub fn new(matcher: M, msg_fn: F) -> Self {
        WithMessageFn { matcher, msg_fn, _phantom: PhantomData }
    }
    fn message(&self, arg: &T) -> String {
        let fun = &self.msg_fn;
        fun(arg)
    }
}
impl<T: Debug, M: MatchArg<T>, F: Fn(&T) -> String> MatchArg<T> for WithMessageFn<T, M, F> {
    fn matches(&self, arg: &T) -> Result<(), String> {
        match self.matcher.matches(arg) {
            Ok(()) => Ok(()),
            Err(_) => Err(self.message(arg)),
        }
    }
    fn describe(&self) -> String {
        self.matcher.describe()
    }
}