//! Combinators and utilities for working with `Future`s, `Stream`s, `Sink`s,
//! and the `AsyncRead` and `AsyncWrite` traits.

#![cfg_attr(feature = "cfg-target-has-atomic", feature(cfg_target_has_atomic))]
#![cfg_attr(feature = "read-initializer", feature(read_initializer))]
#![cfg_attr(feature = "write-all-vectored", feature(io_slice_advance))]

#![cfg_attr(not(feature = "std"), no_std)]
#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms, unreachable_pub)]
#![warn(clippy::all)]

// The solution for this lint is not available on 1.39 which is the current minimum supported version.
// Can be removed as of minimum supported 1.40 or if https://github.com/rust-lang/rust-clippy/issues/3941
// get's implemented.
#![allow(clippy::mem_replace_with_default)]

#![doc(test(attr(deny(warnings), allow(dead_code, unused_assignments, unused_variables))))]

#![doc(html_root_url = "https://docs.rs/futures-util/0.3.5")]

#[cfg(all(feature = "cfg-target-has-atomic", not(feature = "unstable")))]
compile_error!("The `cfg-target-has-atomic` feature requires the `unstable` feature as an explicit opt-in to unstable features");

#[cfg(all(feature = "bilock", not(feature = "unstable")))]
compile_error!("The `bilock` feature requires the `unstable` feature as an explicit opt-in to unstable features");

#[cfg(all(feature = "read-initializer", not(feature = "unstable")))]
compile_error!("The `read-initializer` feature requires the `unstable` feature as an explicit opt-in to unstable features");

#[cfg(feature = "alloc")]
extern crate alloc;

#[macro_use(ready)]
extern crate futures_core;

// Macro re-exports
pub use futures_core::ready;
pub use pin_utils::pin_mut;

// Not public API.
#[cfg(feature = "async-await")]
#[macro_use]
#[doc(hidden)]
pub mod async_await;
#[cfg(feature = "async-await")]
#[doc(hidden)]
pub use self::async_await::*;

// Not public API.
#[doc(hidden)]
pub use futures_core::core_reexport;

// Not public API.
#[cfg(feature = "async-await")]
#[doc(hidden)]
pub mod __reexport {
    #[doc(hidden)]
    pub use crate::*;
}

macro_rules! cfg_target_has_atomic {
    ($($item:item)*) => {$(
        #[cfg_attr(feature = "cfg-target-has-atomic", cfg(target_has_atomic = "ptr"))]
        $item
    )*};
}

#[cfg(feature = "sink")]
macro_rules! delegate_sink {
    ($field:ident, $item:ty) => {
        fn poll_ready(
            self: core::pin::Pin<&mut Self>,
            cx: &mut $crate::core_reexport::task::Context<'_>,
        ) -> $crate::core_reexport::task::Poll<Result<(), Self::Error>> {
            self.project().$field.poll_ready(cx)
        }

        fn start_send(
            self: core::pin::Pin<&mut Self>,
            item: $item,
        ) -> Result<(), Self::Error> {
            self.project().$field.start_send(item)
        }

        fn poll_flush(
            self: core::pin::Pin<&mut Self>,
            cx: &mut $crate::core_reexport::task::Context<'_>,
        ) -> $crate::core_reexport::task::Poll<Result<(), Self::Error>> {
            self.project().$field.poll_flush(cx)
        }

        fn poll_close(
            self: core::pin::Pin<&mut Self>,
            cx: &mut $crate::core_reexport::task::Context<'_>,
        ) -> $crate::core_reexport::task::Poll<Result<(), Self::Error>> {
            self.project().$field.poll_close(cx)
        }
    }
}

macro_rules! delegate_future {
    ($field:ident) => {
        fn poll(
            self: core::pin::Pin<&mut Self>,
            cx: &mut $crate::core_reexport::task::Context<'_>,
        ) -> $crate::core_reexport::task::Poll<Self::Output> {
            self.project().$field.poll(cx)
        }
    }
}

macro_rules! delegate_stream {
    ($field:ident) => {
        fn poll_next(
            self: core::pin::Pin<&mut Self>,
            cx: &mut $crate::core_reexport::task::Context<'_>,
        ) -> $crate::core_reexport::task::Poll<Option<Self::Item>> {
            self.project().$field.poll_next(cx)
        }
        fn size_hint(&self) -> (usize, Option<usize>) {
            self.$field.size_hint()
        }
    }
}

