<commit_before>use crate::errors::{ScrapeError, ScrapeResult, ServiceErrorKind, ServiceResult};
use crate::service::download::DownloadProgress;
use crate::service::session::{self, HttpSession};
use crate::service::{
    Contest, DownloadProps, ExtractZip, PrintTargets as _PrintTargets, ProblemNameConversion,
    Service, SessionProps, UserNameAndPassword, ZipEntries, ZipEntriesSorting,
};
use crate::terminal::{HasTerm, Term, WriteAnsi};
use crate::testsuite::{SimpleSuite, TestSuite};

use itertools::Itertools as _Itertools;
use log::warn;
use once_cell::sync::Lazy;
use once_cell::sync_lazy;
use regex::Regex;
use reqwest::StatusCode;
use select::document::Document;
use select::predicate::{Predicate, Text};
use serde::{Deserialize, Deserializer};
use serde_derive::Deserialize;
use serde_json::json;
use tokio::runtime::{Runtime, TaskExecutor};

use std::borrow::Cow;
use std::collections::HashMap;
use std::fmt;
use std::io::{self, Write as _Write};

pub(crate) fn login(sess_props: SessionProps<impl Term>) -> ServiceResult<()> {
    Hackerrank::try_new(sess_props)?.login(LoginOption::Explicit)
}

pub(crate) fn download(
    mut sess_props: SessionProps<impl Term>,
    download_props: DownloadProps<String>,
) -> ServiceResult<()> {
    let download_props = download_props.convert_contest_and_problems(ProblemNameConversion::Kebab);
    download_props.print_targets(sess_props.term.stdout())?;
    Hackerrank::try_new(sess_props)?.download(&download_props)
}

struct Hackerrank<T: Term> {
    term: T,
    session: HttpSession,
    runtime: Runtime,
    credentials: UserNameAndPassword,
}

impl<T: Term> HasTerm for Hackerrank<T> {
    type Term = T;

    fn term(&mut self) -> &mut T {
        &mut self.term
    }
}

impl<T: Term> Service for Hackerrank<T> {
    type Write = T::Stdout;

    fn requirements(&mut self) -> (&mut T::Stdout, &mut HttpSession, &mut Runtime) {
        (self.term.stdout(), &mut self.session, &mut self.runtime)
    }
}

impl<T: Term> DownloadProgress for Hackerrank<T> {
    type Write = T::Stdout;

    fn requirements(&mut self) -> (&mut T::Stdout, &HttpSession, TaskExecutor) {
        (self.term.stdout(), &self.session, self.runtime.executor())
    }
}

impl<T: Term> ExtractZip for Hackerrank<T> {
    type Write = T::Stdout;

    fn out(&mut self) -> &mut T::Stdout {
        self.term.stdout()
    }
}

impl<T: Term> Hackerrank<T> {
    fn try_new(mut sess_props: SessionProps<T>) -> ServiceResult<Self> {
        let credentials = sess_props.credentials.hackerrank.clone();
        let mut runtime = Runtime::new()?;
        let session = sess_props.start_session(&mut runtime)?;
        Ok(Hackerrank {
            term: sess_props.term,
            session,
            runtime,
            credentials,
        })
    }

