//! The `select!` macro.

/// A helper macro for `select!` to hide the long list of macro patterns from the documentation.
///
/// The macro consists of two stages:
/// 1. Parsing
/// 2. Code generation
///
/// The parsing stage consists of these subparts:
/// 1. `@list`: Turns a list of tokens into a list of cases.
/// 2. `@list_errorN`: Diagnoses the syntax error.
/// 3. `@case`: Parses a single case and verifies its argument list.
///
/// The codegen stage consists of these subparts:
/// 1. `@init`: Attempts to optimize `select!` away and initializes the list of handles.
/// 1. `@count`: Counts the listed cases.
/// 3. `@add`: Adds send/receive operations to the list of handles and starts selection.
/// 4. `@complete`: Completes the selected send/receive operation.
///
/// If the parsing stage encounters a syntax error or the codegen stage ends up with too many
/// cases to process, the macro fails with a compile-time error.
#[doc(hidden)]
#[macro_export]
macro_rules! crossbeam_channel_internal {
    // The list is empty. Now check the arguments of each processed case.
    (@list
        ()
        ($($head:tt)*)
    ) => {
        $crate::crossbeam_channel_internal!(
            @case
            ($($head)*)
            ()
            ()
        )
    };
    // If necessary, insert an empty argument list after `default`.
    (@list
        (default => $($tail:tt)*)
        ($($head:tt)*)
    ) => {
        $crate::crossbeam_channel_internal!(
            @list
            (default() => $($tail)*)
            ($($head)*)
        )
    };
    // But print an error if `default` is followed by a `->`.
    (@list
        (default -> $($tail:tt)*)
        ($($head:tt)*)
    ) => {
        compile_error!(
            "expected `=>` after `default` case, found `->`"
        )
    };
    // Print an error if there's an `->` after the argument list in the default case.
    (@list
        (default $args:tt -> $($tail:tt)*)
        ($($head:tt)*)
    ) => {
        compile_error!(
            "expected `=>` after `default` case, found `->`"
        )
    };
    // Print an error if there is a missing result in a recv case.
    (@list
        (recv($($args:tt)*) => $($tail:tt)*)
        ($($head:tt)*)
    ) => {
        compile_error!(
            "expected `->` after `recv` case, found `=>`"
        )
    };
    // Print an error if there is a missing result in a send case.
    (@list
        (send($($args:tt)*) => $($tail:tt)*)
        ($($head:tt)*)
    ) => {
        compile_error!(
            "expected `->` after `send` operation, found `=>`"
        )
    };
    // Make sure the arrow and the result are not repeated.
    (@list
        ($case:ident $args:tt -> $res:tt -> $($tail:tt)*)
        ($($head:tt)*)
    ) => {
        compile_error!("expected `=>`, found `->`")
    };
    // Print an error if there is a semicolon after the block.
    (@list
        ($case:ident $args:tt $(-> $res:pat)* => $body:block; $($tail:tt)*)
        ($($head:tt)*)
    ) => {
        compile_error!(
            "did you mean to put a comma instead of the semicolon after `}`?"
        )
    };
    // The first case is separated by a comma.
    (@list
        ($case:ident ($($args:tt)*) $(-> $res:pat)* => $body:expr, $($tail:tt)*)
        ($($head:tt)*)
    ) => {
        $crate::crossbeam_channel_internal!(
            @list
            ($($tail)*)
            ($($head)* $case ($($args)*) $(-> $res)* => { $body },)
        )
    };
    // Don't require a comma after the case if it has a proper block.
    (@list
        ($case:ident ($($args:tt)*) $(-> $res:pat)* => $body:block $($tail:tt)*)
        ($($head:tt)*)
    ) => {
        $crate::crossbeam_channel_internal!(
            @list
            ($($tail)*)
            ($($head)* $case ($($args)*) $(-> $res)* => { $body },)
        )
    };
    // Only one case remains.
    (@list
        ($case:ident ($($args:tt)*) $(-> $res:pat)* => $body:expr $(,)?)
        ($($head:tt)*)
    ) => {
        $crate::crossbeam_channel_internal!(
            @list
            ()
            ($($head)* $case ($($args)*) $(-> $res)* => { $body },)
        )
    };
    // Diagnose and print an error.
    (@list
        ($($tail:tt)*)
        ($($head:tt)*)
    ) => {
        $crate::crossbeam_channel_internal!(@list_error1 $($tail)*)
    };
    // Stage 1: check the case type.
    (@list_error1 recv $($tail:tt)*) => {
        $crate::crossbeam_channel_internal!(@list_error2 recv $($tail)*)
    };
    (@list_error1 send $($tail:tt)*) => {
        $crate::crossbeam_channel_internal!(@list_error2 send $($tail)*)
    };
    (@list_error1 default $($tail:tt)*) => {
        $crate::crossbeam_channel_internal!(@list_error2 default $($tail)*)
    };
    (@list_error1 $t:tt $($tail:tt)*) => {
        compile_error!(
            concat!(
                "expected one of `recv`, `send`, or `default`, found `",
                stringify!($t),
                "`",
            )
        )
    };
    (@list_error1 $($tail:tt)*) => {
        $crate::crossbeam_channel_internal!(@list_error2 $($tail)*);
    };
    // Stage 2: check the argument list.
    (@list_error2 $case:ident) => {
        compile_error!(
            concat!(
                "missing argument list after `",
                stringify!($case),
                "`",
            )
        )
    };
    (@list_error2 $case:ident => $($tail:tt)*) => {
        compile_error!(
            concat!(
                "missing argument list after `",
                stringify!($case),
                "`",
            )
        )
    };
    (@list_error2 $($tail:tt)*) => {
        $crate::crossbeam_channel_internal!(@list_error3 $($tail)*)
    };
    // Stage 3: check the `=>` and what comes after it.
    (@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)*) => {
        compile_error!(
            concat!(
                "missing `=>` after `",
                stringify!($case),
                "` case",
            )
        )
    };
    (@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)* =>) => {
        compile_error!(
            "expected expression after `=>`"
        )
    };
    (@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)* => $body:expr; $($tail:tt)*) => {
        compile_error!(
            concat!(
                "did you mean to put a comma instead of the semicolon after `",
                stringify!($body),
                "`?",
            )
        )
    };
    (@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)* => recv($($a:tt)*) $($tail:tt)*) => {
        compile_error!(
            "expected an expression after `=>`"
        )
    };
    (@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)* => send($($a:tt)*) $($tail:tt)*) => {
        compile_error!(
            "expected an expression after `=>`"
        )
    };
    (@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)* => default($($a:tt)*) $($tail:tt)*) => {
        compile_error!(
            "expected an expression after `=>`"
        )
    };
    (@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)* => $f:ident($($a:tt)*) $($tail:tt)*) => {
        compile_error!(
            concat!(
                "did you mean to put a comma after `",
                stringify!($f),
                "(",
                stringify!($($a)*),
                ")`?",
            )
        )
    };
    (@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)* => $f:ident!($($a:tt)*) $($tail:tt)*) => {
        compile_error!(
            concat!(
                "did you mean to put a comma after `",
                stringify!($f),
                "!(",
                stringify!($($a)*),
                ")`?",
            )
        )
    };
    (@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)* => $f:ident![$($a:tt)*] $($tail:tt)*) => {
        compile_error!(
            concat!(
                "did you mean to put a comma after `",
                stringify!($f),
                "![",
                stringify!($($a)*),
                "]`?",
            )
        )
    };
    (@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)* => $f:ident!{$($a:tt)*} $($tail:tt)*) => {
        compile_error!(
            concat!(
                "did you mean to put a comma after `",
                stringify!($f),
                "!{",
                stringify!($($a)*),
                "}`?",
            )
        )
    };
    (@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)* => $body:tt $($tail:tt)*) => {
        compile_error!(
            concat!(
                "did you mean to put a comma after `",
                stringify!($body),
                "`?",
            )
        )
    };
    (@list_error3 $case:ident($($args:tt)*) -> => $($tail:tt)*) => {
        compile_error!("missing pattern after `->`")
    };
    (@list_error3 $case:ident($($args:tt)*) $t:tt $(-> $r:pat)* => $($tail:tt)*) => {
        compile_error!(
            concat!(
                "expected `->`, found `",
                stringify!($t),
                "`",
            )
        )
    };
    (@list_error3 $case:ident($($args:tt)*) -> $t:tt $($tail:tt)*) => {
        compile_error!(
            concat!(
                "expected a pattern, found `",
                stringify!($t),
                "`",
            )
        )
    };
    (@list_error3 recv($($args:tt)*) $t:tt $($tail:tt)*) => {
        compile_error!(
            concat!(
                "expected `->`, found `",
                stringify!($t),
                "`",
            )
        )
    };
    (@list_error3 send($($args:tt)*) $t:tt $($tail:tt)*) => {
        compile_error!(
            concat!(
                "expected `->`, found `",
                stringify!($t),
                "`",
            )
        )
    };
    (@list_error3 recv $args:tt $($tail:tt)*) => {
        compile_error!(
            concat!(
                "expected an argument list after `recv`, found `",
                stringify!($args),
                "`",
            )
        )
    };
    (@list_error3 send $args:tt $($tail:tt)*) => {
        compile_error!(
            concat!(
                "expected an argument list after `send`, found `",
                stringify!($args),
                "`",
            )
        )
    };
    (@list_error3 default $args:tt $($tail:tt)*) => {
        compile_error!(
            concat!(
                "expected an argument list or `=>` after `default`, found `",
                stringify!($args),
                "`",
            )
        )
    };
    (@list_error3 $($tail:tt)*) => {
        $crate::crossbeam_channel_internal!(@list_error4 $($tail)*)
    };
    // Stage 4: fail with a generic error message.
    (@list_error4 $($tail:tt)*) => {
        compile_error!("invalid syntax")
    };

    // Success! All cases were parsed.
    (@case
        ()
        $cases:tt
        $default:tt
    ) => {
        $crate::crossbeam_channel_internal!(
            @init
            $cases
            $default
        )
    };

    // Check the format of a recv case.
    (@case
        (recv($r:expr $(,)?) -> $res:pat => $body:tt, $($tail:tt)*)
        ($($cases:tt)*)
        $default:tt
    ) => {
        $crate::crossbeam_channel_internal!(
            @case
            ($($tail)*)
            ($($cases)* recv($r) -> $res => $body,)
            $default
        )
    };
    // Print an error if the argument list is invalid.
    (@case
        (recv($($args:tt)*) -> $res:pat => $body:tt, $($tail:tt)*)
        ($($cases:tt)*)
        $default:tt
    ) => {
        compile_error!(
            concat!(
                "invalid argument list in `recv(",
                stringify!($($args)*),
                ")`",
            )
        )
    };
    // Print an error if there is no argument list.
    (@case
        (recv $t:tt $($tail:tt)*)
        ($($cases:tt)*)
        $default:tt
    ) => {
        compile_error!(
            concat!(
                "expected an argument list after `recv`, found `",
                stringify!($t),
                "`",
            )
        )
    };

    // Check the format of a send case.
    (@case
        (send($s:expr, $m:expr $(,)?) -> $res:pat => $body:tt, $($tail:tt)*)
        ($($cases:tt)*)
        $default:tt
    ) => {
        $crate::crossbeam_channel_internal!(
            @case
            ($($tail)*)
            ($($cases)* send($s, $m) -> $res => $body,)
            $default
        )
    };
    // Print an error if the argument list is invalid.
    (@case
        (send($($args:tt)*) -> $res:pat => $body:tt, $($tail:tt)*)
        ($($cases:tt)*)
        $default:tt
    ) => {
        compile_error!(
            concat!(
                "invalid argument list in `send(",
                stringify!($($args)*),
                ")`",
            )
        )
    };
    // Print an error if there is no argument list.
    (@case
        (send $t:tt $($tail:tt)*)
        ($($cases:tt)*)
        $default:tt
    ) => {
        compile_error!(
            concat!(
                "expected an argument list after `send`, found `",
                stringify!($t),
                "`",
            )
        )
    };

    // Check the format of a default case.
    (@case
        (default() => $body:tt, $($tail:tt)*)
        $cases:tt
        ()
    ) => {
        $crate::crossbeam_channel_internal!(
            @case
            ($($tail)*)
            $cases
            (default() => $body,)
        )
    };
    // Check the format of a default case with timeout.
    (@case
        (default($timeout:expr $(,)?) => $body:tt, $($tail:tt)*)
        $cases:tt
        ()
    ) => {
        $crate::crossbeam_channel_internal!(
            @case
            ($($tail)*)
            $cases
            (default($timeout) => $body,)
        )
    };
    // Check for duplicate default cases...
    (@case
        (default $($tail:tt)*)
        $cases:tt
        ($($def:tt)+)
    ) => {
        compile_error!(
            "there can be only one `default` case in a `select!` block"
        )
    };
    // Print an error if the argument list is invalid.
    (@case
        (default($($args:tt)*) => $body:tt, $($tail:tt)*)
        $cases:tt
        $default:tt
    ) => {
        compile_error!(
            concat!(
                "invalid argument list in `default(",
                stringify!($($args)*),
                ")`",
            )
        )
    };
    // Print an error if there is an unexpected token after `default`.
    (@case
        (default $t:tt $($tail:tt)*)
        $cases:tt
        $default:tt
    ) => {
        compile_error!(
            concat!(
                "expected an argument list or `=>` after `default`, found `",
                stringify!($t),
                "`",
            )
        )
    };

    // The case was not consumed, therefore it must be invalid.
    (@case
        ($case:ident $($tail:tt)*)
        $cases:tt
        $default:tt
    ) => {
        compile_error!(
            concat!(
                "expected one of `recv`, `send`, or `default`, found `",
                stringify!($case),
                "`",
            )
        )
    };

    // Optimize `select!` into `try_recv()`.
    (@init
        (recv($r:expr) -> $res:pat => $recv_body:tt,)
        (default() => $default_body:tt,)
    ) => {{
        match $r {
            ref _r => {
                let _r: &$crate::Receiver<_> = _r;
                match _r.try_recv() {
                    ::std::result::Result::Err($crate::TryRecvError::Empty) => {
                        $default_body
                    }
                    _res => {
                        let _res = _res.map_err(|_| $crate::RecvError);
                        let $res = _res;
                        $recv_body
                    }
                }
            }
        }
    }};
    // Optimize `select!` into `recv()`.
    (@init
        (recv($r:expr) -> $res:pat => $body:tt,)
        ()
    ) => {{
        match $r {
            ref _r => {
                let _r: &$crate::Receiver<_> = _r;
                let _res = _r.recv();
                let $res = _res;
                $body
            }
        }
    }};
    // Optimize `select!` into `recv_timeout()`.
    (@init
        (recv($r:expr) -> $res:pat => $recv_body:tt,)
        (default($timeout:expr) => $default_body:tt,)
    ) => {{
        match $r {
            ref _r => {
                let _r: &$crate::Receiver<_> = _r;
                match _r.recv_timeout($timeout) {
                    ::std::result::Result::Err($crate::RecvTimeoutError::Timeout) => {
                        $default_body
                    }
                    _res => {
                        let _res = _res.map_err(|_| $crate::RecvError);
                        let $res = _res;
                        $recv_body
                    }
                }
            }
        }
    }};

    // // Optimize the non-blocking case with two receive operations.
    // (@init
    //     (recv($r1:expr) -> $res1:pat => $recv_body1:tt,)
    //     (recv($r2:expr) -> $res2:pat => $recv_body2:tt,)
    //     (default() => $default_body:tt,)
    // ) => {{
    //     match $r1 {
    //         ref _r1 => {
    //             let _r1: &$crate::Receiver<_> = _r1;
    //
    //             match $r2 {
    //                 ref _r2 => {
    //                     let _r2: &$crate::Receiver<_> = _r2;
    //
    //                     // TODO(stjepang): Implement this optimization.
    //                 }
    //             }
    //         }
    //     }
    // }};
    // // Optimize the blocking case with two receive operations.
    // (@init
    //     (recv($r1:expr) -> $res1:pat => $body1:tt,)
    //     (recv($r2:expr) -> $res2:pat => $body2:tt,)
    //     ()
    // ) => {{
    //     match $r1 {
    //         ref _r1 => {
    //             let _r1: &$crate::Receiver<_> = _r1;
    //
    //             match $r2 {
    //                 ref _r2 => {
    //                     let _r2: &$crate::Receiver<_> = _r2;
    //
    //                     // TODO(stjepang): Implement this optimization.
    //                 }
    //             }
    //         }
    //     }
    // }};
    // // Optimize the case with two receive operations and a timeout.
    // (@init
    //     (recv($r1:expr) -> $res1:pat => $recv_body1:tt,)
    //     (recv($r2:expr) -> $res2:pat => $recv_body2:tt,)
    //     (default($timeout:expr) => $default_body:tt,)
    // ) => {{
    //     match $r1 {
    //         ref _r1 => {
    //             let _r1: &$crate::Receiver<_> = _r1;
    //
    //             match $r2 {
    //                 ref _r2 => {
    //                     let _r2: &$crate::Receiver<_> = _r2;
    //
    //                     // TODO(stjepang): Implement this optimization.
    //                 }
    //             }
    //         }
    //     }
    // }};

    // // Optimize `select!` into `try_send()`.
    // (@init
    //     (send($s:expr, $m:expr) -> $res:pat => $send_body:tt,)
    //     (default() => $default_body:tt,)
    // ) => {{
    //     match $s {
    //         ref _s => {
    //             let _s: &$crate::Sender<_> = _s;
    //             // TODO(stjepang): Implement this optimization.
    //         }
    //     }
    // }};
    // // Optimize `select!` into `send()`.
    // (@init
    //     (send($s:expr, $m:expr) -> $res:pat => $body:tt,)
    //     ()
    // ) => {{
    //     match $s {
    //         ref _s => {
    //             let _s: &$crate::Sender<_> = _s;
    //             // TODO(stjepang): Implement this optimization.
    //         }
    //     }
    // }};
    // // Optimize `select!` into `send_timeout()`.
    // (@init
    //     (send($s:expr, $m:expr) -> $res:pat => $body:tt,)
    //     (default($timeout:expr) => $body:tt,)
    // ) => {{
    //     match $s {
    //         ref _s => {
    //             let _s: &$crate::Sender<_> = _s;
    //             // TODO(stjepang): Implement this optimization.
    //         }
    //     }
    // }};

    // Create the list of handles and add operations to it.
    (@init
        ($($cases:tt)*)
        $default:tt
    ) => {{
        const _LEN: usize = $crate::crossbeam_channel_internal!(@count ($($cases)*));
        let _handle: &$crate::internal::SelectHandle = &$crate::never::<()>();

        #[allow(unused_mut)]
        let mut _sel = [(_handle, 0, ::std::ptr::null()); _LEN];

        $crate::crossbeam_channel_internal!(
            @add
            _sel
            ($($cases)*)
            $default
            (
                (0usize _oper0)
                (1usize _oper1)
                (2usize _oper2)
                (3usize _oper3)
                (4usize _oper4)
                (5usize _oper5)
                (6usize _oper6)
                (7usize _oper7)
                (8usize _oper8)
                (9usize _oper9)
                (10usize _oper10)
                (11usize _oper11)
                (12usize _oper12)
                (13usize _oper13)
                (14usize _oper14)
                (15usize _oper15)
                (16usize _oper16)
                (17usize _oper17)
                (18usize _oper18)
                (19usize _oper19)
                (20usize _oper20)
                (21usize _oper21)
                (22usize _oper22)
                (23usize _oper23)
                (24usize _oper24)
                (25usize _oper25)
                (26usize _oper26)
                (27usize _oper27)
                (28usize _oper28)
                (29usize _oper29)
                (30usize _oper30)
                (31usize _oper31)
            )
            ()
        )
    }};

    // Count the listed cases.
    (@count ()) => {
        0
    };
    (@count ($oper:ident $args:tt -> $res:pat => $body:tt, $($cases:tt)*)) => {
        1 + $crate::crossbeam_channel_internal!(@count ($($cases)*))
    };

    // Run blocking selection.
    (@add
        $sel:ident
        ()
        ()
        $labels:tt
        $cases:tt
    ) => {{
        let _oper: $crate::SelectedOperation<'_> = {
            let _oper = $crate::internal::select(&mut $sel);

            // Erase the lifetime so that `sel` can be dropped early even without NLL.
            unsafe { ::std::mem::transmute(_oper) }
        };

        $crate::crossbeam_channel_internal! {
            @complete
            $sel
            _oper
            $cases
        }
    }};
    // Run non-blocking selection.
    (@add
        $sel:ident
        ()
        (default() => $body:tt,)
        $labels:tt
        $cases:tt
    ) => {{
        let _oper: ::std::option::Option<$crate::SelectedOperation<'_>> = {
            let _oper = $crate::internal::try_select(&mut $sel);

            // Erase the lifetime so that `sel` can be dropped early even without NLL.
            unsafe { ::std::mem::transmute(_oper) }
        };

        match _oper {
            None => {
                { $sel };
                $body
            }
            Some(_oper) => {
                $crate::crossbeam_channel_internal! {
                    @complete
                    $sel
                    _oper
                    $cases
                }
            }
        }
    }};
    // Run selection with a timeout.
    (@add
        $sel:ident
        ()
        (default($timeout:expr) => $body:tt,)
        $labels:tt
        $cases:tt
    ) => {{
        let _oper: ::std::option::Option<$crate::SelectedOperation<'_>> = {
            let _oper = $crate::internal::select_timeout(&mut $sel, $timeout);

            // Erase the lifetime so that `sel` can be dropped early even without NLL.
            unsafe { ::std::mem::transmute(_oper) }
        };

        match _oper {
            ::std::option::Option::None => {
                { $sel };
                $body
            }
            ::std::option::Option::Some(_oper) => {
                $crate::crossbeam_channel_internal! {
                    @complete
                    $sel
                    _oper
                    $cases
                }
            }
        }
    }};
    // Have we used up all labels?
    (@add
        $sel:ident
        $input:tt
        $default:tt
        ()
        $cases:tt
    ) => {
        compile_error!("too many operations in a `select!` block")
    };
    // Add a receive operation to `sel`.
    (@add
        $sel:ident
        (recv($r:expr) -> $res:pat => $body:tt, $($tail:tt)*)
        $default:tt
        (($i:tt $var:ident) $($labels:tt)*)
        ($($cases:tt)*)
    ) => {{
        match $r {
            ref _r => {
                let $var: &$crate::Receiver<_> = unsafe {
                    let _r: &$crate::Receiver<_> = _r;

                    // Erase the lifetime so that `sel` can be dropped early even without NLL.
                    unsafe fn unbind<'a, T>(x: &T) -> &'a T {
                        ::std::mem::transmute(x)
                    }
                    unbind(_r)
                };
                $sel[$i] = ($var, $i, $var as *const $crate::Receiver<_> as *const u8);

                $crate::crossbeam_channel_internal!(
                    @add
                    $sel
                    ($($tail)*)
                    $default
                    ($($labels)*)
                    ($($cases)* [$i] recv($var) -> $res => $body,)
                )
            }
        }
    }};
    // Add a send operation to `sel`.
    (@add
        $sel:ident
        (send($s:expr, $m:expr) -> $res:pat => $body:tt, $($tail:tt)*)
        $default:tt
        (($i:tt $var:ident) $($labels:tt)*)
        ($($cases:tt)*)
    ) => {{
        match $s {
            ref _s => {
                let $var: &$crate::Sender<_> = unsafe {
                    let _s: &$crate::Sender<_> = _s;

                    // Erase the lifetime so that `sel` can be dropped early even without NLL.
                    unsafe fn unbind<'a, T>(x: &T) -> &'a T {
                        ::std::mem::transmute(x)
                    }
                    unbind(_s)
                };
                $sel[$i] = ($var, $i, $var as *const $crate::Sender<_> as *const u8);

                $crate::crossbeam_channel_internal!(
                    @add
                    $sel
                    ($($tail)*)
                    $default
                    ($($labels)*)
                    ($($cases)* [$i] send($var, $m) -> $res => $body,)
                )
            }
        }
    }};

    // Complete a receive operation.
    (@complete
        $sel:ident
        $oper:ident
        ([$i:tt] recv($r:ident) -> $res:pat => $body:tt, $($tail:tt)*)
    ) => {{
        if $oper.index() == $i {
            let _res = $oper.recv($r);
            { $sel };

            let $res = _res;
            $body
        } else {
            $crate::crossbeam_channel_internal! {
                @complete
                $sel
                $oper
                ($($tail)*)
            }
        }
    }};
    // Complete a send operation.
    (@complete
        $sel:ident
        $oper:ident
        ([$i:tt] send($s:ident, $m:expr) -> $res:pat => $body:tt, $($tail:tt)*)
    ) => {{
        if $oper.index() == $i {
            let _res = $oper.send($s, $m);
            { $sel };

            let $res = _res;
            $body
        } else {
            $crate::crossbeam_channel_internal! {
                @complete
                $sel
                $oper
                ($($tail)*)
            }
        }
    }};
    // Panic if we don't identify the selected case, but this should never happen.
    (@complete
        $sel:ident
        $oper:ident
        ()
    ) => {{
        unreachable!(
            "internal error in crossbeam-channel: invalid case"
        )
    }};

    // Catches a bug within this macro (should not happen).
    (@$($tokens:tt)*) => {
        compile_error!(
            concat!(
                "internal error in crossbeam-channel: ",
                stringify!(@$($tokens)*),
            )
        )
    };

    // The entry points.
    () => {
        compile_error!("empty `select!` block")
    };
    ($($case:ident $(($($args:tt)*))* => $body:expr $(,)*)*) => {
        $crate::crossbeam_channel_internal!(
            @list
            ($($case $(($($args)*))* => { $body },)*)
            ()
        )
    };
    ($($tokens:tt)*) => {
        $crate::crossbeam_channel_internal!(
            @list
            ($($tokens)*)
            ()
        )
    };
}

