2021-06-06 11:20:08 +01:00
|
|
|
use std::fs;
|
|
|
|
|
|
|
|
use chrono::{DateTime, Local};
|
2021-04-24 13:13:44 +01:00
|
|
|
use colored::*;
|
2021-04-24 11:58:03 +01:00
|
|
|
use comfy_table::presets::UTF8_HORIZONTAL_BORDERS_ONLY;
|
2021-04-24 13:13:44 +01:00
|
|
|
use comfy_table::{Cell, CellAlignment, ContentArrangement, Table};
|
2021-06-06 11:20:08 +01:00
|
|
|
use flexi_logger::LevelFilter;
|
2021-04-24 11:58:03 +01:00
|
|
|
use log::error;
|
|
|
|
|
2021-06-01 10:23:22 +01:00
|
|
|
use crate::errors::PaperoniError;
|
2021-04-24 11:58:03 +01:00
|
|
|
|
|
|
|
pub fn display_summary(
|
|
|
|
initial_article_count: usize,
|
|
|
|
succesful_articles_table: Table,
|
2021-04-29 17:58:37 +01:00
|
|
|
partial_downloads_count: usize,
|
2021-04-24 11:58:03 +01:00
|
|
|
errors: Vec<PaperoniError>,
|
|
|
|
) {
|
2021-04-29 17:58:37 +01:00
|
|
|
let successfully_downloaded_count =
|
|
|
|
initial_article_count - partial_downloads_count - errors.len();
|
2021-04-24 11:58:03 +01:00
|
|
|
|
|
|
|
println!(
|
|
|
|
"{}",
|
2021-04-29 17:58:37 +01:00
|
|
|
short_summary(DownloadCount::new(
|
2021-04-24 11:58:03 +01:00
|
|
|
initial_article_count,
|
|
|
|
successfully_downloaded_count,
|
2021-04-29 17:58:37 +01:00
|
|
|
partial_downloads_count,
|
2021-04-24 11:58:03 +01:00
|
|
|
errors.len()
|
2021-04-29 17:58:37 +01:00
|
|
|
))
|
2021-04-24 13:13:44 +01:00
|
|
|
.bold()
|
2021-04-24 11:58:03 +01:00
|
|
|
);
|
|
|
|
|
|
|
|
if successfully_downloaded_count > 0 {
|
|
|
|
println!("{}", succesful_articles_table);
|
|
|
|
}
|
|
|
|
if !errors.is_empty() {
|
2021-04-24 13:13:44 +01:00
|
|
|
println!("\n{}", "Failed article downloads".bright_red().bold());
|
2021-04-24 11:58:03 +01:00
|
|
|
let mut table_failed = Table::new();
|
|
|
|
table_failed
|
|
|
|
.load_preset(UTF8_HORIZONTAL_BORDERS_ONLY)
|
|
|
|
.set_header(vec![
|
|
|
|
Cell::new("Link").set_alignment(CellAlignment::Center),
|
|
|
|
Cell::new("Reason").set_alignment(CellAlignment::Center),
|
|
|
|
])
|
|
|
|
.set_content_arrangement(ContentArrangement::Dynamic);
|
|
|
|
|
|
|
|
for error in errors {
|
|
|
|
let error_source = error
|
|
|
|
.article_source()
|
|
|
|
.clone()
|
|
|
|
.unwrap_or_else(|| "<unknown link>".to_string());
|
|
|
|
table_failed.add_row(vec![&error_source, &format!("{}", error.kind())]);
|
|
|
|
error!("{}\n - {}", error, error_source);
|
|
|
|
}
|
|
|
|
println!("{}", table_failed);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns a string summary of the total number of failed and successful article downloads
|
2021-04-29 17:58:37 +01:00
|
|
|
fn short_summary(download_count: DownloadCount) -> String {
|
|
|
|
// TODO: Refactor this
|
|
|
|
if download_count.total
|
|
|
|
!= download_count.successful + download_count.failed + download_count.partial
|
|
|
|
{
|
2021-04-24 11:58:03 +01:00
|
|
|
panic!("initial_count must be equal to the sum of failed and successful count")
|
|
|
|
}
|
|
|
|
let get_noun = |count: usize| if count == 1 { "article" } else { "articles" };
|
2021-04-29 17:58:37 +01:00
|
|
|
if download_count.successful == download_count.total && download_count.successful == 1 {
|
2021-04-24 13:13:44 +01:00
|
|
|
"Article downloaded successfully".green().to_string()
|
2021-04-29 17:58:37 +01:00
|
|
|
} else if download_count.total == download_count.failed && download_count.failed == 1 {
|
2021-04-24 13:13:44 +01:00
|
|
|
"Article failed to download".red().to_string()
|
2021-04-29 17:58:37 +01:00
|
|
|
} else if download_count.total == download_count.partial && download_count.partial == 1 {
|
|
|
|
"Article partially failed to download".yellow().to_string()
|
|
|
|
} else if download_count.successful == download_count.total {
|
2021-04-24 13:13:44 +01:00
|
|
|
"All articles downloaded successfully".green().to_string()
|
2021-04-29 17:58:37 +01:00
|
|
|
} else if download_count.failed == download_count.total {
|
2021-04-24 13:13:44 +01:00
|
|
|
"All articles failed to download".red().to_string()
|
2021-04-29 17:58:37 +01:00
|
|
|
} else if download_count.partial == download_count.total {
|
|
|
|
"All articles partially failed to download"
|
|
|
|
.yellow()
|
|
|
|
.to_string()
|
|
|
|
} else if download_count.partial == 0 {
|
2021-04-24 11:58:03 +01:00
|
|
|
format!(
|
|
|
|
"{} {} downloaded successfully, {} {} failed",
|
2021-04-29 17:58:37 +01:00
|
|
|
download_count.successful,
|
|
|
|
get_noun(download_count.successful),
|
|
|
|
download_count.failed,
|
|
|
|
get_noun(download_count.failed)
|
|
|
|
)
|
|
|
|
.yellow()
|
|
|
|
.to_string()
|
|
|
|
} else if download_count.successful == 0
|
|
|
|
&& download_count.partial > 0
|
|
|
|
&& download_count.failed > 0
|
|
|
|
{
|
|
|
|
format!(
|
|
|
|
"{} {} partially failed to download, {} {} failed",
|
|
|
|
download_count.partial,
|
|
|
|
get_noun(download_count.partial),
|
|
|
|
download_count.failed,
|
|
|
|
get_noun(download_count.failed)
|
2021-04-24 11:58:03 +01:00
|
|
|
)
|
2021-04-24 13:13:44 +01:00
|
|
|
.yellow()
|
|
|
|
.to_string()
|
2021-04-29 17:58:37 +01:00
|
|
|
} else if download_count.failed == 0
|
|
|
|
&& download_count.successful > 0
|
|
|
|
&& download_count.partial > 0
|
|
|
|
{
|
|
|
|
format!(
|
|
|
|
"{} {} downloaded successfully, {} {} partially failed to download",
|
|
|
|
download_count.successful,
|
|
|
|
get_noun(download_count.successful),
|
|
|
|
download_count.partial,
|
|
|
|
get_noun(download_count.partial)
|
|
|
|
)
|
|
|
|
.yellow()
|
|
|
|
.to_string()
|
|
|
|
} else {
|
|
|
|
format!(
|
|
|
|
"{} {} downloaded successfully, {} {} partially failed to download, {} {} failed",
|
|
|
|
download_count.successful,
|
|
|
|
get_noun(download_count.successful),
|
|
|
|
download_count.partial,
|
|
|
|
get_noun(download_count.partial),
|
|
|
|
download_count.failed,
|
|
|
|
get_noun(download_count.failed)
|
|
|
|
)
|
|
|
|
.yellow()
|
|
|
|
.to_string()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct DownloadCount {
|
|
|
|
total: usize,
|
|
|
|
successful: usize,
|
|
|
|
partial: usize,
|
|
|
|
failed: usize,
|
|
|
|
}
|
|
|
|
impl DownloadCount {
|
|
|
|
fn new(total: usize, successful: usize, partial: usize, failed: usize) -> Self {
|
|
|
|
Self {
|
|
|
|
total,
|
|
|
|
successful,
|
|
|
|
partial,
|
|
|
|
failed,
|
|
|
|
}
|
2021-04-24 11:58:03 +01:00
|
|
|
}
|
|
|
|
}
|
2021-06-06 11:20:08 +01:00
|
|
|
|
|
|
|
use crate::errors::LogError as Error;
|
|
|
|
|
|
|
|
pub fn init_logger(
|
|
|
|
log_level: LevelFilter,
|
|
|
|
start_time: &DateTime<Local>,
|
|
|
|
is_logging_to_file: bool,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
use directories::UserDirs;
|
|
|
|
use flexi_logger::LogSpecBuilder;
|
|
|
|
|
|
|
|
match UserDirs::new() {
|
|
|
|
Some(user_dirs) => {
|
|
|
|
let home_dir = user_dirs.home_dir();
|
|
|
|
let paperoni_dir = home_dir.join(".paperoni");
|
|
|
|
let log_dir = paperoni_dir.join("logs");
|
|
|
|
|
|
|
|
let log_spec = LogSpecBuilder::new().module("paperoni", log_level).build();
|
|
|
|
let formatted_timestamp = start_time.format("%Y-%m-%d_%H-%M-%S");
|
|
|
|
let mut logger = flexi_logger::Logger::with(log_spec);
|
|
|
|
|
|
|
|
if is_logging_to_file && (!paperoni_dir.is_dir() || !log_dir.is_dir()) {
|
|
|
|
if let Err(e) = fs::create_dir_all(&log_dir) {
|
|
|
|
return Err(Error::LogDirectoryError(format!("Unable to create paperoni directories on home directory for logging purposes\n{}",e)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if is_logging_to_file {
|
|
|
|
logger = logger
|
|
|
|
.directory(log_dir)
|
|
|
|
.discriminant(formatted_timestamp.to_string())
|
|
|
|
.suppress_timestamp()
|
|
|
|
.log_to_file();
|
|
|
|
}
|
|
|
|
logger.start()?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
None => Err(Error::LogDirectoryError(
|
|
|
|
"Unable to get user directories for logging purposes".to_string(),
|
|
|
|
)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-24 11:58:03 +01:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2021-04-29 17:58:37 +01:00
|
|
|
use super::{short_summary, DownloadCount};
|
2021-04-24 13:13:44 +01:00
|
|
|
use colored::*;
|
2021-04-24 11:58:03 +01:00
|
|
|
#[test]
|
|
|
|
fn test_short_summary() {
|
2021-04-24 13:13:44 +01:00
|
|
|
assert_eq!(
|
2021-04-29 17:58:37 +01:00
|
|
|
short_summary(DownloadCount::new(1, 1, 0, 0)),
|
2021-04-24 13:13:44 +01:00
|
|
|
"Article downloaded successfully".green().to_string()
|
|
|
|
);
|
|
|
|
assert_eq!(
|
2021-04-29 17:58:37 +01:00
|
|
|
short_summary(DownloadCount::new(1, 0, 0, 1)),
|
2021-04-24 13:13:44 +01:00
|
|
|
"Article failed to download".red().to_string()
|
|
|
|
);
|
2021-04-24 11:58:03 +01:00
|
|
|
assert_eq!(
|
2021-04-29 17:58:37 +01:00
|
|
|
short_summary(DownloadCount::new(10, 10, 0, 0)),
|
2021-04-24 13:13:44 +01:00
|
|
|
"All articles downloaded successfully".green().to_string()
|
2021-04-24 11:58:03 +01:00
|
|
|
);
|
|
|
|
assert_eq!(
|
2021-04-29 17:58:37 +01:00
|
|
|
short_summary(DownloadCount::new(10, 0, 0, 10)),
|
2021-04-24 13:13:44 +01:00
|
|
|
"All articles failed to download".red().to_string()
|
2021-04-24 11:58:03 +01:00
|
|
|
);
|
|
|
|
assert_eq!(
|
2021-04-29 17:58:37 +01:00
|
|
|
short_summary(DownloadCount::new(10, 8, 0, 2)),
|
2021-04-24 13:13:44 +01:00
|
|
|
"8 articles downloaded successfully, 2 articles failed"
|
|
|
|
.yellow()
|
|
|
|
.to_string()
|
2021-04-24 11:58:03 +01:00
|
|
|
);
|
|
|
|
assert_eq!(
|
2021-04-29 17:58:37 +01:00
|
|
|
short_summary(DownloadCount::new(10, 1, 0, 9)),
|
2021-04-24 13:13:44 +01:00
|
|
|
"1 article downloaded successfully, 9 articles failed"
|
|
|
|
.yellow()
|
|
|
|
.to_string()
|
2021-04-24 11:58:03 +01:00
|
|
|
);
|
|
|
|
assert_eq!(
|
2021-04-29 17:58:37 +01:00
|
|
|
short_summary(DownloadCount::new(7, 6, 0, 1)),
|
2021-04-24 13:13:44 +01:00
|
|
|
"6 articles downloaded successfully, 1 article failed"
|
|
|
|
.yellow()
|
|
|
|
.to_string()
|
2021-04-24 11:58:03 +01:00
|
|
|
);
|
2021-04-29 17:58:37 +01:00
|
|
|
assert_eq!(
|
|
|
|
short_summary(DownloadCount::new(7, 4, 2, 1)),
|
|
|
|
"4 articles downloaded successfully, 2 articles partially failed to download, 1 article failed"
|
|
|
|
.yellow()
|
|
|
|
.to_string()
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
short_summary(DownloadCount::new(12, 6, 6, 0)),
|
|
|
|
"6 articles downloaded successfully, 6 articles partially failed to download"
|
|
|
|
.yellow()
|
|
|
|
.to_string()
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
short_summary(DownloadCount::new(5, 0, 4, 1)),
|
|
|
|
"4 articles partially failed to download, 1 article failed"
|
|
|
|
.yellow()
|
|
|
|
.to_string()
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
short_summary(DownloadCount::new(4, 0, 4, 0)),
|
|
|
|
"All articles partially failed to download"
|
|
|
|
.yellow()
|
|
|
|
.to_string()
|
|
|
|
);
|
2021-04-24 11:58:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[should_panic(
|
|
|
|
expected = "initial_count must be equal to the sum of failed and successful count"
|
|
|
|
)]
|
|
|
|
fn test_short_summary_panics_on_invalid_input() {
|
2021-04-29 17:58:37 +01:00
|
|
|
short_summary(DownloadCount::new(0, 12, 0, 43));
|
2021-04-24 11:58:03 +01:00
|
|
|
}
|
|
|
|
}
|