//! <div class="warning">
//!
//! This module is not intended to be used directly. It is only public for
//! documentation purposes.
//!
//! </div>

#[cfg(unix)]
pub(crate) mod unix;
#[cfg(windows)]
pub(crate) mod windows;

use std::marker::PhantomData;

#[allow(missing_debug_implementations, reason = "XXX")]
#[doc(hidden)]
pub struct StreamSpec;

#[allow(missing_debug_implementations, reason = "XXX")]
#[doc(hidden)]
pub struct DgramSpec;

#[allow(missing_debug_implementations, reason = "XXX")]
#[doc(hidden)]
/// The socket sub-type is unknown or not applicable.
pub struct UnknownTy;

#[allow(missing_debug_implementations, reason = "XXX")]
#[doc(hidden)]
/// The socket is an established stream (connected to a peer).
pub struct StreamTy;

#[allow(missing_debug_implementations, reason = "XXX")]
#[doc(hidden)]
/// The socket is listening for incoming connections.
pub struct ListenerTy;

wrapper_lite::wrapper!(
    #[wrapper(Debug)]
    /// A unified socket type.
    ///
    /// <div class="warning">
    ///
    /// This type is exposed only to show documentations.
    ///
    /// </div>
    pub struct UniSocket<I, Spec, Ty> {
        inner: I,
        #[default(PhantomData)]
        _spec: PhantomData<Spec>,
        #[default(PhantomData)]
        _ty: PhantomData<Ty>,
    }
);

impl<I, Ty> UniSocket<I, StreamSpec, Ty> {
    const SOCKET_SPEC_PROTOCOL: socket2::Protocol = socket2::Protocol::TCP;
    const SOCKET_SPEC_TYPE: socket2::Type = socket2::Type::STREAM;
}

impl<I, Ty> UniSocket<I, DgramSpec, Ty> {
    const SOCKET_SPEC_PROTOCOL: socket2::Protocol = socket2::Protocol::UDP;
    const SOCKET_SPEC_TYPE: socket2::Type = socket2::Type::DGRAM;
}

macro_rules! impl_raw_socket_new {
    (| $socket:ident | $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        #[inline]
        /// Creates a new socket.
        ///
        /// The given socket address `domain` must be consistent with the one of
        /// the socket address you intend to bind or connect this socket to.
        ///
        /// ## Notes
        ///
        /// The `domain` here is not a domain name, but the `domain` (address family)
        /// returned by [`socket2::SockAddr::domain`].
        pub fn new(domain: Domain) -> io::Result<Self> {
            let ty = Self::SOCKET_SPEC_TYPE;

            #[cfg(any(
                target_os = "android",
                target_os = "dragonfly",
                target_os = "freebsd",
                target_os = "fuchsia",
                target_os = "illumos",
                target_os = "linux",
                target_os = "netbsd",
                target_os = "openbsd"
            ))]
            let ty = ty.nonblocking();

            let inner = match &domain {
                &Domain::IPV4 | &Domain::IPV6 => {
                    socket2::Socket::new(domain, ty, Some(Self::SOCKET_SPEC_PROTOCOL))
                }
                &Domain::UNIX => socket2::Socket::new(domain, ty, None),
                _ => Err(io::Error::other("unsupported socket address family")),
            }?;

            #[cfg(not(any(
                target_os = "android",
                target_os = "dragonfly",
                target_os = "freebsd",
                target_os = "fuchsia",
                target_os = "illumos",
                target_os = "linux",
                target_os = "netbsd",
                target_os = "openbsd"
            )))]
            inner.set_nonblocking(true)?;

            // On platforms with Berkeley-derived sockets, this allows to quickly
            // rebind a socket, without needing to wait for the OS to clean up the
            // previous one.
            //
            // On Windows, this allows rebinding sockets which are actively in use,
            // which allows “socket hijacking”, so we explicitly don't set it here.
            // https://docs.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse
            #[cfg(not(windows))]
            inner.set_reuse_address(true)?;

            let $socket = inner;

            $block.map(Self::from_inner)
        }
    };
}

pub(crate) use impl_raw_socket_new;

macro_rules! impl_raw_socket_bind {
    (|$self:ident, $addr:ident| $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        /// Binds the socket to the specified local address.
        ///
        /// This function directly corresponds to the `bind(2)` function on
        /// Windows and Unix.
        pub fn bind($self, $addr: &SockAddr) -> io::Result<Self> $block
    };
}

pub(crate) use impl_raw_socket_bind;

macro_rules! impl_bound_socket_local_addr {
    (|$self:ident| $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        #[inline]
        /// Returns the address of the local half of this socket.
        ///
        /// This directly corresponds to the `getsockname(2)` function on
        /// Windows and Unix.
        ///
        /// ## Errors
        ///
        /// - This may return an error if the socket is not bound, depending on
        ///   the OS behavior.
        pub fn local_addr(&$self) -> io::Result<SockAddr> $block
    };
}

