use crossterm::{execute, terminal};
use indicatif::ProgressBar;
use reqwest::{blocking::Client, header::HeaderMap};
use serde::Deserialize;
use std::{
fs::{self, File},
io::{self, Write},
path::PathBuf,
};
pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
#[derive(Deserialize, Debug)]
pub struct GitHubFile {
pub path: String,
}
impl GitHubFile {
fn to_rawfile_link(&self) -> String {
let mut base = String::from("https://raw.githubusercontent.com/rust-lang/book/main/");
base.push_str(self.path.as_str());
base
}
}
#[derive(Deserialize)]
struct GithubResponse {
tree: Vec<GitHubFile>,
}
fn book_dir() -> PathBuf {
match home::home_dir() {
Some(path) => {
let path = path.join(".the-book-tui/book");
return path;
}
None => {
panic!("Impossible to get your home dir! Please set your $HOME environment variable.")
}
}
}
fn get_structure() -> Result<Vec<(String, PathBuf)>> {
let mut contents = Vec::new();
let mut entries = fs::read_dir(book_dir())?
.map(|res| res.map(|e| e.path()))
.collect::<std::result::Result<Vec<_>, io::Error>>()?;
entries.sort();
for path in entries {
if !path.is_dir() {
contents.push((
path.file_stem()
.unwrap()
.to_os_string()
.to_str()
.unwrap()
.to_string(),
path,
));
}
}
Ok(contents)
}
pub fn get_pages() -> Result<Vec<(String, PathBuf)>> {
if book_dir().is_dir() {
return get_structure();
}
println!("Book not found. Downloading...");
let mut headers = HeaderMap::new();
headers.insert("User-Agent", "the-book-tui".parse().unwrap());
let client = Client::new();
let git_tree = client.get("https://api.github.com/repos/rust-lang/book/git/trees/21a2ed14f4480dab62438dcc1130291bebc65379?recursive=1")
.headers(headers.clone())
.send()?
.json::<GithubResponse>()?
.tree;
let latest_version: Vec<&GitHubFile> = git_tree
.iter()
.filter(|f| f.path.starts_with("src/") && f.path.ends_with(".md"))
.collect();
let pb = ProgressBar::new(latest_version.iter().len() as u64);
for f in latest_version.iter() {
let title = f.path.split_once('/').unwrap().1;
let rawfile_link = f.to_rawfile_link();
let markdown = client
.get(rawfile_link)
.headers(headers.clone())
.send()?
.text()?;
fs::create_dir_all(book_dir())?;
let path = book_dir();
let path = path.join(title);
let mut file = File::create(path)?;
file.write_all(markdown.as_bytes())?;
pb.inc(1);
}
pb.finish();
let mut stdout = io::stdout();
execute!(stdout, terminal::Clear(terminal::ClearType::All)).unwrap();
return get_structure();
}