/// Selects from a set of channel operations.
///
/// This macro allows you to define a set of channel operations, wait until any one of them becomes
/// ready, and finally execute it. If multiple operations are ready at the same time, a random one
/// among them is selected.
///
/// It is also possible to define a `default` case that gets executed if none of the operations are
/// ready, either right away or for a certain duration of time.
///
/// An operation is considered to be ready if it doesn't have to block. Note that it is ready even
/// when it will simply return an error because the channel is disconnected.
///
/// The `select!` macro is a convenience wrapper around [`Select`]. However, it cannot select over a
/// dynamically created list of channel operations.
///
/// [`Select`]: super::Select
///
/// # Examples
///
/// Block until a send or a receive operation is selected:
///
/// ```
/// use crossbeam_channel::{select, unbounded};
///
/// let (s1, r1) = unbounded();
/// let (s2, r2) = unbounded();
/// s1.send(10).unwrap();
///
/// // Since both operations are initially ready, a random one will be executed.
/// select! {
///     recv(r1) -> msg => assert_eq!(msg, Ok(10)),
///     send(s2, 20) -> res => {
///         assert_eq!(res, Ok(()));
///         assert_eq!(r2.recv(), Ok(20));
///     }
/// }
/// ```
///
/// Select from a set of operations without blocking:
///
/// ```
/// use std::thread;
/// use std::time::Duration;
/// use crossbeam_channel::{select, unbounded};
///
/// let (s1, r1) = unbounded();
/// let (s2, r2) = unbounded();
///
/// thread::spawn(move || {
///     thread::sleep(Duration::from_secs(1));
///     s1.send(10).unwrap();
/// });
/// thread::spawn(move || {
///     thread::sleep(Duration::from_millis(500));
///     s2.send(20).unwrap();
/// });
///
/// // None of the operations are initially ready.
/// select! {
///     recv(r1) -> msg => panic!(),
///     recv(r2) -> msg => panic!(),
///     default => println!("not ready"),
/// }
/// ```
///
/// Select over a set of operations with a timeout:
///
/// ```
/// use std::thread;
/// use std::time::Duration;
/// use crossbeam_channel::{select, unbounded};
///
/// let (s1, r1) = unbounded();
/// let (s2, r2) = unbounded();
///
/// thread::spawn(move || {
///     thread::sleep(Duration::from_secs(1));
///     s1.send(10).unwrap();
/// });
/// thread::spawn(move || {
///     thread::sleep(Duration::from_millis(500));
///     s2.send(20).unwrap();
/// });
///
/// // None of the two operations will become ready within 100 milliseconds.
/// select! {
///     recv(r1) -> msg => panic!(),
///     recv(r2) -> msg => panic!(),
///     default(Duration::from_millis(100)) => println!("timed out"),
/// }
/// ```
///
/// Optionally add a receive operation to `select!` using [`never`]:
///
/// ```
/// use std::thread;
/// use std::time::Duration;
/// use crossbeam_channel::{select, never, unbounded};
///
/// let (s1, r1) = unbounded();
/// let (s2, r2) = unbounded();
///
/// thread::spawn(move || {
///     thread::sleep(Duration::from_secs(1));
///     s1.send(10).unwrap();
/// });
/// thread::spawn(move || {
///     thread::sleep(Duration::from_millis(500));
///     s2.send(20).unwrap();
/// });
///
/// // This receiver can be a `Some` or a `None`.
/// let r2 = Some(&r2);
///
/// // None of the two operations will become ready within 100 milliseconds.
/// select! {
///     recv(r1) -> msg => panic!(),
///     recv(r2.unwrap_or(&never())) -> msg => assert_eq!(msg, Ok(20)),
/// }
/// ```
///
/// To optionally add a timeout to `select!`, see the [example] for [`never`].
///
/// [`never`]: super::never
/// [example]: super::never#examples
#[macro_export]
macro_rules! select {
    ($($tokens:tt)*) => {
        $crate::crossbeam_channel_internal!(
            $($tokens)*
        )
    };
}