pub(crate) use impl_bound_socket_local_addr;

macro_rules! impl_connected_socket_peer_addr {
    (|$self:ident| $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        #[inline]
        /// Returns the address of the remote peer of this socket.
        ///
        /// This function directly corresponds to the `getpeername(2)` function on
        /// Windows and Unix.
        pub fn peer_addr(&$self) -> io::Result<SockAddr> $block
    };
}

pub(crate) use impl_connected_socket_peer_addr;

macro_rules! impl_tcp_socket_connect {
    (|$self:ident, $addr:ident| $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        /// Initiates a connection on this socket to the specified address, and
        /// wait for the connection to be established.
        pub async fn connect($self, $addr: &SockAddr) -> io::Result<TcpStream> $block
    }
}

pub(crate) use impl_tcp_socket_connect;

macro_rules! impl_tcp_socket_listen {
    (|$self:ident, $backlog:ident| $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        /// Marks the socket as ready to accept incoming connection requests.
        ///
        /// ## Notes
        ///
        /// - This method must be called in the context of a `tokio` runtime.
        pub fn listen($self, $backlog: i32) -> io::Result<TcpListener> $block
    };
}

pub(crate) use impl_tcp_socket_listen;

macro_rules! impl_tcp_stream_connect {
    (| $addr:ident | $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        /// Initiates a connection to the specified address, and wait
        /// for the connection to be established.
        ///
        /// [`TcpSocket`] may help if you want to customize the socket before
        /// connecting to the peer.
        pub async fn connect($addr: &SockAddr) -> io::Result<TcpStream> $block
    };
}

pub(crate) use impl_tcp_stream_connect;

macro_rules! impl_tcp_stream_peek {
    (|$self:ident, $buf:ident| $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        /// Reads data from the stream without consuming it, and returns the
        /// number of bytes peeked.
        pub async fn peek(
            &$self,
            $buf: &mut [MaybeUninit<u8>]
        ) -> io::Result<usize> $block
    };
}

pub(crate) use impl_tcp_stream_peek;

macro_rules! impl_tcp_stream_poll_peek {
    (| $self:ident, $cx:ident, $buf:ident | $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        /// The polling version of [`peek`].
        ///
        /// ## Notes
        ///
        /// - On multiple calls to [`poll_peek`], only the `Waker` from the
        ///   `Context` passed to the most recent call is scheduled to receive a
        ///   wakeup. This method is intended for cases where creating and
        ///   pinning a future via [`peek`] is not feasible. Where possible,
        ///   using [`peek`] is preferred, as this supports polling from
        ///   multiple tasks at once.
        ///
        /// [`peek`]: Self::peek
        /// [`poll_peek`]: Self::poll_peek
        pub fn poll_peek(
            &$self,
            $cx: &mut Context<'_>,
            $buf: &mut [MaybeUninit<u8>],
        ) -> Poll<io::Result<usize>> $block
    };
}

pub(crate) use impl_tcp_stream_poll_peek;

macro_rules! impl_tcp_stream_read {
    (|$self:ident, $buf:ident| $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        #[inline]
        /// Reads data from the stream, and returns the number of bytes read.
        ///
        /// ## Cancellation
        ///
        /// It's guaranteed that no data would be read into the buffer if the
        /// future were successfully cancelled.
        pub async fn read(
            &$self,
            $buf: &mut [MaybeUninit<u8>]
        ) -> io::Result<usize> $block
    };
}

pub(crate) use impl_tcp_stream_read;

macro_rules! impl_tcp_stream_write {
    (|$self:ident, $buf:ident| $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        #[inline]
        /// Writes data to the stream, and returns the number of bytes written.
        ///
        /// ## Cancellation
        ///
        /// It's guaranteed that no data would be written if the future were
        /// successfully cancelled.
        pub async fn write(&$self, $buf: &[u8]) -> io::Result<usize> $block
    };
}

pub(crate) use impl_tcp_stream_write;

macro_rules! impl_tcp_stream_split {
    (|$self:ident| $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        /// Splits this stream into an owned read half and an owned write half,
        /// which can be used independently.
        pub fn split($self) -> (OwnedReadHalf, OwnedWriteHalf) $block
    };
}

pub(crate) use impl_tcp_stream_split;

