glit_core/
user.rs

1use ahash::RandomState;
2use async_trait::async_trait;
3use dashmap::DashMap;
4use reqwest::{Client, Url};
5use scraper::{Html, Selector};
6use serde::Serialize;
7
8use crate::{config::UserConfig, repo::Repository, types::RepoName, ExtractLog, Factory};
9
10#[derive(Serialize)]
11pub struct User {
12    pub name: String,
13    #[serde(skip)]
14    pub url: Url,
15    pub repo_count: usize,
16    #[serde(skip)]
17    pub pages_urls: Vec<Url>,
18    #[serde(skip)]
19    pub all_branches: bool,
20    pub repositories_data: DashMap<RepoName, Repository, RandomState>,
21}
22
23pub struct UserFactory {
24    url: Url,
25    name: String,
26    page_url: Url,
27    all_branches: bool,
28}
29
30impl UserFactory {
31    pub fn with_config(user_config: UserConfig) -> Self {
32        // CLI param
33        let url = user_config.url;
34        let all_branches: bool = user_config.all_branches;
35
36        // Craft other param
37        let mut path_segment = url.path_segments().unwrap();
38        let name = path_segment.next().unwrap().to_string();
39
40        let page_url = format!("{}?tab=repositories&type=source", url);
41        let page_url = Url::parse(&page_url).unwrap();
42
43        UserFactory {
44            url,
45            name,
46            page_url,
47            all_branches,
48        }
49    }
50
51    pub async fn build_with_client(self, client: &Client) -> User {
52        let repo_count = Self::_repositories_count(client, self.page_url.clone()).await;
53        let pages_count = Self::_pages_count(repo_count);
54        let pages_urls = Self::_build_repo_links(self.page_url, repo_count, pages_count);
55
56        User {
57            name: self.name,
58            url: self.url,
59            repo_count,
60            pages_urls,
61            all_branches: self.all_branches,
62            repositories_data: DashMap::<_, _, RandomState>::with_capacity_and_hasher(
63                repo_count,
64                RandomState::new(),
65            ),
66        }
67    }
68}
69
70#[async_trait]
71impl Factory for UserFactory {
72    async fn _repositories_count(client: &Client, url: Url) -> usize {
73        let resp = client.get(url).send().await.unwrap();
74        let text = resp.text().await.unwrap();
75
76        let parser = Html::parse_document(&text);
77        let selector_repositories_count =
78            Selector::parse(r#"div > div.user-repo-search-results-summary > strong:nth-child(1)"#)
79                .unwrap();
80
81        let repository_count_str = parser
82            .select(&selector_repositories_count)
83            .next()
84            .unwrap()
85            .inner_html();
86
87        repository_count_str
88            .trim()
89            .replace(',', "")
90            .parse::<usize>()
91            .unwrap()
92    }
93}
94
95#[async_trait]
96impl ExtractLog for User {
97    async fn extract_log(mut self, client: &Client) -> Self {
98        let user_selector =
99            Selector::parse(r#"turbo-frame > div > div > ul > li > div > div > h3 > a"#).unwrap();
100
101        self.repositories_data = Self::common_log_feature(&self, client, user_selector).await;
102        self
103    }
104
105    fn get_repo_count(&self) -> usize {
106        self.repo_count
107    }
108
109    fn get_all_branches(&self) -> bool {
110        self.all_branches
111    }
112
113    fn get_url(&self) -> Url {
114        self.url.clone()
115    }
116
117    fn get_pages_url(&self) -> Vec<Url> {
118        self.pages_urls.clone()
119    }
120}