    fn login(&mut self, option: LoginOption) -> ServiceResult<()> {
        let mut res = self.get("/login").acceptable(&[200, 302]).send()?;
        if res.status() == StatusCode::FOUND && option == LoginOption::Explicit {
            writeln!(self.stderr(), "Already signed in.")?;
            self.stderr().flush()?;
        } else if res.status() == StatusCode::OK {
            let (mut username, mut password, on_test) = {
                match self.credentials.take() {
                    UserNameAndPassword::Some(username, password) => (username, password, true),
                    UserNameAndPassword::None => (
                        self.prompt_reply_stderr("Username: ")?,<commit_msg>`/login` to `/auth/login`<commit_after>use crate::errors::{ScrapeError, ScrapeResult, ServiceErrorKind, ServiceResult};
use crate::service::download::DownloadProgress;
use crate::service::session::{self, HttpSession};
use crate::service::{
    Contest, DownloadProps, ExtractZip, PrintTargets as _PrintTargets, ProblemNameConversion,
    Service, SessionProps, UserNameAndPassword, ZipEntries, ZipEntriesSorting,
};
use crate::terminal::{HasTerm, Term, WriteAnsi};
use crate::testsuite::{SimpleSuite, TestSuite};

use itertools::Itertools as _Itertools;
use log::warn;
use once_cell::sync::Lazy;
use once_cell::sync_lazy;
use regex::Regex;
use reqwest::StatusCode;
use select::document::Document;
use select::predicate::{Predicate, Text};
use serde::{Deserialize, Deserializer};
use serde_derive::Deserialize;
use serde_json::json;
use tokio::runtime::{Runtime, TaskExecutor};

use std::borrow::Cow;
use std::collections::HashMap;
use std::fmt;
use std::io::{self, Write as _Write};

pub(crate) fn login(sess_props: SessionProps<impl Term>) -> ServiceResult<()> {
    Hackerrank::try_new(sess_props)?.login(LoginOption::Explicit)
}

pub(crate) fn download(
    mut sess_props: SessionProps<impl Term>,
    download_props: DownloadProps<String>,
) -> ServiceResult<()> {
    let download_props = download_props.convert_contest_and_problems(ProblemNameConversion::Kebab);
    download_props.print_targets(sess_props.term.stdout())?;
    Hackerrank::try_new(sess_props)?.download(&download_props)
}

struct Hackerrank<T: Term> {
    term: T,
    session: HttpSession,
    runtime: Runtime,
    credentials: UserNameAndPassword,
}

impl<T: Term> HasTerm for Hackerrank<T> {
    type Term = T;

    fn term(&mut self) -> &mut T {
        &mut self.term
    }
}

impl<T: Term> Service for Hackerrank<T> {
    type Write = T::Stdout;

    fn requirements(&mut self) -> (&mut T::Stdout, &mut HttpSession, &mut Runtime) {
        (self.term.stdout(), &mut self.session, &mut self.runtime)
    }
}

impl<T: Term> DownloadProgress for Hackerrank<T> {
    type Write = T::Stdout;

    fn requirements(&mut self) -> (&mut T::Stdout, &HttpSession, TaskExecutor) {
        (self.term.stdout(), &self.session, self.runtime.executor())
    }
}

impl<T: Term> ExtractZip for Hackerrank<T> {
    type Write = T::Stdout;

    fn out(&mut self) -> &mut T::Stdout {
        self.term.stdout()
    }
}

impl<T: Term> Hackerrank<T> {
    fn try_new(mut sess_props: SessionProps<T>) -> ServiceResult<Self> {
        let credentials = sess_props.credentials.hackerrank.clone();
        let mut runtime = Runtime::new()?;
        let session = sess_props.start_session(&mut runtime)?;
        Ok(Hackerrank {
            term: sess_props.term,
            session,
            runtime,
            credentials,
        })
    }

    fn login(&mut self, option: LoginOption) -> ServiceResult<()> {
        let mut res = self.get("/auth/login").acceptable(&[200, 302]).send()?;
        if res.status() == StatusCode::FOUND && option == LoginOption::Explicit {
            writeln!(self.stderr(), "Already signed in.")?;
            self.stderr().flush()?;
        } else if res.status() == StatusCode::OK {
            let (mut username, mut password, on_test) = {
                match self.credentials.take() {
                    UserNameAndPassword::Some(username, password) => (username, password, true),
                    UserNameAndPassword::None => (
                        self.prompt_reply_stderr("Username: ")?,
{'fraction_non_alphanumeric': 0.14508960573476704, 'fraction_numerical': 0.0017204301075268817, 'mean_word_length': 3.787920384351407, 'pattern_counts': {'":': 0, '<': 41, '<?xml version=': 0, '>': 61, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 24, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1520913', 'n_tokens_mistral': 2221, 'n_tokens_neox': 2185, 'n_words': 478}