macro_rules! impl_tcp_stream_read_fn {
    (|$self:ident, $f:ident| async $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        /// Waits for "read" readiness, and then performs the provided IO
        /// operation.
        ///
        /// ## Notes
        ///
        /// - The provided function `f` should only perform a non-blocking
        ///   "read" operation on the socket.
        /// - The provided function `f` should only return `WouldBlock` when the
        ///   socket is not yet ready for reading, otherwise the readiness state
        ///   on the `tokio` side and the readiness state of the underlying file
        ///   descriptor would be out of sync, leading to undefined behaviors.
        pub async fn read_fn<F, R>($self: &Self, mut $f: F) -> io::Result<R>
        where
            F: FnMut(&socket2::Socket) -> io::Result<R>,
        $block
    };
    (|$self:ident, $f:ident| $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        #[must_use = "Futures do nothing unless you `.await` or poll them"]
        /// Waits for "read" readiness, and then performs the provided IO
        /// operation.
        ///
        /// ## Notes
        ///
        /// - The provided function `f` should only perform a non-blocking
        ///   "read" operation on the socket.
        /// - The provided function `f` should only return `WouldBlock` when the
        ///   socket is not yet ready for reading, otherwise the readiness state
        ///   on the `tokio` side and the readiness state of the underlying file
        ///   descriptor would be out of sync, leading to undefined behaviors.
        pub fn read_fn<'f, F, R>(
            $self: &'f Self,
            #[allow(unused_mut, reason = "XXX")] mut $f: F
        ) -> impl Future<Output = io::Result<R>> + 'f
        where
            F: FnMut(&socket2::Socket) -> io::Result<R> + 'f,
            R: 'f,
        $block
    };
}

pub(crate) use impl_tcp_stream_read_fn;

macro_rules! impl_tcp_stream_poll_read_fn {
    (|$self:ident, $cx:ident, $f:ident| $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        /// The polling version of [`read_fn`].
        ///
        /// ## Notes
        ///
        /// In addition to the important notes we pointed out at [`read_fn`]:
        ///
        /// - On multiple calls to [`poll_read_fn`], only the `Waker` from the
        ///   `Context` passed to the most recent call is scheduled to receive a
        ///   wakeup. This method is intended for cases where creating and
        ///   pinning a future via [`read_fn`] is not feasible. Where possible,
        ///   using [`read_fn`] is preferred, as this supports polling from
        ///   multiple tasks at once.
        ///
        /// [`read_fn`]: Self::read_fn
        /// [`poll_read_fn`]: Self::poll_read_fn
        pub fn poll_read_fn<F, R>(
            $self: &Self,
            $cx: &mut Context<'_>,
            #[allow(unused_mut, reason = "XXX")] mut $f: F
        ) -> Poll<io::Result<R>>
        where
            F: FnMut(&socket2::Socket) -> io::Result<R>,
        $block
    }
}

pub(crate) use impl_tcp_stream_poll_read_fn;

macro_rules! impl_tcp_stream_write_fn {
    (|$self:ident, $f:ident| async $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        /// Waits for "write" readiness, and then performs the provided IO
        /// operation.
        ///
        /// ## Notes
        ///
        /// - The provided function `f` should only perform a non-blocking
        ///   "write" operation on the socket.
        /// - The provided function `f` should only return `WouldBlock` when the
        ///   socket is not yet ready for writing, otherwise the readiness state
        ///   on the `tokio` side and the readiness state of the underlying file
        ///   descriptor would be out of sync, leading to undefined behaviors.
        pub async fn write_fn<F, R>($self: &Self, mut $f: F) -> io::Result<R>
        where
            F: FnMut(&socket2::Socket) -> io::Result<R>,
        $block
    };
    (|$self:ident, $f:ident| $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        #[must_use = "Futures do nothing unless you `.await` or poll them"]
        /// Waits for "write" readiness, and then performs the provided IO
        /// operation.
        ///
        /// ## Notes
        ///
        /// - The provided function `f` should only perform a non-blocking
        ///   "write" operation on the socket.
        /// - The provided function `f` should only return `WouldBlock` when the
        ///   socket is not yet ready for writing, otherwise the readiness state
        ///   on the `tokio` side and the readiness state of the underlying file
        ///   descriptor would be out of sync, leading to undefined behaviors.
        pub fn write_fn<'f, F, R>(
            $self: &'f Self,
            #[allow(unused_mut, reason = "XXX")] mut $f: F
        ) -> impl Future<Output = io::Result<R>> + 'f
        where
            F: FnMut(&socket2::Socket) -> io::Result<R> + 'f,
            R: 'f,
        $block
    };
}

pub(crate) use impl_tcp_stream_write_fn;

