1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
use crate::app_ui::event::Event;
use crate::deserializers::problemset_question_list::Question;
use crate::errors::AppResult;
use crate::graphql::problemset_question_list::Query as QuestionDbQuery;
use crate::graphql::GQLLeetcodeQuery;
use crate::{config::Config, db_ops::ModelUtils};
use sea_orm::DatabaseConnection;

pub async fn update_database_questions(
    client: &reqwest::Client,
    database_client: &DatabaseConnection,
) -> AppResult<()> {
    let query = QuestionDbQuery::default();
    let query_response = query.send(client).await?;
    let total_questions = query_response.get_total_questions();
    let chunk_size: usize = 100;
    let total_range = (0..total_questions).collect::<Vec<_>>();
    for chunk in kdam::tqdm!(total_range.chunks(chunk_size)) {
        if let Some(skip) = chunk.first() {
            let client_copy = client.clone();
            let db_client_copy = database_client.clone();
            let resp = QuestionDbQuery::new(chunk.len() as i32, *skip)
                .send(&client_copy)
                .await?;
            let questions = resp.get_questions();
            Question::multi_insert(&db_client_copy, questions).await?;
        }
    }
    Ok(())
}

use crate::migrations::{Migrator, MigratorTrait};

pub async fn do_migrations(database_client: &DatabaseConnection) -> AppResult<()> {
    Ok(Migrator::up(database_client, None).await?)
}

use reqwest::header::{HeaderMap, HeaderValue};

pub async fn get_reqwest_client(config: &Config) -> AppResult<reqwest::Client> {
    let csrf = config.leetcode.csrftoken.as_str();
    let sess = config.leetcode.leetcode_session.as_str();
    let mut headers = HeaderMap::new();
    let header_k_v = [
        (
            "Cookie",
            format!("LEETCODE_SESSION={sess}; csrftoken={csrf}"),
        ),
        ("Content-Type", "application/json".to_string()),
        ("x-csrftoken", csrf.to_string()),
        ("Origin", "https://leetcode.com".to_string()),
        ("Referer", "https://leetcode.com".to_string()),
        ("Connection", "keep-alive".to_string()),
    ];

    for (key, value) in header_k_v {
        headers.append(key, HeaderValue::from_str(value.as_str())?);
    }

    let client = reqwest::ClientBuilder::new()
        .default_headers(headers)
        .build()?;
    Ok(client)
}

use crate::config::Db;

pub async fn get_config() -> AppResult<Option<Config>> {
    let config_path = Config::get_config_base_file()?;
    let config: Config;

    if !config_path.exists() {
        config = Config::default();
        config.write_config(config_path.clone()).await?;
        println!("\nConfig is created at config_path {}.\n\nKindly set LEETCODE_SESSION and csrftoken in the config file. These can be obained from leetcode cookies in the browser.", config_path.display());
        let db_data_path = Db::get_base_sqlite_data_path()?;
        if !db_data_path.exists() {
            Db::touch_default_db().await?;
            println!("\nDatabase resides in {}", db_data_path.display());
        }
        if !Config::get_default_solutions_dir()?.exists() {
            Config::create_solutions_dir().await?;
        }
        Ok(None)
    } else {
        println!("Config file found @ {}", &config_path.display());
        config = Config::read_config(config_path).await?;
        Ok(Some(config))
    }
}

use crate::app_ui::async_task_channel::ChannelRequestReceiver;

pub async fn async_tasks_executor(
    mut rx_request: ChannelRequestReceiver,
    tx_response: std::sync::mpsc::Sender<Event>,
    client: &reqwest::Client,
    conn: &DatabaseConnection,
) -> AppResult<()> {
    while let Some(task) = rx_request.recv().await {
        let response = task.execute(client, conn).await;
        tx_response.send(Event::TaskResponse(Box::new(response)))?;
    }
    Ok(())
}

#[cfg(test)]
mod tests {

    #[test]
    fn test_chunking() {
        let range = (0..11).collect::<Vec<_>>();
        for (chunk, first) in range.chunks(2).zip([0, 2, 4, 6, 8]) {
            assert_eq!(chunk.first().unwrap(), &first);
            assert_eq!(chunk.len(), 2);
        }
        assert_eq!(range.chunks(2).nth(5), Some(vec![10].as_slice()));
    }
}