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
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#[macro_export]
macro_rules! pipe {
    ( $v:expr => $( $t:tt )=>+ ) => {
        {
            #[allow(unused_mut)]
            let mut v = $v;
            $(
                #[allow(unused_mut)]
                let mut v = $crate::pipe_match!($t, v);
            )*
            v
        }
    }
}

#[macro_export]
macro_rules! pipe_match {
    ( ref, $v:ident ) => {
        &$v
    };
    ( (ref), $v:ident ) => {
        &$v
    };
    ( (mut ref), $v:ident ) => {
        &mut $v
    };
    ( deref, $v:ident ) => {
        *$v
    };
    ( (deref), $v:ident ) => {
        *$v
    };
    ( {$f:expr}, $v:ident ) => {
        $f($v)
    };
    ( $f:ident, $v:ident ) => {
        $f($v)
    };
    ( ($f:ident), $v:ident ) => {
        $f($v)
    };
    ( ($f:ident($( $arg:expr ),*)), $v:ident ) => {
        $f($( $arg, )* $v)
    };
    ( ($f:ident($( $arg:expr, )*)), $v:ident ) => {
        $f($( $arg, )* $v)
    };
    ( ($f:ident?), $v:ident ) => {
        $f($v)?
    };
    ( ($f:ident($( $arg:expr ),*)?), $v:ident ) => {
        $f($( $arg, )* $v)?
    };
    ( ($f:ident($( $arg:expr, )*)?), $v:ident ) => {
        $f($( $arg, )* $v)?
    };
    ( (.$f:ident), $v:ident ) => {
        $v.$f()
    };
    ( (.$f:ident($( $arg:expr ),*)), $v:ident ) => {
        $v.$f($( $arg ),*)
    };
    ( (.$f:ident($( $arg:expr, )*)), $v:ident ) => {
        $v.$f($( $arg ),*)
    };
    ( (.$f:ident?), $v:ident ) => {
        $v.$f()?
    };
    ( (.$f:ident($( $arg:expr ),*)?), $v:ident ) => {
        $v.$f($( $arg ),*)?
    };
    ( (.$f:ident($( $arg:expr, )*)?), $v:ident ) => {
        $v.$f($( $arg ),*)?
    };
    ( [$i:expr], $v:ident ) => {
        $v[$i]
    };
    ( [.$i:tt], $v:ident ) => {
        $v.$i
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_ref() {
        let incr = |n: &u32| n + 1;
        fn incr_mut(n: &mut u32) -> &u32 {
            *n += 1;
            n
        }
        assert_eq!(2, pipe!(1 => ref => incr));
        assert_eq!(2, pipe!(1 => (mut ref) => incr_mut => deref));
        assert_eq!(2, pipe!(1 => (mut ref) => (incr_mut) => (ref) => (deref) => deref));
    }

    #[test]
    fn test_closure() {
        assert_eq!(2, pipe!(1 => {|n| n + 1}));
        assert_eq!(
            3,
            pipe!(1 => {|n| {
                let n = n + 1;
                n + 1
            }})
        );
    }

    #[test]
    fn test_fn() {
        let incr = |a| a + 1;
        let add2 = |a, b| a + b;
        let add3 = |a, b, c| a + b + c;
        assert_eq!(2, pipe!(1 => incr));
        assert_eq!(2, pipe!(1 => (add2(1))));
        assert_eq!(3, pipe!(1 => (add2(1,)) => (incr)));
        assert_eq!(4, pipe!(1 => (add3(0, 2)) => (incr())));
        assert_eq!(5, pipe!(1 => incr => (add3(1, 2,))));
    }

    #[test]
    fn test_method() {
        #[derive(Debug, PartialEq, Eq)]
        struct U32(u32);
        impl U32 {
            fn incr(self) -> U32 {
                U32(self.0 + 1)
            }

            fn add2(self, b: u32) -> U32 {
                U32(self.0 + b)
            }

            fn add3(self, b: u32, c: u32) -> U32 {
                U32(self.0 + b + c)
            }
        }

        assert_eq!(U32(2), pipe!(U32(1) => (.incr)));
        assert_eq!(U32(2), pipe!(U32(1) => (.add2(1))));
        assert_eq!(U32(3), pipe!(U32(1) => (.add2(1,)) => (.incr())));
        assert_eq!(U32(4), pipe!(U32(1) => (.add3(0, 2)) => (.incr())));
        assert_eq!(U32(5), pipe!(U32(1) => (.incr) => (.add3(1, 2,))));
    }

    #[test]
    fn test_index() {
        let a = [0, 1, 2];
        let i = 1;
        assert_eq!(0, pipe!(&a => [0]));
        assert_eq!(1, pipe!(&a => [i]));
        assert_eq!(2, pipe!(&a => [(|| 1 + 1)()]));
    }

    #[test]
    fn test_access() {
        struct Foo(u32, u32);
        struct Bar {
            _a: u32,
            b: u32,
        }
        let a = (0, 1);
        let b = Foo(2, 3);
        let c = Bar { _a: 4, b: 5 };
        assert_eq!(1, pipe!(&a => [.1]));
        assert_eq!(3, pipe!(&b => [.1]));
        assert_eq!(5, pipe!(&c => [.b]));
    }
}