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
120
121
122
123
124
125
126
127
128
129
use std::io::Write;
use std::sync::Arc;

use crate::api_traits::TrendingProjectURL;
use crate::config::ConfigProperties;
use crate::display::{Column, DisplayBody};
use crate::remote::{self, CacheType, GetRemoteCliArgs};
use crate::Result;

use super::common;

pub struct TrendingCliArgs {
    pub language: String,
    pub get_args: GetRemoteCliArgs,
    // Used for macro compatibility when listing resources during display.
    pub flush: bool,
}

#[derive(Clone)]
pub struct TrendingProject {
    pub url: String,
    pub description: String,
}

impl TrendingProject {
    pub fn new(url: String, description: String) -> Self {
        Self { url, description }
    }
}

impl From<TrendingProject> for DisplayBody {
    fn from(trpr: TrendingProject) -> Self {
        DisplayBody::new(vec![
            Column::new("URL", trpr.url),
            Column::builder()
                .name("Description".to_string())
                .value(trpr.description)
                .optional(true)
                .build()
                .unwrap(),
        ])
    }
}

pub fn execute(
    cli_args: TrendingCliArgs,
    config: Arc<dyn ConfigProperties>,
    domain: &str,
) -> Result<()> {
    let remote = remote::get_trending(
        domain.to_string(),
        // does not matter in this command. Implementing it for
        // Github.com which is just a query against HTML page.
        "".to_string(),
        config,
        Some(&cli_args.get_args.cache_args),
        CacheType::File,
    )?;
    get_urls(remote, cli_args, &mut std::io::stdout())
}

fn get_urls<W: Write>(
    remote: Arc<dyn TrendingProjectURL>,
    cli_args: TrendingCliArgs,
    writer: &mut W,
) -> Result<()> {
    common::list_trending(remote, cli_args.language.to_string(), cli_args, writer)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[derive(Default)]
    struct MockTrendingProjectURL {
        projects: Vec<TrendingProject>,
    }

    impl MockTrendingProjectURL {
        fn new(projects: Vec<TrendingProject>) -> Self {
            Self { projects }
        }
    }

    impl TrendingProjectURL for MockTrendingProjectURL {
        fn list(&self, _language: String) -> Result<Vec<TrendingProject>> {
            Ok(self.projects.clone())
        }
    }

    #[test]
    fn test_no_urls() {
        let remote = Arc::new(MockTrendingProjectURL::default());
        let cli_args = TrendingCliArgs {
            language: "rust".to_string(),
            get_args: GetRemoteCliArgs::builder().build().unwrap(),
            flush: false,
        };
        let mut buf = Vec::new();
        get_urls(remote, cli_args, &mut buf).unwrap();
        assert_eq!("No resources found.\n", String::from_utf8(buf).unwrap(),)
    }

    #[test]
    fn test_trending_projects() {
        let projects = vec![
            TrendingProject::new(
                "https://github.com/kubernetes/kubernetes".to_string(),
                "".to_string(),
            ),
            TrendingProject::new(
                "https://github.com/jordilin/gitar".to_string(),
                "".to_string(),
            ),
        ];
        let remote = Arc::new(MockTrendingProjectURL::new(projects));
        let cli_args = TrendingCliArgs {
            language: "rust".to_string(),
            get_args: GetRemoteCliArgs::builder().build().unwrap(),
            flush: false,
        };
        let mut buf = Vec::new();
        get_urls(remote, cli_args, &mut buf).unwrap();
        assert_eq!(
            "URL\nhttps://github.com/kubernetes/kubernetes\nhttps://github.com/jordilin/gitar\n",
            String::from_utf8(buf).unwrap(),
        )
    }
}