use std::collections::HashMap;
use std::env::args;
use std::fs::{read_dir, File};
use std::io::{Result, Write};
use std::path::PathBuf;
use std::process::exit;
use dirs::data_dir;
use serde_json::json;
mod lib;
fn print_version() {
println!("{} {}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"));
println!(
"Copyright (C) {}, all rights reserved",
env!("CARGO_PKG_AUTHORS")
);
println!("This is free software. It is licensed for use, modification and");
println!("redistribution under the terms of the GNU Affero General Public License,");
println!("version 3. <https://www.gnu.org/licenses/agpl-3.0.en.html>");
println!("");
println!("{}", env!("CARGO_PKG_DESCRIPTION"));
}
struct Cli {
location: Option<lib::Location>,
command: String,
repo: Option<String>,
path: PathBuf,
fonts: Vec<String>,
use_local_repos: bool,
}
fn run() -> Result<()> {
let args: Vec<String> = args().collect();
let mut clean_args: Vec<String> = Vec::new();
let mut cli = Cli {
location: None,
command: "version".to_string(),
repo: None,
use_local_repos: true,
path: PathBuf::from("."),
fonts: Vec::new(),
};
let mut skip: bool = false;
if args.len() > 1 {
for i in 0..(args.len()) {
match args[i].as_str() {
"--repo" => {
cli.repo = Some(args[i + 1].clone());
skip = true;
}
"--user" => {
cli.location = Some(lib::Location::User);
}
"--path" => {
cli.path = PathBuf::from(&args[i + 1]);
skip = true;
}
"--system" => {
cli.location = Some(lib::Location::System);
}
"--use-preinstalled-repos" => {
cli.use_local_repos = false;
}
_ => {
if !skip {
clean_args.push(args[i].clone());
}
skip = false;
}
}
}
} else {
print_version();
return Ok(());
}
cli.command = clean_args[1].clone();
cli.fonts = clean_args[2..].to_vec();
let font_catcher_dir = data_dir().unwrap().join("font-catcher");
let repos_dir = font_catcher_dir.join("repos");
let repos_file = font_catcher_dir.join("repos.conf");
let local_repos_file: Vec<lib::Repository> = lib::generate_repos_from_file(&repos_file)?;
let mut local_repos: HashMap<String, Vec<lib::RepoFont>> = HashMap::new();
for file in read_dir(&repos_dir)? {
let file = file.unwrap();
match lib::generate_repo_font_list_from_file(&file.path()) {
Ok(fonts_list) => {
local_repos.insert(file.file_name().into_string().unwrap(), fonts_list);
}
Err(_) => {
eprintln!("Error while reading repo...");
}
}
}
let fonts_list = match cli.use_local_repos {
true => lib::generate_fonts_list(
local_repos.clone(),
lib::generate_local_fonts(None).unwrap(),
),
false => lib::init()?,
};
match cli.command.as_str() {
"version" => {
print_version();
}
"update-repos" => {
for r in local_repos_file.iter() {
println!("Updating {}...", r.name);
let mut file = File::create(repos_dir.join(r.name.clone() + ".json"))?;
file.write_all(
serde_json::to_string_pretty(&json!({
"kind": "webfonts#webfontList",
"items": &lib::generate_repo_font_list_from_url(
&r.url, r.key.clone()
)?
}))?
.as_bytes(),
)?;
}
}
"list-local-repos" => {
for r in local_repos.keys() {
println!("{}", r);
}
}
"install" => {
for font in cli.fonts.iter() {
match fonts_list.get(font) {
Some(data) => {
data.clone().install_to_user(cli.repo.as_deref(), true)?;
}
None => {
println!("{} not found anywhere!", font);
}
};
}
}
"download" => {
for font in cli.fonts.iter() {
match fonts_list.get(font) {
Some(data) => {
data.clone().download(cli.repo.as_deref(), &cli.path, true)?;
}
None => {
println!("{} not found anywhere!", font);
}
};
}
}
"search" => {
for font in cli.fonts.iter() {
for (name, data) in &fonts_list {
let mut data = data.clone();
if name.to_lowercase().contains(&font.to_lowercase())
&& (match cli.repo {
Some(ref repo) => data.is_font_in_repo(&repo),
None => true,
})
{
println!("\n{}:", &name);
println!(
" Available on: {}",
match data.get_repos_availability() {
Some(r) => r.join(" "),
None => "".to_string(),
}
);
println!(" User installed: {}", data.is_font_user_installed());
println!(" System installed: {}", data.is_font_system_installed());
}
}
}
}
"remove" => {
for font in cli.fonts.iter() {
match fonts_list.get(font) {
Some(data) => {
data.clone().uninstall_from_user(true)?;
}
None => {
println!("{} not found anywhere!", font);
}
};
}
}
"check-for-updates" => {
for (name, data) in fonts_list {
let mut data = data.clone();
if cli.location == Some(lib::Location::System) {
match data.get_all_repos_with_update_system() {
Some(repos) => {
println!("Updates for {} available on:", name);
for r in repos.iter() {
println!(" {}", r);
}
}
None => {}
}
} else {
match data.get_all_repos_with_update_user() {
Some(repos) => {
println!("Updates for {} available on:", name);
for r in repos.iter() {
println!(" {}", r);
}
}
None => {}
}
}
}
}
"update-all" => {
for (_name, data) in fonts_list {
let mut data = data.clone();
if cli.location == Some(lib::Location::System) {
match data.get_all_repos_with_update_system() {
Some(repos) => {
data.install_to_user(Some(&repos[0]), true)?;
}
None => {}
}
} else {
match data.get_all_repos_with_update_user() {
Some(repos) => {
data.install_to_user(Some(&repos[0]), true)?;
}
None => {}
}
}
}
}
"update" => {
for font in cli.fonts.iter() {
match fonts_list.get(font) {
Some(data) => {
let mut data = data.clone();
if cli.location == Some(lib::Location::System)
&& data.is_update_available_system()
{
let repo_with_update = &data.get_all_repos_with_update_system().unwrap()[0];
data.install_to_user(
Some(repo_with_update),
true,
)?;
} else if data.is_update_available_user() {
let repo_with_update = &data.get_all_repos_with_update_user().unwrap()[0];
data.install_to_user(
Some(repo_with_update),
true,
)?;
}
}
None => {
println!("{} not found anywhere!", font);
}
}
}
}
"list" => {
for (name, data) in fonts_list {
let mut data = data.clone();
if (cli.repo != None && data.is_font_in_repo(&cli.repo.as_ref().unwrap()))
|| cli.repo == None
{
if cli.location == Some(lib::Location::System)
&& data.is_font_system_installed()
{
println!("{}", name);
} else if cli.location == Some(lib::Location::User)
&& data.is_font_user_installed()
{
println!("{}", name);
} else if cli.location == None {
println!("{}", name);
}
}
}
}
_ => {
println!("{} is not a valid operation, skipping...", cli.command);
}
}
Ok(())
}
fn main() {
let result = run();
match result {
Ok(()) => {
exit(0);
}
Err(err) => {
eprintln!("error: {:#}", err);
exit(1);
}
}
}