macro_rules! impl_tcp_stream_poll_write_fn {
    (|$self:ident, $cx:ident, $f:ident| $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        /// The polling version of [`write_fn`].
        ///
        /// ## Notes
        ///
        /// In addition to the important notes we pointed out at [`write_fn`]:
        ///
        /// - On multiple calls to [`poll_write_fn`], only the `Waker` from the
        ///   `Context` passed to the most recent call is scheduled to receive a
        ///   wakeup. This method is intended for cases where creating and
        ///   pinning a future via [`write_fn`] is not feasible. Where possible,
        ///   using [`write_fn`] is preferred, as this supports polling from
        ///   multiple tasks at once.
        ///
        /// [`write_fn`]: Self::write_fn
        /// [`poll_write_fn`]: Self::poll_write_fn
        pub fn poll_write_fn<F, R>(
            $self: &Self,
            $cx: &mut Context<'_>,
            #[allow(unused_mut, reason = "XXX")] mut $f: F
        ) -> Poll<io::Result<R>>
        where
            F: FnMut(&socket2::Socket) -> io::Result<R>,
        $block
    };
}

pub(crate) use impl_tcp_stream_poll_write_fn;

macro_rules! impl_tcp_listener_bind {
    (|$addr:ident| $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        #[allow(clippy::unused_async_trait_impl, reason = "Windows platform requires so")]
        /// Creates a new [`TcpListener`] bound to the specified address.
        ///
        /// To customize the socket before binding, you can use [`TcpSocket`]
        /// instead.
        pub async fn bind($addr: &SockAddr) -> io::Result<Self> $block
    };
}

pub(crate) use impl_tcp_listener_bind;

macro_rules! impl_tcp_listener_accept {
    (| $self:ident | $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        #[inline]
        /// Accepts an incoming connection to this listener, and returns the
        /// accepted stream and the peer address.
        ///
        /// ## Notes
        ///
        /// This method will internally retry on non-deadly errors, including:
        ///
        /// - `ECONNABORTED`: the connection has been aborted by the peer.
        /// - `ECONNRESET`: the connection has been reset by the peer.
        /// - `EINTR`: the call was interrupted by a signal.
        /// - `EMFILE` / `ENFILE`: the per-process / system-wide limit on the number
        ///   of open file descriptors has been reached (for UNIX platforms).
        pub async fn accept(&$self) -> io::Result<(TcpStream, SockAddr)> $block
    };
}

pub(crate) use impl_tcp_listener_accept;

macro_rules! impl_tcp_listener_poll_accept {
    (|$self:ident, $cx:ident| $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        #[inline]
        /// The polling version of [`accept`].
        ///
        /// ## Notes
        ///
        /// - On multiple calls to [`poll_accept`], only the `Waker` from the
        ///   `Context` passed to the most recent call is scheduled to receive a
        ///   wakeup. This method is intended for cases where creating and pinning a
        ///   future via [`accept`] is not feasible. Where possible, using
        ///   [`accept`] is preferred, as this supports polling from multiple tasks
        ///   at once.
        /// - Unlike [`accept`], this method will not retry on `EMFILE` / `ENFILE`
        ///   errors, and will instead return the error immediately.
        ///
        /// [`accept`]: Self::accept
        /// [`poll_accept`]: Self::poll_accept
        pub fn poll_accept(
            &$self,
            $cx: &mut Context<'_>,
        ) -> Poll<io::Result<(TcpStream, SockAddr)>> $block
    };
}

pub(crate) use impl_tcp_listener_poll_accept;

macro_rules! impl_udp_socket_connect {
    (|$self:ident, $addr:ident| $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        /// Set the default remote address for this socket. This allows using the
        /// [`send`] and [`recv`] methods on the socket, which do not require
        /// specifying the remote address on each call.
        ///
        /// [`send`]: Self::send
        /// [`recv`]: Self::recv
        pub fn connect(&$self, $addr: &SockAddr) -> io::Result<()> $block
    };
}

pub(crate) use impl_udp_socket_connect;

