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
use auth_token::AuthToken;
use choice_presenter::{ChoicePresenter, ChoiceResult};
use error_box::ErrorBox;
use file_finder::FileFinder;
use file_maker::FileMaker;
use github_request_maker::GitHubRequestMaker;
use request_maker::RequestMaker;
use std::io;
mod auth_token;
mod choice_presenter;
mod error_box;
mod file_finder;
mod file_maker;
mod github_request_maker;
mod github_url_builder;
mod request_maker;
mod requester;
mod test_request_maker;
type RealRequestMaker = GitHubRequestMaker<RequestMaker>;
pub struct Controller {
request_maker: RealRequestMaker,
}
impl Controller {
pub fn new(token: Option<AuthToken>) -> Result<Controller, ErrorBox> {
let request_maker = Self::create_request_maker(token)?;
Ok(Controller { request_maker })
}
fn create_request_maker(token: Option<AuthToken>) -> Result<RealRequestMaker, ErrorBox> {
let requester = RequestMaker::new(token);
let request_maker = GitHubRequestMaker::new(requester);
if let Some(limit) = request_maker.too_close_to_limit()? {
println!(
"Warning: The GitHub API will only allow you to make {} more requests for the hour. Consider providing an authentication token.",
limit
)
}
Ok(request_maker)
}
pub fn show_is_authenticated(&self) -> Result<(), ErrorBox> {
let is_authenticated = self.request_maker.is_authenticated()?;
match is_authenticated {
true => println!("According to the GitHub API, you are authenticated!"),
false => println!("According to the GitHub API, you are not authenticated. Consider providing an authentication token.")
};
Ok(())
}
pub fn list_files(&self) -> Result<(), ErrorBox> {
for file_name in self.request_maker.get_file_names()? {
println!("{}", file_name);
}
Ok(())
}
pub fn find_files(&self, query: &str) -> Result<(), ErrorBox> {
let file_names = self.request_maker.get_file_names()?;
let results = FileFinder::find(&file_names, query);
if results.len() == 1 {
self.download_exact_match(&results[0])?;
} else if !results.is_empty() {
self.handle_multiple_matches(query, results)?;
} else {
println!("No matches found for '{}'", query);
}
Ok(())
}
fn download_exact_match(&self, file_name: &str) -> Result<(), ErrorBox> {
println!("Found match '{}'\nDownloading...", file_name);
let content = self.request_maker.get_file(file_name)?;
FileMaker::make_file(".gitignore", &content)?;
println!("Downloaded .gitignore for '{}'", file_name.replace(".gitignore", ""));
Ok(())
}
fn handle_multiple_matches(&self, query: &str, results: Vec<String>) -> Result<(), ErrorBox> {
println!("Several matches found for '{}':\n", query);
let choice_presenter = ChoicePresenter::new(results);
println!("{}\n", choice_presenter.present_choices());
loop {
match self.get_choice(&choice_presenter) {
Ok(choice) => match choice {
Some(choice) => return self.download_exact_match(&choice),
None => return Ok(()),
},
Err(_) => {
println!("Invalid input, please try again.");
continue;
}
}
}
}
fn get_choice<'c>(&self, choice_presenter: &'c ChoicePresenter) -> ChoiceResult<'c> {
println!(
"Which do you want to use (0 to cancel)? [0-{}]:",
choice_presenter.len()
);
let input = self.get_choice_input();
choice_presenter.select_choice(&input)
}
fn get_choice_input(&self) -> String {
let mut choice = String::new();
io::stdin().read_line(&mut choice).unwrap();
choice
}
}