Skip to content

Commit

Permalink
Port cache and output modules to anyhow results
Browse files Browse the repository at this point in the history
  • Loading branch information
dbrgn committed Jan 9, 2022
1 parent 405a2c9 commit fd30775
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 116 deletions.
118 changes: 56 additions & 62 deletions src/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,17 @@ use std::{
fs::{self, File},
io::{BufReader, Cursor, Read, Seek},
path::{Path, PathBuf},
time::{Duration, SystemTime},
};

use anyhow::{ensure, Context, Result};
use app_dirs::{get_app_root, AppDataType};
use log::debug;
use reqwest::{blocking::Client, Proxy};
use std::time::{Duration, SystemTime};
use walkdir::{DirEntry, WalkDir};
use zip::ZipArchive;

use crate::{
error::TealdeerError::{self, CacheError, UpdateError},
types::{PathSource, PlatformType},
};
use crate::types::{PathSource, PlatformType};

static CACHE_DIR_ENV_VAR: &str = "TEALDEER_CACHE_DIR";

Expand Down Expand Up @@ -53,16 +51,16 @@ impl PageLookupResult {
///
/// This will return an error if either the page file or the patch file
/// cannot be opened.
pub fn reader(&self) -> Result<BufReader<Box<dyn Read>>, String> {
pub fn reader(&self) -> Result<BufReader<Box<dyn Read>>> {
// Open page file
let page_file = File::open(&self.page_path)
.map_err(|msg| format!("Could not open page file at {:?}: {}", self.page_path, msg))?;
.with_context(|| format!("Could not open page file at {:?}", self.page_path))?;

// Open patch file
let patch_file_opt = match &self.patch_path {
Some(path) => Some(
File::open(path)
.map_err(|msg| format!("Could not open patch file at {:?}: {}", path, msg))?,
.with_context(|| format!("Could not open patch file at {:?}", path))?,
),
None => None,
};
Expand Down Expand Up @@ -102,26 +100,25 @@ impl Cache {
}

/// Return the path to the cache directory.
pub fn get_cache_dir() -> Result<(PathBuf, PathSource), TealdeerError> {
pub fn get_cache_dir() -> Result<(PathBuf, PathSource)> {
// Allow overriding the cache directory by setting the env variable.
if let Ok(value) = env::var(CACHE_DIR_ENV_VAR) {
let path = PathBuf::from(value);
let (path_exists, path_is_dir) = path
.metadata()
.map_or((false, false), |md| (true, md.is_dir()));
if path_exists && !path_is_dir {
return Err(CacheError(format!(
"Path specified by ${} is not a directory.",
CACHE_DIR_ENV_VAR
)));
}
ensure!(
!path_exists || path_is_dir,
"Path specified by ${} is not a directory",
CACHE_DIR_ENV_VAR
);
if !path_exists {
// Try to create the complete directory path.
fs::create_dir_all(&path).map_err(|_| {
CacheError(format!(
"Directory path specified by ${} cannot be created.",
fs::create_dir_all(&path).with_context(|| {
format!(
"Directory path specified by ${} cannot be created",
CACHE_DIR_ENV_VAR
))
)
})?;
eprintln!(
"Successfully created cache directory path `{}`.",
Expand All @@ -132,16 +129,13 @@ impl Cache {
};

// Otherwise, fall back to user cache directory.
match get_app_root(AppDataType::UserCache, &crate::APP_INFO) {
Ok(dirs) => Ok((dirs, PathSource::OsConvention)),
Err(_) => Err(CacheError(
"Could not determine user cache directory.".into(),
)),
}
let dirs = get_app_root(AppDataType::UserCache, &crate::APP_INFO)
.context("Could not determine user cache directory")?;
Ok((dirs, PathSource::OsConvention))
}

/// Download the archive
fn download(&self) -> Result<Vec<u8>, TealdeerError> {
fn download(&self) -> Result<Vec<u8>> {
let mut builder = Client::builder();
if let Ok(ref host) = env::var("HTTP_PROXY") {
if let Ok(proxy) = Proxy::http(host) {
Expand All @@ -153,9 +147,9 @@ impl Cache {
builder = builder.proxy(proxy);
}
}
let client = builder.build().map_err(|e| {
TealdeerError::UpdateError(format!("Could not instantiate HTTP client: {}", e))
})?;
let client = builder
.build()
.context("Could not instantiate HTTP client")?;
let mut resp = client.get(&self.url).send()?;
let mut buf: Vec<u8> = vec![];
let bytes_downloaded = resp.copy_to(&mut buf)?;
Expand All @@ -169,7 +163,7 @@ impl Cache {
}

/// Update the pages cache.
pub fn update(&self) -> Result<(), TealdeerError> {
pub fn update(&self) -> Result<()> {
// First, download the compressed data
let bytes: Vec<u8> = self.download()?;

Expand All @@ -182,21 +176,20 @@ impl Cache {

// Make sure that cache directory exists
debug!("Ensure cache directory {:?} exists", &cache_dir);
fs::create_dir_all(&cache_dir)
.map_err(|e| UpdateError(format!("Could not create cache directory: {}", e)))?;
fs::create_dir_all(&cache_dir).context("Could not create cache directory")?;

// Clear cache directory
// Note: This is not the best solution. Ideally we would download the
// archive to a temporary directory and then swap the two directories.
// But renaming a directory doesn't work across filesystems and Rust
// does not yet offer a recursive directory copying function. So for
// now, we'll use this approach.
Self::clear()?;
Self::clear().context("Could not clear the cache directory")?;

// Extract archive
archive
.extract(&pages_dir)
.map_err(|e| UpdateError(format!("Could not unpack compressed data: {}", e)))?;
.context("Could not unpack compressed data")?;

Ok(())
}
Expand Down Expand Up @@ -308,7 +301,7 @@ impl Cache {
}

/// Return the available pages.
pub fn list_pages(&self) -> Result<Vec<String>, TealdeerError> {
pub fn list_pages(&self) -> Result<Vec<String>> {
// Determine platforms directory and platform
let (cache_dir, _) = Self::get_cache_dir()?;
let platforms_dir = cache_dir.join(TLDR_PAGES_DIR).join("pages");
Expand Down Expand Up @@ -353,35 +346,36 @@ impl Cache {
}

/// Delete the cache directory.
pub fn clear() -> Result<(), TealdeerError> {
pub fn clear() -> Result<()> {
let (path, _) = Self::get_cache_dir()?;
if path.exists() && path.is_dir() {
// Delete old tldr-pages cache location as well if present
// TODO: To be removed in the future
for pages_dir_name in [TLDR_PAGES_DIR, TLDR_OLD_PAGES_DIR] {
let pages_dir = path.join(pages_dir_name);

if pages_dir.exists() {
fs::remove_dir_all(&pages_dir).map_err(|e| {
CacheError(format!(
"Could not remove cache directory ({}): {}",
pages_dir.display(),
e
))
})?;
}

// Check preconditions
ensure!(
path.exists(),
"Cache path ({}) does not exist.",
path.display(),
);
ensure!(
path.is_dir(),
"Cache path ({}) is not a directory.",
path.display()
);

// Delete old tldr-pages cache location as well if present
// TODO: To be removed in the future
for pages_dir_name in [TLDR_PAGES_DIR, TLDR_OLD_PAGES_DIR] {
let pages_dir = path.join(pages_dir_name);

if pages_dir.exists() {
fs::remove_dir_all(&pages_dir).with_context(|| {
format!(
"Could not remove the cache directory at {}",
pages_dir.display()
)
})?;
}
} else if path.exists() {
return Err(CacheError(format!(
"Cache path ({}) is not a directory.",
path.display()
)));
} else {
return Err(CacheError(format!(
"Cache path ({}) does not exist.",
path.display()
)));
};
}

Ok(())
}
}
Expand Down
15 changes: 0 additions & 15 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,10 @@ use std::fmt;

use reqwest::Error as ReqwestError;


#[derive(Debug)]
#[allow(clippy::enum_variant_names)]
pub enum TealdeerError {
CacheError(String),
UpdateError(String),
WriteError(String),
}

impl TealdeerError {
pub fn message(&self) -> &str {
match self {
Self::CacheError(msg)
| Self::UpdateError(msg)
| Self::WriteError(msg) => msg,
}
}
}

impl From<ReqwestError> for TealdeerError {
Expand All @@ -30,9 +17,7 @@ impl From<ReqwestError> for TealdeerError {
impl fmt::Display for TealdeerError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::CacheError(e) => write!(f, "CacheError: {}", e),
Self::UpdateError(e) => write!(f, "UpdateError: {}", e),
Self::WriteError(e) => write!(f, "WriteError: {}", e),
}
}
}
31 changes: 11 additions & 20 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ use crate::{
extensions::Dedup,
output::print_page,
types::{ColorOptions, PlatformType},
utils::{print_anyhow_error, print_error, print_warning},
utils::{print_error, print_warning},
};

const NAME: &str = "tealdeer";
Expand Down Expand Up @@ -210,10 +210,7 @@ fn check_cache(args: &Args, enable_styles: bool) -> CheckCacheResult {
/// Clear the cache
fn clear_cache(quietly: bool, enable_styles: bool) {
Cache::clear().unwrap_or_else(|e| {
print_error(
enable_styles,
&format!("Could not delete cache: {}", e.message()),
);
print_error(enable_styles, &e.context("Could not clear cache"));
process::exit(1);
});
if !quietly {
Expand All @@ -224,10 +221,7 @@ fn clear_cache(quietly: bool, enable_styles: bool) {
/// Update the cache
fn update_cache(cache: &Cache, quietly: bool, enable_styles: bool) {
cache.update().unwrap_or_else(|e| {
print_error(
enable_styles,
&format!("Could not update cache: {}", e.message()),
);
print_error(enable_styles, &e.context("Could not update cache"));
process::exit(1);
});
if !quietly {
Expand All @@ -242,7 +236,7 @@ fn show_config_path(enable_styles: bool) {
println!("Config path is: {}", config_file_path.to_str().unwrap());
}
Err(e) => {
print_anyhow_error(enable_styles, &e.context("Could not look up config path"));
print_error(enable_styles, &e.context("Could not look up config path"));
process::exit(1);
}
}
Expand Down Expand Up @@ -309,7 +303,7 @@ fn create_config_and_exit(enable_styles: bool) {
process::exit(0);
}
Err(e) => {
print_anyhow_error(enable_styles, &e);
print_error(enable_styles, &e);
process::exit(1);
}
}
Expand Down Expand Up @@ -414,7 +408,7 @@ fn main() {
let config = match Config::load(enable_styles) {
Ok(config) => config,
Err(e) => {
print_anyhow_error(enable_styles, &e.context("Could not load config"));
print_error(enable_styles, &e.context("Could not load config"));
process::exit(1);
}
};
Expand All @@ -440,8 +434,8 @@ fn main() {
// If a local file was passed in, render it and exit
if let Some(file) = args.render {
let path = PageLookupResult::with_page(file);
if let Err(msg) = print_page(&path, args.raw, &config) {
print_error(enable_styles, &msg);
if let Err(ref e) = print_page(&path, args.raw, &config) {
print_error(enable_styles, e);
process::exit(1);
} else {
process::exit(0);
Expand Down Expand Up @@ -476,10 +470,7 @@ fn main() {
if args.list {
// Get list of pages
let pages = cache.list_pages().unwrap_or_else(|e| {
print_error(
enable_styles,
&format!("Could not get list of pages: {}", e.message()),
);
print_error(enable_styles, &e.context("Could not get list of pages"));
process::exit(1);
});

Expand All @@ -506,8 +497,8 @@ fn main() {
&languages,
config.directories.custom_pages_dir.as_deref(),
) {
if let Err(msg) = print_page(&lookup_result, args.raw, &config) {
print_error(enable_styles, &msg);
if let Err(ref e) = print_page(&lookup_result, args.raw, &config) {
print_error(enable_styles, e);
process::exit(1);
}
process::exit(0);
Expand Down
Loading

0 comments on commit fd30775

Please sign in to comment.