macro_rules! impl_udp_socket_peek {
    (|$self:ident, $buf:ident| $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        /// Receives a single datagram message which is from the [`connected`]
        /// remote address without removing it from the queue, and returns the
        /// number of bytes peeked.
        ///
        /// ## Behaviours
        ///
        /// On Windows, if the data is larger than the buffer specified, the
        /// buffer is filled with the first part of the data, and [`peek`]
        /// returns the error `WSAEMSGSIZE(10040)`. The excess data is lost.
        /// Make sure to always use a sufficiently large buffer to hold the
        /// maximum UDP packet size, which can be up to 65536 bytes in size.
        ///
        /// On macOS, an error will be returned if you pass a zero-sized buffer.
        ///
        /// [`connected`]: Self::connect
        /// [`peek`]: Self::peek
        pub async fn peek(&$self, $buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> $block
    };
}

pub(crate) use impl_udp_socket_peek;

macro_rules! impl_udp_socket_poll_peek {
    (|$self:ident, $cx:ident, $buf:ident| $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        /// The polling version of [`peek`].
        ///
        /// ## Notes
        ///
        /// - On multiple calls to [`poll_peek`], only the `Waker` from the
        ///   `Context` passed to the most recent call is scheduled to receive a
        ///   wakeup. This method is intended for cases where creating and
        ///   pinning a future via [`peek`] is not feasible. Where possible,
        ///   using [`peek`] is preferred, as this supports polling from
        ///   multiple tasks at once.
        ///
        /// [`peek`]: Self::peek
        /// [`poll_peek`]: Self::poll_peek
        pub fn poll_peek(
            &$self,
            $cx: &mut Context<'_>,
            $buf: &mut [MaybeUninit<u8>],
        ) -> Poll<io::Result<usize>> $block
    };
}

pub(crate) use impl_udp_socket_poll_peek;

macro_rules! impl_udp_socket_peek_sender {
    (|$self:ident| $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        /// Receives a single datagram without removing it from the queue, and
        /// returns the source address of the datagram.
        pub async fn peek_sender(&$self) -> io::Result<SockAddr> $block
    };
}

pub(crate) use impl_udp_socket_peek_sender;

macro_rules! impl_udp_socket_poll_peek_sender {
    (|$self:ident, $cx:ident| $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        /// The polling version of [`peek_sender`].
        ///
        /// ## Notes
        ///
        /// - On multiple calls to [`poll_peek_sender`], only the `Waker` from
        ///   the `Context` passed to the most recent call is scheduled to
        ///   receive a wakeup. This method is intended for cases where creating
        ///   and pinning a future via [`peek_sender`] is not feasible. Where
        ///   possible, using [`peek_sender`] is preferred, as this supports
        ///   polling from multiple tasks at once.
        ///
        /// [`peek_sender`]: Self::peek_sender
        /// [`poll_peek_sender`]: Self::poll_peek_sender
        pub fn poll_peek_sender(
            &$self,
            $cx: &mut Context<'_>,
        ) -> Poll<io::Result<SockAddr>> $block
    };
}

pub(crate) use impl_udp_socket_poll_peek_sender;

macro_rules! impl_udp_socket_peek_from {
    (|$self:ident, $buf:ident| $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        /// Receives a single datagram message without removing it from the
        /// queue, and returns the number of bytes peeked and the source
        /// address of the datagram.
        ///
        /// If you're merely interested in learning the sender of the data at
        /// the head of the queue, try [`peek_sender`].
        ///
        /// If the socket is [`connected`] to a remote peer, then the source
        /// address is always the remote peer's address and [`peek`] may
        /// be used instead.
        ///
        /// ## Behaviours
        ///
        /// On Windows, if the data is larger than the buffer specified, the
        /// buffer is filled with the first part of the data, and [`peek_from`]
        /// returns the error `WSAEMSGSIZE(10040)`. The excess data is lost.
        /// Make sure to always use a sufficiently large buffer to hold the
        /// maximum UDP packet size, which can be up to 65536 bytes in size.
        ///
        /// On macOS, an error will be returned if you pass a zero-sized buffer.
        ///
        /// [`connected`]: Self::connect
        /// [`peek`]: Self::peek
        /// [`peek_from`]: Self::peek_from
        /// [`peek_sender`]: Self::peek_sender
        pub async fn peek_from(
            &$self,
            $buf: &mut [MaybeUninit<u8>],
        ) -> io::Result<(usize, SockAddr)> $block
    };
}

pub(crate) use impl_udp_socket_peek_from;

macro_rules! impl_udp_socket_poll_peek_from {
    (|$self:ident, $cx:ident, $buf:ident| $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        /// The polling version of [`peek_from`].
        ///
        /// ## Notes
        ///
        /// - On multiple calls to [`poll_peek_from`], only the `Waker` from the
        ///   `Context` passed to the most recent call is scheduled to receive a
        ///   wakeup. This method is intended for cases where creating and
        ///   pinning a future via [`peek_from`] is not feasible. Where
        ///   possible, using [`peek_from`]  is preferred, as this supports
        ///   polling from multiple tasks at once.
        ///
        /// [`peek_from`]: Self::peek_from
        /// [`poll_peek_from`]: Self::poll_peek_from
        pub fn poll_peek_from(
            &$self,
            $cx: &mut Context<'_>,
            $buf: &mut [MaybeUninit<u8>],
        ) -> Poll<io::Result<(usize, SockAddr)>> $block
    };
}

pub(crate) use impl_udp_socket_poll_peek_from;

macro_rules! impl_udp_socket_send {
    (|$self:ident, $buf:ident| $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        /// Sends a datagram message to the remote address the socket is
        /// [`connected`] to.
        ///
        /// ## Cancellation
        ///
        /// It's guaranteed that the message would not be sent if the future
        /// receives a cancellation.
        ///
        /// ## Errors
        ///
        /// - If the socket is not [`connected`] to a remote address, this
        ///   method would fail with an error.
        ///
        /// - If the remote address had sent back an ICMP "Destination
        ///   Unreachable" message, this method may fail with a
        ///   [`ConnectionRefused`] error. However, this behavior depends
        ///   on the OS.
        ///
        /// - ...
        ///
        /// [`connected`]: Self::connect
        /// [`ConnectionRefused`]: io::ErrorKind::ConnectionRefused
        pub async fn send(&$self, $buf: &[u8]) -> io::Result<usize> $block
    };
}

pub(crate) use impl_udp_socket_send;

macro_rules! impl_udp_socket_poll_send {
    (|$self:ident, $cx:ident, $buf:ident| $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        /// The polling version of [`send`].
        ///
        /// ## Notes
        ///
        /// - On multiple calls to [`poll_send`], only the `Waker` from the
        ///   `Context` passed to the most recent call is scheduled to receive a
        ///   wakeup. This method is intended for cases where creating and
        ///   pinning a future via [`send`] is not feasible. Where possible,
        ///   using [`send`] is preferred, as this supports polling from
        ///   multiple tasks at once.
        ///
        /// [`send`]: Self::send
        /// [`poll_send`]: Self::poll_send
        pub fn poll_send(
            &$self,
            $cx: &mut Context<'_>,
            $buf: &[u8],
        ) -> Poll<io::Result<usize>> $block
    };
}

pub(crate) use impl_udp_socket_poll_send;

macro_rules! impl_udp_socket_send_to {
    (|$self:ident, $buf:ident, $target:ident| $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        /// Sends a datagram message to the specified remote address.
        ///
        /// ## Cancellation
        ///
        /// It's guaranteed that the message would not be sent if the future
        /// receives a cancellation.
        pub async fn send_to(
            &$self,
            $buf: &[u8],
            $target: &SockAddr,
        ) -> io::Result<usize> $block
    };
}

pub(crate) use impl_udp_socket_send_to;

macro_rules! impl_udp_socket_poll_send_to {
    (|$self:ident, $cx:ident, $buf:ident, $target:ident| $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        /// The polling version of [`send_to`].
        ///
        /// ## Notes
        ///
        /// - On multiple calls to [`poll_send_to`], only the `Waker` from the
        ///   `Context` passed to the most recent call is scheduled to receive a
        ///   wakeup. This method is intended for cases where creating and
        ///   pinning a future via [`send_to`] is not feasible. Where possible,
        ///   using [`send_to`] is preferred, as this supports polling from
        ///   multiple tasks at once.
        ///
        /// [`send_to`]: Self::send_to
        /// [`poll_send_to`]: Self::poll_send_to
        pub fn poll_send_to(
            &$self,
            $cx: &mut Context<'_>,
            $buf: &[u8],
            $target: &SockAddr,
        ) -> Poll<io::Result<usize>> $block
    };
}

pub(crate) use impl_udp_socket_poll_send_to;

macro_rules! impl_udp_socket_recv {
    (|$self:ident, $buf:ident| $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        /// Receives a single datagram message which is from the [`connected`]
        /// remote address, and returns the number of bytes received.
        ///
        /// ## Behaviours
        ///
        /// On Windows, if the data is larger than the buffer specified, the
        /// buffer is filled with the first part of the data, and [`recv`]
        /// returns the error `WSAEMSGSIZE(10040)`. The excess data is lost.
        /// Make sure to always use a sufficiently large buffer to hold the
        /// maximum UDP packet size, which can be up to 65536 bytes in size.
        ///
        /// On macOS, an error will be returned if you pass a zero-sized buffer.
        ///
        /// When the socket is "disconnected", calling `recv` on the socket may
        /// immediately return an error like `ECONNREFUSED` or `ECONNRESET`, but
        /// callers should not rely on this behavior. It should be noted that
        /// UDP is stateless, and the OS can only determine that the socket has
        /// been "disconnected" when attempting to send a UDP packet to the
        /// "connected" peer (upon receiving an "ICMP Unreachable" message from
        /// the peer). Before that, calling `recv` will hang indefinitely.
        /// What's more, for a UNIX domain socket, such a mechanism does
        /// not seem to exist at all. The implementation should add a timeout
        /// when necessary.
        ///
        /// ## Cancellation
        ///
        /// It's guaranteed that no message would be received if the future
        /// receives a cancellation.
        ///
        /// [`connected`]: Self::connect
        /// [`recv`]: Self::recv
        pub async fn recv(&$self, $buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> $block
    };
}

pub(crate) use impl_udp_socket_recv;

macro_rules! impl_udp_socket_poll_recv {
    (|$self:ident, $cx:ident, $buf:ident| $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        /// The polling version of [`recv`].
        ///
        /// ## Notes
        ///
        /// - On multiple calls to [`poll_recv`], only the `Waker` from the
        ///   `Context` passed to the most recent call is scheduled to receive a
        ///   wakeup. This method is intended for cases where creating and
        ///   pinning a future via [`recv`] is not feasible. Where possible,
        ///   using [`recv`] is preferred, as this supports polling from
        ///   multiple tasks at once.
        ///
        /// [`recv`]: Self::recv
        /// [`poll_recv`]: Self::poll_recv
        pub fn poll_recv(
            &$self,
            $cx: &mut Context<'_>,
            $buf: &mut [MaybeUninit<u8>],
        ) -> Poll<io::Result<usize>> $block
    };
}

pub(crate) use impl_udp_socket_poll_recv;

macro_rules! impl_udp_socket_recv_from {
    (|$self:ident, $buf:ident| $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        /// Receives a single datagram message, and returns the number of bytes
        /// received and the source address of the datagram.
        ///
        /// If the socket is [`connected`] to a remote peer, then the source
        /// address is always the remote peer's address and [`recv`] may
        /// be used instead.
        ///
        /// ## Behaviours
        ///
        /// On Windows, if the data is larger than the buffer specified, the
        /// buffer is filled with the first part of the data, and [`recv`]
        /// returns the error `WSAEMSGSIZE(10040)`. The excess data is lost.
        /// Make sure to always use a sufficiently large buffer to hold the
        /// maximum UDP packet size, which can be up to 65536 bytes in size.
        ///
        /// On macOS, an error will be returned if you pass a zero-sized buffer.
        ///
        /// When the socket is "disconnected", calling `recv` on the socket may
        /// immediately return an error like `ECONNREFUSED` or `ECONNRESET`, but
        /// callers should not rely on this behavior. It should be noted that
        /// UDP is stateless, and the OS can only determine that the socket has
        /// been "disconnected" when attempting to send a UDP packet to the
        /// "connected" peer (upon receiving an "ICMP Unreachable" message from
        /// the peer). Before that, calling `recv` will hang indefinitely.
        /// What's more, for a UNIX domain socket, such a mechanism does
        /// not seem to exist at all. The implementation should add a timeout
        /// when necessary.
        ///
        /// [`recv`]: Self::recv
        /// [`connected`]: Self::connect
        pub async fn recv_from(
            &$self,
            $buf: &mut [MaybeUninit<u8>],
        ) -> io::Result<(usize, SockAddr)> $block
    };
}

pub(crate) use impl_udp_socket_recv_from;

macro_rules! impl_udp_socket_poll_recv_from {
    (|$self:ident, $cx:ident, $buf:ident| $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        /// The polling version of [`recv_from`].
        ///
        /// ## Notes
        ///
        /// - On multiple calls to [`poll_recv_from`], only the `Waker` from the
        ///   `Context` passed to the most recent call is scheduled to receive a
        ///   wakeup. This method is intended for cases where creating and
        ///   pinning a future via [`recv_from`] is not feasible. Where
        ///   possible, using [`recv_from`]  is preferred, as this supports
        ///   polling from multiple tasks at once.
        ///
        /// [`recv_from`]: Self::recv_from
        /// [`poll_recv_from`]: Self::poll_recv_from
        pub fn poll_recv_from(
            &$self,
            $cx: &mut Context<'_>,
            $buf: &mut [MaybeUninit<u8>],
        ) -> Poll<io::Result<(usize, SockAddr)>> $block
    };
}

pub(crate) use impl_udp_socket_poll_recv_from;

macro_rules! impl_udp_socket_recv_fn {
    (|$self:ident, $f:ident| async $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        /// Waits for "read" readiness, and then performs the provided IO
        /// operation.
        ///
        /// ## Notes
        ///
        /// - The provided function `f` should only perform a non-blocking
        ///   "read" operation on the socket.
        /// - The provided function `f` should only return `WouldBlock` when the
        ///   socket is not yet ready for reading, otherwise the readiness state
        ///   on the `tokio` side and the readiness state of the underlying file
        ///   descriptor would be out of sync, leading to undefined behaviors.
        pub async fn recv_fn<F, R>($self: &Self, mut $f: F) -> io::Result<R>
        where
            F: FnMut(&socket2::Socket) -> io::Result<R>,
        $block
    };
    (|$self:ident, $f:ident| $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        #[must_use = "Futures do nothing unless you `.await` or poll them"]
        /// Waits for "read" readiness, and then performs the provided IO
        /// operation.
        ///
        /// ## Notes
        ///
        /// - The provided function `f` should only perform a non-blocking
        ///   "read" operation on the socket.
        /// - The provided function `f` should only return `WouldBlock` when the
        ///   socket is not yet ready for reading, otherwise the readiness state
        ///   on the `tokio` side and the readiness state of the underlying file
        ///   descriptor would be out of sync, leading to undefined behaviors.
        pub fn recv_fn<'f, F, R>(
            $self: &'f Self,
            mut $f: F
        ) -> impl Future<Output = io::Result<R>> + 'f
        where
            F: FnMut(&socket2::Socket) -> io::Result<R> + 'f,
            R: 'f,
        $block
    };
}

pub(crate) use impl_udp_socket_recv_fn;

macro_rules! impl_udp_socket_poll_recv_fn {
    (|$self:ident, $cx:ident, $f:ident| $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        /// The polling version of [`recv_fn`].
        ///
        /// ## Notes
        ///
        /// In addition to the important notes we pointed out at [`recv_fn`]:
        ///
        /// - On multiple calls to [`poll_recv_fn`], only the `Waker` from the
        ///   `Context` passed to the most recent call is scheduled to receive a
        ///   wakeup. This method is intended for cases where creating and
        ///   pinning a future via [`recv_fn`] is not feasible. Where possible,
        ///   using [`recv_fn`] is preferred, as this supports polling from
        ///   multiple tasks at once.
        ///
        /// [`recv_fn`]: Self::recv_fn
        /// [`poll_recv_fn`]: Self::poll_recv_fn
        pub fn poll_recv_fn<F, R>(
            $self: &Self,
            $cx: &mut Context<'_>,
            #[allow(unused_mut, reason = "XXX")] mut $f: F
        ) -> Poll<io::Result<R>>
        where
            F: FnMut(&socket2::Socket) -> io::Result<R>,
        $block
    }
}

pub(crate) use impl_udp_socket_poll_recv_fn;

macro_rules! impl_udp_socket_send_fn {
    (|$self:ident, $f:ident| async $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        /// Waits for "write" readiness, and then performs the provided IO
        /// operation.
        ///
        /// ## Notes
        ///
        /// - The provided function `f` should only perform a non-blocking
        ///   "write" operation on the socket.
        /// - The provided function `f` should only return `WouldBlock` when the
        ///   socket is not yet ready for writing, otherwise the readiness state
        ///   on the `tokio` side and the readiness state of the underlying file
        ///   descriptor would be out of sync, leading to undefined behaviors.
        pub async fn send_fn<F, R>($self: &Self, mut $f: F) -> io::Result<R>
        where
            F: FnMut(&socket2::Socket) -> io::Result<R>,
        $block
    };
    (|$self:ident, $f:ident| $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        #[must_use = "Futures do nothing unless you `.await` or poll them"]
        /// Waits for "write" readiness, and then performs the provided IO
        /// operation.
        ///
        /// ## Notes
        ///
        /// - The provided function `f` should only perform a non-blocking
        ///   "write" operation on the socket.
        /// - The provided function `f` should only return `WouldBlock` when the
        ///   socket is not yet ready for writing, otherwise the readiness state
        ///   on the `tokio` side and the readiness state of the underlying file
        ///   descriptor would be out of sync, leading to undefined behaviors.
        pub fn send_fn<'f, F, R>(
            $self: &'f Self,
            mut $f: F
        ) -> impl Future<Output = io::Result<R>> + 'f
        where
            F: FnMut(&socket2::Socket) -> io::Result<R> + 'f,
            R: 'f,
        $block
    };
}