#[cfg(feature = "io")]
#[cfg(feature = "std")]
macro_rules! delegate_async_write {
    ($field:ident) => {
        fn poll_write(self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>, buf: &[u8])
            -> core::task::Poll<std::io::Result<usize>>
        {
            self.project().$field.poll_write(cx, buf)
        }
        fn poll_write_vectored(self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>, bufs: &[std::io::IoSlice<'_>])
            -> core::task::Poll<std::io::Result<usize>>
        {
            self.project().$field.poll_write_vectored(cx, bufs)
        }
        fn poll_flush(self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>)
            -> core::task::Poll<std::io::Result<()>>
        {
            self.project().$field.poll_flush(cx)
        }
        fn poll_close(self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>)
            -> core::task::Poll<std::io::Result<()>>
        {
            self.project().$field.poll_close(cx)
        }
    }
}

#[cfg(feature = "io")]
#[cfg(feature = "std")]
macro_rules! delegate_async_read {
    ($field:ident) => {
        #[cfg(feature = "read-initializer")]
        unsafe fn initializer(&self) -> $crate::io::Initializer {
            self.$field.initializer()
        }

        fn poll_read(self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>, buf: &mut [u8])
            -> core::task::Poll<std::io::Result<usize>>
        {
            self.project().$field.poll_read(cx, buf)
        }

        fn poll_read_vectored(self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>, bufs: &mut [std::io::IoSliceMut<'_>])
            -> core::task::Poll<std::io::Result<usize>>
        {
            self.project().$field.poll_read_vectored(cx, bufs)
        }
    }
}

#[cfg(feature = "io")]
#[cfg(feature = "std")]
macro_rules! delegate_async_buf_read {
    ($field:ident) => {
        fn poll_fill_buf(
            self: core::pin::Pin<&mut Self>,
            cx: &mut core::task::Context<'_>,
        ) -> core::task::Poll<std::io::Result<&[u8]>> {
            self.project().$field.poll_fill_buf(cx)
        }
    
        fn consume(self: core::pin::Pin<&mut Self>, amt: usize) {
            self.project().$field.consume(amt)
        }
    }
}

macro_rules! delegate_access_inner {
    ($field:ident, $inner:ty, ($($ind:tt)*)) => {
        /// Acquires a reference to the underlying sink or stream that this combinator is
        /// pulling from.
        pub fn get_ref(&self) -> &$inner {
            (&self.$field) $($ind get_ref())*
        }

        /// Acquires a mutable reference to the underlying sink or stream that this
        /// combinator is pulling from.
        ///
        /// Note that care must be taken to avoid tampering with the state of the
        /// sink or stream which may otherwise confuse this combinator.
        pub fn get_mut(&mut self) -> &mut $inner {
            (&mut self.$field) $($ind get_mut())*
        }

        /// Acquires a pinned mutable reference to the underlying sink or stream that this
        /// combinator is pulling from.
        ///
        /// Note that care must be taken to avoid tampering with the state of the
        /// sink or stream which may otherwise confuse this combinator.
        pub fn get_pin_mut(self: core::pin::Pin<&mut Self>) -> core::pin::Pin<&mut $inner> {
            self.project().$field $($ind get_pin_mut())*
        }

        /// Consumes this combinator, returning the underlying sink or stream.
        ///
        /// Note that this may discard intermediate state of this combinator, so
        /// care should be taken to avoid losing resources when this is called.
        pub fn into_inner(self) -> $inner {
            self.$field $($ind into_inner())*
        }
    }
}

macro_rules! delegate_all {
    (@trait Future $name:ident < $($arg:ident),* > ($t:ty) $(where $($bound:tt)*)*) => {
        impl<$($arg),*> futures_core::future::Future for $name<$($arg),*> where $t: futures_core::future::Future $(, $($bound)*)* {
            type Output = <$t as futures_core::future::Future>::Output;

            delegate_future!(inner);
        }
    };
    (@trait FusedFuture $name:ident < $($arg:ident),* > ($t:ty) $(where $($bound:tt)*)*) => {
        impl<$($arg),*> futures_core::future::FusedFuture for $name<$($arg),*> where $t: futures_core::future::FusedFuture $(, $($bound)*)* {
            fn is_terminated(&self) -> bool {
                self.inner.is_terminated()
            }
        }
    };
    (@trait Stream $name:ident < $($arg:ident),* > ($t:ty) $(where $($bound:tt)*)*) => {
        impl<$($arg),*> futures_core::stream::Stream for $name<$($arg),*> where $t: futures_core::stream::Stream $(, $($bound)*)* {
            type Item = <$t as futures_core::stream::Stream>::Item;

            delegate_stream!(inner);
        }
    };
    (@trait FusedStream $name:ident < $($arg:ident),* > ($t:ty) $(where $($bound:tt)*)*) => {
        impl<$($arg),*> futures_core::stream::FusedStream for $name<$($arg),*> where $t: futures_core::stream::FusedStream $(, $($bound)*)* {
            fn is_terminated(&self) -> bool {
                self.inner.is_terminated()
            }
        }
    };
    (@trait Sink $name:ident < $($arg:ident),* > ($t:ty) $(where $($bound:tt)*)*) => {
        #[cfg(feature = "sink")]
        impl<_Item, $($arg),*> futures_sink::Sink<_Item> for $name<$($arg),*> where $t: futures_sink::Sink<_Item> $(, $($bound)*)* {
            type Error = <$t as futures_sink::Sink<_Item>>::Error;

            delegate_sink!(inner, _Item);
        }
    };
    (@trait Debug $name:ident < $($arg:ident),* > ($t:ty) $(where $($bound:tt)*)*) => {
        impl<$($arg),*> core::fmt::Debug for $name<$($arg),*> where $t: core::fmt::Debug $(, $($bound)*)* {
            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
                core::fmt::Debug::fmt(&self.inner, f)
            }
        }
    };
    (@trait AccessInner[$inner:ty, ($($ind:tt)*)] $name:ident < $($arg:ident),* > ($t:ty) $(where $($bound:tt)*)*) => {
        impl<$($arg),*> $name<$($arg),*> $(where $($bound)*)* {
            delegate_access_inner!(inner, $inner, ($($ind)*));
        }
    };
    (@trait New[|$($param:ident: $paramt:ty),*| $cons:expr] $name:ident < $($arg:ident),* > ($t:ty) $(where $($bound:tt)*)*) => {
        impl<$($arg),*> $name<$($arg),*> $(where $($bound)*)* {
            pub(crate) fn new($($param: $paramt),*) -> Self {
                Self { inner: $cons }
            }
        }
    };
    ($(#[$attr:meta])* $name:ident<$($arg:ident),*>($t:ty) : $ftrait:ident $([$($targs:tt)*])* $({$($item:tt)*})* $(where $($bound:tt)*)*) => {
        #[pin_project::pin_project]
        #[must_use = "futures/streams/sinks do nothing unless you `.await` or poll them"]
        $(#[$attr])*
        pub struct $name< $($arg),* > $(where $($bound)*)* { #[pin] inner:$t }

        impl<$($arg),*> $name< $($arg),* > $(where $($bound)*)* {
            $($($item)*)*
        }

        delegate_all!(@trait $ftrait $([$($targs)*])* $name<$($arg),*>($t) $(where $($bound)*)*);
    };
    ($(#[$attr:meta])* $name:ident<$($arg:ident),*>($t:ty) : $ftrait:ident $([$($ftargs:tt)*])* + $strait:ident $([$($stargs:tt)*])* $(+ $trait:ident $([$($targs:tt)*])*)* $({$($item:tt)*})* $(where $($bound:tt)*)*) => {
        delegate_all!($(#[$attr])* $name<$($arg),*>($t) : $strait $([$($stargs)*])* $(+ $trait $([$($targs)*])*)* $({$($item)*})* $(where $($bound)*)*);

        delegate_all!(@trait $ftrait $([$($ftargs)*])* $name<$($arg),*>($t) $(where $($bound)*)*);
    };
}

pub mod future;
#[doc(hidden)] pub use crate::future::{FutureExt, TryFutureExt};

pub mod stream;
#[doc(hidden)] pub use crate::stream::{StreamExt, TryStreamExt};

#[cfg(feature = "sink")]
pub mod sink;
#[cfg(feature = "sink")]
#[doc(hidden)] pub use crate::sink::SinkExt;

pub mod task;

pub mod never;

#[cfg(feature = "compat")]
pub mod compat;

#[cfg(feature = "io")]
#[cfg(feature = "std")]
pub mod io;
#[cfg(feature = "io")]
#[cfg(feature = "std")]
#[doc(hidden)] pub use crate::io::{AsyncReadExt, AsyncWriteExt, AsyncSeekExt, AsyncBufReadExt};

mod fns;


cfg_target_has_atomic! {
    #[cfg(feature = "alloc")]
    pub mod lock;
}
