1use std::str;
2
3use crate::error::Error;
4use crate::error::Result;
5
6const API_URL: &str = "https://www.toptal.com/developers/gitignore/api";
7
8pub fn list_templates() -> Result<Vec<String>> {
9 get(&list_endpoint())
10 .and_then(|res| res.text())
11 .map_err(|_| Error::fetch_templates_failed())
12 .map(|content| {
13 content
14 .lines()
15 .filter(|l| !l.is_empty())
16 .flat_map(|l| l.split(',').map(|t| t.to_owned()))
17 .collect::<Vec<String>>()
18 })
19}
20
21pub fn create<'a, I>(templates: I) -> Result<String>
22where
23 I: IntoIterator<Item = &'a str>,
24{
25 let templates = templates
26 .into_iter()
27 .map(|t| t.to_owned())
28 .collect::<Vec<String>>();
29 let res = get(&create_endpoint(&templates))?;
30 if res.status().is_client_error() || res.status().is_server_error() {
31 return match res.status() {
32 reqwest::StatusCode::NOT_FOUND => handle_404(&templates),
33 _ => Err(Error::generic()),
34 };
35 }
36 Ok(res.text()?)
37}
38
39fn list_endpoint() -> String {
40 format!("{}/list", API_URL)
41}
42
43fn create_endpoint(templates: &[String]) -> String {
44 let templates_str = templates.join(",");
45 format!("{}/{}", API_URL, templates_str)
46}
47
48fn get(url: &str) -> reqwest::Result<reqwest::blocking::Response> {
49 let client = reqwest::blocking::ClientBuilder::new()
50 .user_agent("Mozilla/5.0")
51 .build()?;
52 client.get(url).send()
53}
54
55fn handle_404(templates: &[String]) -> Result<String> {
56 let Ok(known_templates) = list_templates() else { return Err(Error::generic());};
58 let unknown_templates = find_unknown_templates(&known_templates, templates);
59 let err = if !unknown_templates.is_empty() {
60 Error::TemplateNotFound(unknown_templates)
61 } else {
62 Error::generic()
63 };
64 Err(err)
65}
66
67fn find_unknown_templates(known_templates: &[String], templates: &[String]) -> Vec<String> {
68 templates
72 .iter()
73 .filter(|t| !known_templates.contains(t))
74 .map(|t| (*t).to_owned())
75 .collect::<Vec<String>>()
76}