pub(crate) use impl_udp_socket_send_fn;

macro_rules! impl_udp_socket_poll_send_fn {
    (|$self:ident, $cx:ident, $f:ident| $block:block) => {
        #[cfg_attr(docsrs, doc(auto_cfg = false))]
        /// The polling version of [`send_fn`].
        ///
        /// ## Notes
        ///
        /// In addition to the important notes we pointed out at [`send_fn`]:
        ///
        /// - On multiple calls to [`poll_send_fn`], only the `Waker` from the
        ///   `Context` passed to the most recent call is scheduled to receive a
        ///   wakeup. This method is intended for cases where creating and
        ///   pinning a future via [`send_fn`] is not feasible. Where possible,
        ///   using [`send_fn`] is preferred, as this supports polling from
        ///   multiple tasks at once.
        ///
        /// [`send_fn`]: Self::send_fn
        /// [`poll_send_fn`]: Self::poll_send_fn
        pub fn poll_send_fn<F, R>(
            $self: &Self,
            $cx: &mut Context<'_>,
            #[allow(unused_mut, reason = "XXX")] mut $f: F
        ) -> Poll<io::Result<R>>
        where
            F: FnMut(&socket2::Socket) -> io::Result<R>,
        $block
    };
}

pub(crate) use impl_udp_socket_poll_send_fn;
