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
#[macro_export]
macro_rules! handler {
    ($state:ident . $($idents:ident).+ (_)) => {{
        let cloned_state = $state.clone();
        move |arg| cloned_state.$($idents).+(arg)
    }};
    ($state:ident . $($idents:ident).+ (_, $($tt:tt)+)) => {{
        let cloned_state = $state.clone();
        move |arg| cloned_state.$($idents).+(arg, $($tt)+)
    }};
    ($state:ident . $($idents:ident).+ ($($tt:tt)*)) => {{
        let cloned_state = $state.clone();
        move |_| cloned_state.$($idents).+($($tt)*)
    }};
}

#[macro_export]
macro_rules! update {
    ($state:ident, $first:ident$(::$idents:ident)*) => {{
        let cloned_state = $state.clone();
        move |_| cloned_state.update($first$(::$idents)*)
    }};
    ($state:ident, $first:ident$(::$idents:ident)*(_)) => {{
        let cloned_state = $state.clone();
        move |arg| cloned_state.update($first$(::$idents)*(arg))
    }};
    ($state:ident, $first:ident$(::$idents:ident)*(_, $($tt:tt)+)) => {{
        let cloned_state = $state.clone();
        move |arg| cloned_state.update($first$(::$idents)*(arg, $($tt)+))
    }};
    ($state:ident, $first:ident$(::$idents:ident)*($($tt:tt)+)) => {{
        let cloned_state = $state.clone();
        move |_| cloned_state.update($first$(::$idents)*($($tt)+))
    }};
}

#[macro_export]
macro_rules! create_app_handle {
    ($TypeName:ident) => {
        #[wasm_bindgen]
        pub struct AppHandle {
            _app: App<$TypeName>,
        }

        #[wasm_bindgen]
        impl AppHandle {
            #[wasm_bindgen(constructor)]
            pub fn new() -> Self {
                Self {
                    _app: App::start_in_body($TypeName::new())
                }
            }
        }
    };
}

#[cfg(test)]
mod test {
    enum Msg {
        N42,
        Double(i32),
        Add(i32,i32),
        Sum(i32,i32,i32),
    }
    #[derive(Clone)]
    struct Test;
    impl Test {
        fn n42(&self) -> i32 {
            42
        }
        fn double(&self, me: i32)  -> i32 {
            me * 2
        }
        fn add(&self, a: i32, b: i32)  -> i32 {
            a + b
        }
        fn sum(&self, a: i32, b: i32, c: i32) -> i32 {
            a + b + c
        }
        fn update(&self, m: Msg) -> i32 {
            match m {
                Msg::N42 => 42,
                Msg::Double(value) => value*2,
                Msg::Add(v1, v2) => v1 + v2,
                Msg::Sum(v1,v2,v3) => v1 + v2 + v3,
            }
        }
    }

    #[test]
    fn handler_no_arg() {
        assert_eq!(42, handler!(Test.n42())(()));
    }

    #[test]
    fn handler_with_arg() {
        assert_eq!(84, handler!(Test.double(42))(()));
        assert_eq!(84, handler!(Test.add(42,42))(()));
        assert_eq!(85, handler!(Test.sum(42,42,1))(()));
    }

    #[test]
    fn handler_optional_arg_only() {
        assert_eq!(84, handler!(Test.double(_))(42));
    }

    #[test]
    fn handler_optional_arg_and_other() {
        assert_eq!(84, handler!(Test.add(_,42))(42));
        assert_eq!(85, handler!(Test.sum(_,42,1))(42));
    }

    #[test]
    fn update() {
        assert_eq!(42, update!(Test, Msg::N42)(()));
        assert_eq!(84, update!(Test, Msg::Double(42))(()));
        assert_eq!(84, update!(Test, Msg::Add(42,42))(()));
        assert_eq!(80, update!(Test, Msg::Sum(42,42,-4))(()));
    }

    #[test]
    fn update_with_optional_arg() {
        assert_eq!(84, update!(Test, Msg::Double(_))(42));
        assert_eq!(84, update!(Test, Msg::Add(_,42))(42));
        assert_eq!(80, update!(Test, Msg::Sum(_,42,42))(-4));
    }
}