<commit_before>use std::io;
use std::net::ToSocketAddrs;
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};

use futures::{ready, Sink, Stream, StreamExt};
use pin_project::{pin_project, project};
use tokio::io::{AsyncRead, AsyncWrite};
use tokio::net::TcpStream;
use tokio_rustls::rustls::ClientConfig;
use tokio_rustls::webpki::DNSNameRef;
use tokio_rustls::{client::TlsStream, TlsConnector};
use tokio_util::codec::{Decoder, Framed};

use crate::codec::{ImapCodec, ResponseData};
use imap_proto::builders::command::Command;
use imap_proto::{Request, RequestId, State};

pub type TlsClient = Client<TlsStream<TcpStream>>;

pub struct Client<T> {
    transport: Framed<T, ImapCodec>,
    state: State,
    request_ids: IdGenerator,
}

impl TlsClient {
    pub async fn connect(server: &str) -> io::Result<(ResponseData, Self)> {
        let addr = (server, 993).to_socket_addrs()?.next().ok_or_else(|| {
            io::Error::new(
                io::ErrorKind::Other,
                format!("no IP addresses found for {}", server),
            )
        })?;

        let mut tls_config = ClientConfig::new();
        tls_config
            .root_store
            .add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS);
        let connector: TlsConnector = Arc::new(tls_config).into();

        let stream = TcpStream::connect(&addr).await?;
        let stream = connector
            .connect(DNSNameRef::try_from_ascii_str(server).unwrap(), stream)
            .await?;
        let mut transport = ImapCodec::default().framed(stream);

        let greeting = match transport.next().await {
            Some(greeting) => Ok(greeting),
            None => Err(io::Error::new(io::ErrorKind::Other, "no greeting found")),
        }?;
        let client = Client {
            transport,
            state: State::NotAuthenticated,
            request_ids: IdGenerator::new(),
        };

        greeting.map(|greeting| (greeting, client))
    }

    pub fn call<C: Into<Command>>(&mut self, cmd: C) -> ResponseStream<TlsStream<TcpStream>> {
        let request_id = self.request_ids.next().unwrap(); // safe: never returns Err,
        ResponseStream {
            client: self,
            request_id,
            cmd: cmd.into(),
            state: ResponseStreamState::Start,
        }
    }
}

#[pin_project]
pub struct ResponseStream<'a, T> {
    #[pin]
    client: &'a mut Client<T>,
    request_id: RequestId,
    cmd: Command,
    state: ResponseStreamState,
}

impl<'a, T> Stream for ResponseStream<'a, T>
where
    T: AsyncRead + AsyncWrite + Unpin,
{
    type Item = Result<ResponseData, io::Error>;

    #[project]<commit_msg>Use new-style pin projections<commit_after>use std::io;
use std::net::ToSocketAddrs;
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};

use futures::{ready, Sink, Stream, StreamExt};
use pin_project::pin_project;
use tokio::io::{AsyncRead, AsyncWrite};
use tokio::net::TcpStream;
use tokio_rustls::rustls::ClientConfig;
use tokio_rustls::webpki::DNSNameRef;
use tokio_rustls::{client::TlsStream, TlsConnector};
use tokio_util::codec::{Decoder, Framed};

use crate::codec::{ImapCodec, ResponseData};
use imap_proto::builders::command::Command;
use imap_proto::{Request, RequestId, State};

pub type TlsClient = Client<TlsStream<TcpStream>>;

pub struct Client<T> {
    transport: Framed<T, ImapCodec>,
    state: State,
    request_ids: IdGenerator,
}

impl TlsClient {
    pub async fn connect(server: &str) -> io::Result<(ResponseData, Self)> {
        let addr = (server, 993).to_socket_addrs()?.next().ok_or_else(|| {
            io::Error::new(
                io::ErrorKind::Other,
                format!("no IP addresses found for {}", server),
            )
        })?;

        let mut tls_config = ClientConfig::new();
        tls_config
            .root_store
            .add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS);
        let connector: TlsConnector = Arc::new(tls_config).into();

        let stream = TcpStream::connect(&addr).await?;
        let stream = connector
            .connect(DNSNameRef::try_from_ascii_str(server).unwrap(), stream)
            .await?;
        let mut transport = ImapCodec::default().framed(stream);

        let greeting = match transport.next().await {
            Some(greeting) => Ok(greeting),
            None => Err(io::Error::new(io::ErrorKind::Other, "no greeting found")),
        }?;
        let client = Client {
            transport,
            state: State::NotAuthenticated,
            request_ids: IdGenerator::new(),
        };

        greeting.map(|greeting| (greeting, client))
    }

    pub fn call<C: Into<Command>>(&mut self, cmd: C) -> ResponseStream<TlsStream<TcpStream>> {
        let request_id = self.request_ids.next().unwrap(); // safe: never returns Err,
        ResponseStream {
            client: self,
            request_id,
            cmd: cmd.into(),
            state: ResponseStreamState::Start,
        }
    }
}

#[pin_project]
pub struct ResponseStream<'a, T> {
    #[pin]
    client: &'a mut Client<T>,
    request_id: RequestId,
    cmd: Command,
    state: ResponseStreamState,
}

impl<'a, T> Stream for ResponseStream<'a, T>
where
    T: AsyncRead + AsyncWrite + Unpin,
{
    type Item = Result<ResponseData, io::Error>;

{'fraction_non_alphanumeric': 0.14406461307287755, 'fraction_numerical': 0.0011269722013523666, 'mean_word_length': 3.08672294704528, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 39, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1970755', 'n_tokens_mistral': 1673, 'n_tokens_neox': 1700, 'n_words': 358}