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, PDF_PAGE_SCALE, PDF_PAGE_WIDTH,
}; };
use crate::references::configuration::Configuration; 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::protocol::page::PrintToPdfOptions;
use headless_chrome::{Browser, LaunchOptionsBuilder, Tab}; use headless_chrome::{Browser, LaunchOptionsBuilder, Tab};
use std::fs; use std::fs;
@ -22,8 +22,9 @@ pub mod result;
/// Renders the document to pdf and returns the resulting bytes /// Renders the document to pdf and returns the resulting bytes
pub fn render_to_pdf(document: Document) -> PdfRenderingResult<Vec<u8>> { pub fn render_to_pdf(document: Document) -> PdfRenderingResult<Vec<u8>> {
let cache = CacheStorage::new();
let mut file_path = PathBuf::from(format!("tmp-document.html")); 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; let mut mathjax = false;
if let Some(entry) = document.config.get_entry(INCLUDE_MATHJAX) { 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::html_writer::HTMLWriter;
use snekdown::format::html::to_html::ToHtml; use snekdown::format::html::to_html::ToHtml;
use snekdown::parser::ParserOptions; use snekdown::parser::ParserOptions;
use snekdown::utils::caching::CacheStorage;
use snekdown::Parser; use snekdown::Parser;
use std::fs::{File, OpenOptions}; use std::fs::{File, OpenOptions};
use std::io::{BufWriter, Write}; use std::io::{BufWriter, Write};
use std::path::PathBuf; use std::path::PathBuf;
use std::process::exit;
use std::sync::mpsc::channel; use std::sync::mpsc::channel;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use structopt::StructOpt; use structopt::StructOpt;
#[derive(StructOpt, Debug)] #[derive(StructOpt, Debug)]
struct Opt { 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 /// Path to the input file
#[structopt(parse(from_os_str))] #[structopt(parse(from_os_str))]
input: PathBuf, input: PathBuf,
@ -31,19 +53,6 @@ struct Opt {
/// Don't use the cache /// Don't use the cache
#[structopt(long)] #[structopt(long)]
no_cache: bool, 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() { fn main() {
@ -68,19 +77,16 @@ fn main() {
) )
}) })
.init(); .init();
if !opt.input.exists() {
log::error!(
"The input file {} could not be found",
opt.input.to_str().unwrap()
);
return;
}
match &opt.sub_command { match &opt.sub_command {
Some(SubCommand::Render) | None => { SubCommand::Render(opt) => {
let _ = 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 /// 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 parser = render(opt);
let (tx, rx) = channel(); let (tx, rx) = channel();
let mut watcher = watcher(tx, Duration::from_millis(250)).unwrap(); 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 /// 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 start = Instant::now();
let mut parser = Parser::with_defaults( let mut parser = Parser::with_defaults(
@ -142,7 +156,7 @@ fn render(opt: &Opt) -> Parser {
} }
#[cfg(not(feature = "pdf"))] #[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() { match opt.format.as_str() {
"html" => render_html(document, writer), "html" => render_html(document, writer),
_ => log::error!("Unknown format {}", opt.format), _ => log::error!("Unknown format {}", opt.format),
@ -150,7 +164,7 @@ fn render_format(opt: &Opt, document: Document, writer: BufWriter<File>) {
} }
#[cfg(feature = "pdf")] #[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() { match opt.format.as_str() {
"html" => render_html(document, writer), "html" => render_html(document, writer),
"pdf" => render_pdf(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 indicatif::{ProgressBar, ProgressStyle};
use platform_dirs::{AppDirs, AppUI};
use rayon::prelude::*; use rayon::prelude::*;
use std::collections::hash_map::DefaultHasher;
use std::fs;
use std::fs::read; use std::fs::read;
use std::hash::{Hash, Hasher};
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
@ -60,6 +57,7 @@ pub struct PendingDownload {
pub(crate) path: String, pub(crate) path: String,
pub(crate) data: Option<Vec<u8>>, pub(crate) data: Option<Vec<u8>>,
pub(crate) use_cache: bool, pub(crate) use_cache: bool,
cache: CacheStorage,
} }
impl PendingDownload { impl PendingDownload {
@ -68,6 +66,7 @@ impl PendingDownload {
path, path,
data: None, data: None,
use_cache: true, use_cache: true,
cache: CacheStorage::new(),
} }
} }
@ -98,22 +97,18 @@ impl PendingDownload {
/// Stores the data to a cache file to retrieve it later /// Stores the data to a cache file to retrieve it later
fn store_to_cache(&self, data: &Vec<u8>) { fn store_to_cache(&self, data: &Vec<u8>) {
if self.use_cache { if self.use_cache {
let cache_file = get_cached_path(PathBuf::from(&self.path)); let path = PathBuf::from(&self.path);
log::debug!("Writing to cache {} -> {:?}", self.path, cache_file); self.cache
fs::write(&cache_file, data.clone()).unwrap_or_else(|_| { .write(&path, data.clone())
log::warn!( .unwrap_or_else(|_| log::warn!("Failed to write file to cache: {}", self.path));
"Failed to write file to cache: {} -> {:?}",
self.path,
cache_file
)
});
} }
} }
fn read_from_cache(&self) -> Option<Vec<u8>> { fn read_from_cache(&self) -> Option<Vec<u8>> {
let cache_path = get_cached_path(PathBuf::from(&self.path)); let path = PathBuf::from(&self.path);
if cache_path.exists() && self.use_cache {
read(cache_path).ok() if self.cache.has_file(&path) && self.use_cache {
self.cache.read(&path).ok()
} else { } else {
None None
} }
@ -128,19 +123,3 @@ impl PendingDownload {
.map(|b| b.to_vec()) .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 downloads;
pub mod macros; pub mod macros;
pub mod parsing; pub mod parsing;

Loading…
Cancel
Save