gr/cmds/
trending.rs

1use std::io::Write;
2use std::sync::Arc;
3
4use crate::api_traits::TrendingProjectURL;
5use crate::config::ConfigProperties;
6use crate::display::{Column, DisplayBody};
7use crate::remote::{self, CacheType, GetRemoteCliArgs};
8use crate::Result;
9
10use super::common;
11
12pub struct TrendingCliArgs {
13    pub language: String,
14    pub get_args: GetRemoteCliArgs,
15    // Used for macro compatibility when listing resources during display.
16    pub flush: bool,
17}
18
19#[derive(Clone)]
20pub struct TrendingProject {
21    pub url: String,
22    pub description: String,
23}
24
25impl TrendingProject {
26    pub fn new(url: String, description: String) -> Self {
27        Self { url, description }
28    }
29}
30
31impl From<TrendingProject> for DisplayBody {
32    fn from(trpr: TrendingProject) -> Self {
33        DisplayBody::new(vec![
34            Column::new("URL", trpr.url),
35            Column::builder()
36                .name("Description".to_string())
37                .value(trpr.description)
38                .optional(true)
39                .build()
40                .unwrap(),
41        ])
42    }
43}
44
45pub fn execute(
46    cli_args: TrendingCliArgs,
47    config: Arc<dyn ConfigProperties>,
48    domain: &str,
49) -> Result<()> {
50    let remote = remote::get_trending(
51        domain.to_string(),
52        // does not matter in this command. Implementing it for
53        // Github.com which is just a query against HTML page.
54        "".to_string(),
55        config,
56        Some(&cli_args.get_args.cache_args),
57        CacheType::File,
58    )?;
59    get_urls(remote, cli_args, &mut std::io::stdout())
60}
61
62fn get_urls<W: Write>(
63    remote: Arc<dyn TrendingProjectURL>,
64    cli_args: TrendingCliArgs,
65    writer: &mut W,
66) -> Result<()> {
67    common::list_trending(remote, cli_args.language.to_string(), cli_args, writer)
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73
74    #[derive(Default)]
75    struct MockTrendingProjectURL {
76        projects: Vec<TrendingProject>,
77    }
78
79    impl MockTrendingProjectURL {
80        fn new(projects: Vec<TrendingProject>) -> Self {
81            Self { projects }
82        }
83    }
84
85    impl TrendingProjectURL for MockTrendingProjectURL {
86        fn list(&self, _language: String) -> Result<Vec<TrendingProject>> {
87            Ok(self.projects.clone())
88        }
89    }
90
91    #[test]
92    fn test_no_urls() {
93        let remote = Arc::new(MockTrendingProjectURL::default());
94        let cli_args = TrendingCliArgs {
95            language: "rust".to_string(),
96            get_args: GetRemoteCliArgs::builder().build().unwrap(),
97            flush: false,
98        };
99        let mut buf = Vec::new();
100        get_urls(remote, cli_args, &mut buf).unwrap();
101        assert_eq!("No resources found.\n", String::from_utf8(buf).unwrap(),)
102    }
103
104    #[test]
105    fn test_trending_projects() {
106        let projects = vec![
107            TrendingProject::new(
108                "https://github.com/kubernetes/kubernetes".to_string(),
109                "".to_string(),
110            ),
111            TrendingProject::new(
112                "https://github.com/jordilin/gitar".to_string(),
113                "".to_string(),
114            ),
115        ];
116        let remote = Arc::new(MockTrendingProjectURL::new(projects));
117        let cli_args = TrendingCliArgs {
118            language: "rust".to_string(),
119            get_args: GetRemoteCliArgs::builder().build().unwrap(),
120            flush: false,
121        };
122        let mut buf = Vec::new();
123        get_urls(remote, cli_args, &mut buf).unwrap();
124        assert_eq!(
125            "URL\nhttps://github.com/kubernetes/kubernetes\nhttps://github.com/jordilin/gitar\n",
126            String::from_utf8(buf).unwrap(),
127        )
128    }
129}