Change input syntax and add cache storage handler

- input syntax changed to <subcommand> <options...>
- Added clear-cache subcommand
- CacheStorage now handles the caching of files

Signed-off-by: trivernis <trivernis@protonmail.com>
pull/4/head
trivernis 3 years ago
parent d42821a6eb
commit 53c4818f9d
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

@ -8,7 +8,7 @@ use crate::references::configuration::keys::{
PDF_PAGE_SCALE, PDF_PAGE_WIDTH,
};
use crate::references::configuration::Configuration;
use crate::utils::downloads::get_cached_path;
use crate::utils::caching::CacheStorage;
use headless_chrome::protocol::page::PrintToPdfOptions;
use headless_chrome::{Browser, LaunchOptionsBuilder, Tab};
use std::fs;
@ -22,8 +22,9 @@ pub mod result;
/// Renders the document to pdf and returns the resulting bytes
pub fn render_to_pdf(document: Document) -> PdfRenderingResult<Vec<u8>> {
let cache = CacheStorage::new();
let mut file_path = PathBuf::from(format!("tmp-document.html"));
file_path = get_cached_path(file_path).with_extension("html");
file_path = cache.get_file_path(&file_path);
let mut mathjax = false;
if let Some(entry) = document.config.get_entry(INCLUDE_MATHJAX) {

@ -6,16 +6,38 @@ use snekdown::elements::Document;
use snekdown::format::html::html_writer::HTMLWriter;
use snekdown::format::html::to_html::ToHtml;
use snekdown::parser::ParserOptions;
use snekdown::utils::caching::CacheStorage;
use snekdown::Parser;
use std::fs::{File, OpenOptions};
use std::io::{BufWriter, Write};
use std::path::PathBuf;
use std::process::exit;
use std::sync::mpsc::channel;
use std::time::{Duration, Instant};
use structopt::StructOpt;
#[derive(StructOpt, Debug)]
struct Opt {
#[structopt(subcommand)]
sub_command: SubCommand,
}
#[derive(StructOpt, Debug)]
#[structopt()]
enum SubCommand {
/// Watch the document and its imports and render on change.
Watch(RenderOptions),
/// Parse and render the document.
Render(RenderOptions),
/// Clears the cache directory
ClearCache,
}
#[derive(StructOpt, Debug)]
#[structopt()]
struct RenderOptions {
/// Path to the input file
#[structopt(parse(from_os_str))]
input: PathBuf,
@ -31,19 +53,6 @@ struct Opt {
/// Don't use the cache
#[structopt(long)]
no_cache: bool,
#[structopt(subcommand)]
sub_command: Option<SubCommand>,
}
#[derive(StructOpt, Debug)]
#[structopt()]
enum SubCommand {
/// Watch the document and its imports and render on change.
Watch,
/// Default. Parse and render the document.
Render,
}
fn main() {
@ -68,19 +77,16 @@ fn main() {
)
})
.init();
if !opt.input.exists() {
log::error!(
"The input file {} could not be found",
opt.input.to_str().unwrap()
);
return;
}
match &opt.sub_command {
Some(SubCommand::Render) | None => {
SubCommand::Render(opt) => {
let _ = render(&opt);
}
Some(SubCommand::Watch) => watch(&opt),
SubCommand::Watch(opt) => watch(&opt),
SubCommand::ClearCache => {
let cache = CacheStorage::new();
cache.clear().expect("Failed to clear cache");
}
};
}
@ -95,7 +101,7 @@ fn get_level_style(level: Level) -> colored::Color {
}
/// Watches a file with all of its imports and renders on change
fn watch(opt: &Opt) {
fn watch(opt: &RenderOptions) {
let parser = render(opt);
let (tx, rx) = channel();
let mut watcher = watcher(tx, Duration::from_millis(250)).unwrap();
@ -112,7 +118,15 @@ fn watch(opt: &Opt) {
}
/// Renders the document to the output path
fn render(opt: &Opt) -> Parser {
fn render(opt: &RenderOptions) -> Parser {
if !opt.input.exists() {
log::error!(
"The input file {} could not be found",
opt.input.to_str().unwrap()
);
exit(1)
}
let start = Instant::now();
let mut parser = Parser::with_defaults(
@ -142,7 +156,7 @@ fn render(opt: &Opt) -> Parser {
}
#[cfg(not(feature = "pdf"))]
fn render_format(opt: &Opt, document: Document, writer: BufWriter<File>) {
fn render_format(opt: &RenderOptions, document: Document, writer: BufWriter<File>) {
match opt.format.as_str() {
"html" => render_html(document, writer),
_ => log::error!("Unknown format {}", opt.format),
@ -150,7 +164,7 @@ fn render_format(opt: &Opt, document: Document, writer: BufWriter<File>) {
}
#[cfg(feature = "pdf")]
fn render_format(opt: &Opt, document: Document, writer: BufWriter<File>) {
fn render_format(opt: &RenderOptions, document: Document, writer: BufWriter<File>) {
match opt.format.as_str() {
"html" => render_html(document, writer),
"pdf" => render_pdf(document, writer),

@ -0,0 +1,63 @@
use platform_dirs::{AppDirs, AppUI};
use std::collections::hash_map::DefaultHasher;
use std::fs;
use std::hash::{Hash, Hasher};
use std::io;
use std::path::PathBuf;
#[derive(Clone, Debug)]
pub struct CacheStorage {
location: PathBuf,
}
impl CacheStorage {
pub fn new() -> Self {
lazy_static::lazy_static! {
static ref APP_DIRS: AppDirs = AppDirs::new(Some("snekdown"), AppUI::CommandLine).unwrap();
}
Self {
location: APP_DIRS.cache_dir.clone(),
}
}
/// Returns the cache path for a given file
pub fn get_file_path(&self, path: &PathBuf) -> PathBuf {
let mut hasher = DefaultHasher::new();
path.hash(&mut hasher);
let mut file_name = PathBuf::from(format!("{:x}", hasher.finish()));
if let Some(extension) = path.extension() {
file_name.set_extension(extension);
}
return self.location.join(PathBuf::from(file_name));
}
/// Returns if the given file exists in the cache
pub fn has_file(&self, path: &PathBuf) -> bool {
let cache_path = self.get_file_path(path);
cache_path.exists()
}
/// Writes into the corresponding cache file
pub fn read(&self, path: &PathBuf) -> io::Result<Vec<u8>> {
let cache_path = self.get_file_path(path);
fs::read(cache_path)
}
/// Reads the corresponding cache file
pub fn write<R: AsRef<[u8]>>(&self, path: &PathBuf, contents: R) -> io::Result<()> {
let cache_path = self.get_file_path(path);
fs::write(cache_path, contents)
}
/// Clears the cache directory by deleting and recreating it
pub fn clear(&self) -> io::Result<()> {
fs::remove_dir_all(&self.location)?;
fs::create_dir(&self.location)
}
}

@ -1,10 +1,7 @@
use crate::utils::caching::CacheStorage;
use indicatif::{ProgressBar, ProgressStyle};
use platform_dirs::{AppDirs, AppUI};
use rayon::prelude::*;
use std::collections::hash_map::DefaultHasher;
use std::fs;
use std::fs::read;
use std::hash::{Hash, Hasher};
use std::path::PathBuf;
use std::sync::{Arc, Mutex};
@ -60,6 +57,7 @@ pub struct PendingDownload {
pub(crate) path: String,
pub(crate) data: Option<Vec<u8>>,
pub(crate) use_cache: bool,
cache: CacheStorage,
}
impl PendingDownload {
@ -68,6 +66,7 @@ impl PendingDownload {
path,
data: None,
use_cache: true,
cache: CacheStorage::new(),
}
}
@ -98,22 +97,18 @@ impl PendingDownload {
/// Stores the data to a cache file to retrieve it later
fn store_to_cache(&self, data: &Vec<u8>) {
if self.use_cache {
let cache_file = get_cached_path(PathBuf::from(&self.path));
log::debug!("Writing to cache {} -> {:?}", self.path, cache_file);
fs::write(&cache_file, data.clone()).unwrap_or_else(|_| {
log::warn!(
"Failed to write file to cache: {} -> {:?}",
self.path,
cache_file
)
});
let path = PathBuf::from(&self.path);
self.cache
.write(&path, data.clone())
.unwrap_or_else(|_| log::warn!("Failed to write file to cache: {}", self.path));
}
}
fn read_from_cache(&self) -> Option<Vec<u8>> {
let cache_path = get_cached_path(PathBuf::from(&self.path));
if cache_path.exists() && self.use_cache {
read(cache_path).ok()
let path = PathBuf::from(&self.path);
if self.cache.has_file(&path) && self.use_cache {
self.cache.read(&path).ok()
} else {
None
}
@ -128,19 +123,3 @@ impl PendingDownload {
.map(|b| b.to_vec())
}
}
pub fn get_cached_path(path: PathBuf) -> PathBuf {
lazy_static::lazy_static! {
static ref APP_DIRS: AppDirs = AppDirs::new(Some("snekdown"), AppUI::CommandLine).unwrap();
}
let mut hasher = DefaultHasher::new();
path.hash(&mut hasher);
let file_name = PathBuf::from(format!("{:x}", hasher.finish()));
if !APP_DIRS.cache_dir.is_dir() {
fs::create_dir(&APP_DIRS.cache_dir)
.unwrap_or_else(|_| log::warn!("Failed to create cache dir {:?}", APP_DIRS.cache_dir))
}
APP_DIRS.cache_dir.join(file_name)
}

@ -1,3 +1,4 @@
pub mod caching;
pub mod downloads;
pub mod macros;
pub mod parsing;

Loading…
Cancel
Save