mirror of
https://github.com/sharkdp/bat.git
synced 2025-09-02 03:12:25 +01:00
Add metadata information to cached assets
When saving/reading user-provided syntaxes or themes, `bat` will now maintain a `metadata.yaml` file which includes information about the `bat` version which was used to create the cached files. When loading cached files, we now print an error if they have been created with an incompatible version closes #882
This commit is contained in:
@@ -8,6 +8,7 @@ use syntect::dumps::{dump_to_file, from_binary, from_reader};
|
||||
use syntect::highlighting::{Theme, ThemeSet};
|
||||
use syntect::parsing::{SyntaxReference, SyntaxSet, SyntaxSetBuilder};
|
||||
|
||||
use crate::assets_metadata::AssetsMetadata;
|
||||
use crate::errors::*;
|
||||
use crate::inputfile::{InputFile, InputFileReader};
|
||||
use crate::syntax_mapping::{MappingTarget, SyntaxMapping};
|
||||
@@ -68,8 +69,11 @@ impl HighlightingAssets {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn from_cache(theme_set_path: &Path, syntax_set_path: &Path) -> Result<Self> {
|
||||
let syntax_set_file = File::open(syntax_set_path).chain_err(|| {
|
||||
pub fn from_cache(cache_path: &Path) -> Result<Self> {
|
||||
let syntax_set_path = cache_path.join("syntaxes.bin");
|
||||
let theme_set_path = cache_path.join("themes.bin");
|
||||
|
||||
let syntax_set_file = File::open(&syntax_set_path).chain_err(|| {
|
||||
format!(
|
||||
"Could not load cached syntax set '{}'",
|
||||
syntax_set_path.to_string_lossy()
|
||||
@@ -142,6 +146,13 @@ impl HighlightingAssets {
|
||||
})?;
|
||||
println!("okay");
|
||||
|
||||
print!(
|
||||
"Writing metadata to folder {} ... ",
|
||||
target_dir.to_string_lossy()
|
||||
);
|
||||
AssetsMetadata::new().save_to_folder(target_dir)?;
|
||||
println!("okay");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
82
src/assets_metadata.rs
Normal file
82
src/assets_metadata.rs
Normal file
@@ -0,0 +1,82 @@
|
||||
use std::fs::File;
|
||||
use std::path::Path;
|
||||
use std::time::SystemTime;
|
||||
|
||||
use clap::crate_version;
|
||||
use semver::Version;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::errors::*;
|
||||
|
||||
#[derive(Debug, PartialEq, Default, Serialize, Deserialize)]
|
||||
pub struct AssetsMetadata {
|
||||
bat_version: Option<String>,
|
||||
creation_time: Option<SystemTime>,
|
||||
}
|
||||
|
||||
const FILENAME: &'static str = "metadata.yaml";
|
||||
|
||||
impl AssetsMetadata {
|
||||
pub(crate) fn new() -> AssetsMetadata {
|
||||
AssetsMetadata {
|
||||
bat_version: Some(crate_version!().into()),
|
||||
creation_time: Some(SystemTime::now()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn save_to_folder(&self, path: &Path) -> Result<()> {
|
||||
let file = File::create(path.join(FILENAME))?;
|
||||
serde_yaml::to_writer(file, self)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn try_load_from_folder(path: &Path) -> Result<Self> {
|
||||
let file = File::open(path.join(FILENAME))?;
|
||||
Ok(serde_yaml::from_reader(file)?)
|
||||
}
|
||||
|
||||
/// Load metadata about the stored cache file from the given folder.
|
||||
///
|
||||
/// There are several possibilities:
|
||||
/// - We find a metadata.yaml file and are able to parse it
|
||||
/// => return the contained information
|
||||
/// - We find a metadata.yaml file and but are not able to parse it
|
||||
/// => return a SerdeYamlError
|
||||
/// - We do not find a metadata.yaml file but a syntaxes.bin or themes.bin file
|
||||
/// => assume that these were created by an old version of bat and return
|
||||
/// AssetsMetadata::default() without version information
|
||||
/// - We do not find a metadata.yaml file and no cached assets
|
||||
/// => no user provided assets are available, return None
|
||||
pub fn load_from_folder(path: &Path) -> Result<Option<Self>> {
|
||||
match Self::try_load_from_folder(path) {
|
||||
Ok(metadata) => Ok(Some(metadata)),
|
||||
Err(e) => match e.kind() {
|
||||
ErrorKind::SerdeYamlError(_) => Err(e),
|
||||
_ => {
|
||||
if path.join("syntaxes.bin").exists() || path.join("themes.bin").exists() {
|
||||
Ok(Some(Self::default()))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_compatible(&self) -> bool {
|
||||
let current_version =
|
||||
Version::parse(crate_version!()).expect("bat follows semantic versioning");
|
||||
let stored_version = self
|
||||
.bat_version
|
||||
.as_ref()
|
||||
.and_then(|ver| Version::parse(ver).ok());
|
||||
|
||||
if let Some(stored_version) = stored_version {
|
||||
current_version.major == stored_version.major
|
||||
&& current_version.minor == stored_version.minor
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,18 +1,12 @@
|
||||
use std::borrow::Cow;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use clap::crate_version;
|
||||
|
||||
use crate::directories::PROJECT_DIRS;
|
||||
|
||||
use bat::HighlightingAssets;
|
||||
|
||||
fn theme_set_path() -> PathBuf {
|
||||
PROJECT_DIRS.cache_dir().join("themes.bin")
|
||||
}
|
||||
|
||||
fn syntax_set_path() -> PathBuf {
|
||||
PROJECT_DIRS.cache_dir().join("syntaxes.bin")
|
||||
}
|
||||
use bat::errors::*;
|
||||
use bat::{AssetsMetadata, HighlightingAssets};
|
||||
|
||||
pub fn config_dir() -> Cow<'static, str> {
|
||||
PROJECT_DIRS.config_dir().to_string_lossy()
|
||||
@@ -23,16 +17,40 @@ pub fn cache_dir() -> Cow<'static, str> {
|
||||
}
|
||||
|
||||
pub fn clear_assets() {
|
||||
let theme_set_path = PROJECT_DIRS.cache_dir().join("themes.bin");
|
||||
let syntax_set_path = PROJECT_DIRS.cache_dir().join("syntaxes.bin");
|
||||
let metadata_file = PROJECT_DIRS.cache_dir().join("metadata.yaml");
|
||||
|
||||
print!("Clearing theme set cache ... ");
|
||||
fs::remove_file(theme_set_path()).ok();
|
||||
fs::remove_file(theme_set_path).ok();
|
||||
println!("okay");
|
||||
|
||||
print!("Clearing syntax set cache ... ");
|
||||
fs::remove_file(syntax_set_path()).ok();
|
||||
fs::remove_file(syntax_set_path).ok();
|
||||
println!("okay");
|
||||
|
||||
print!("Clearing metadata file ... ");
|
||||
fs::remove_file(metadata_file).ok();
|
||||
println!("okay");
|
||||
}
|
||||
|
||||
pub fn assets_from_cache_or_binary() -> HighlightingAssets {
|
||||
HighlightingAssets::from_cache(&theme_set_path(), &syntax_set_path())
|
||||
.unwrap_or(HighlightingAssets::from_binary())
|
||||
pub fn assets_from_cache_or_binary() -> Result<HighlightingAssets> {
|
||||
let cache_dir = PROJECT_DIRS.cache_dir();
|
||||
if let Some(metadata) = AssetsMetadata::load_from_folder(&cache_dir)? {
|
||||
if !metadata.is_compatible() {
|
||||
return Err(format!(
|
||||
"The binary caches for the user-customized syntaxes and themes \
|
||||
in '{}' are not compatible with this version of bat ({}). To solve this, \
|
||||
either rebuild the cache (bat cache --build) or remove \
|
||||
the custom syntaxes/themes (bat cache --clear).\n\
|
||||
For more information, see:\n\n \
|
||||
https://github.com/sharkdp/bat#adding-new-syntaxes--language-definitions",
|
||||
cache_dir.to_string_lossy(),
|
||||
crate_version!()
|
||||
)
|
||||
.into());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(HighlightingAssets::from_cache(&cache_dir).unwrap_or(HighlightingAssets::from_binary()))
|
||||
}
|
||||
|
@@ -54,7 +54,7 @@ fn run_cache_subcommand(matches: &clap::ArgMatches) -> Result<()> {
|
||||
}
|
||||
|
||||
pub fn list_languages(config: &Config) -> Result<()> {
|
||||
let assets = assets_from_cache_or_binary();
|
||||
let assets = assets_from_cache_or_binary()?;
|
||||
let mut languages = assets
|
||||
.syntaxes()
|
||||
.iter()
|
||||
@@ -116,7 +116,7 @@ pub fn list_languages(config: &Config) -> Result<()> {
|
||||
}
|
||||
|
||||
pub fn list_themes(cfg: &Config) -> Result<()> {
|
||||
let assets = assets_from_cache_or_binary();
|
||||
let assets = assets_from_cache_or_binary()?;
|
||||
let mut config = cfg.clone();
|
||||
let mut style = HashSet::new();
|
||||
style.insert(StyleComponent::Plain);
|
||||
@@ -147,7 +147,7 @@ pub fn list_themes(cfg: &Config) -> Result<()> {
|
||||
}
|
||||
|
||||
fn run_controller(config: &Config) -> Result<bool> {
|
||||
let assets = assets_from_cache_or_binary();
|
||||
let assets = assets_from_cache_or_binary()?;
|
||||
let controller = Controller::new(&config, &assets);
|
||||
controller.run()
|
||||
}
|
||||
|
@@ -7,18 +7,27 @@ error_chain! {
|
||||
SyntectError(::syntect::LoadingError);
|
||||
ParseIntError(::std::num::ParseIntError);
|
||||
GlobParsingError(::globset::Error);
|
||||
SerdeYamlError(::serde_yaml::Error);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_error_handler(error: &Error) {
|
||||
use ansi_term::Colour::Red;
|
||||
|
||||
match error {
|
||||
Error(ErrorKind::Io(ref io_error), _)
|
||||
if io_error.kind() == ::std::io::ErrorKind::BrokenPipe =>
|
||||
{
|
||||
::std::process::exit(0);
|
||||
}
|
||||
Error(ErrorKind::SerdeYamlError(_), _) => {
|
||||
eprintln!(
|
||||
"{}: Error while parsing metadata.yaml file: {}",
|
||||
Red.paint("[bat error]"),
|
||||
error
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
use ansi_term::Colour::Red;
|
||||
eprintln!("{}: {}", Red.paint("[bat error]"), error);
|
||||
}
|
||||
};
|
||||
|
@@ -2,6 +2,7 @@
|
||||
#![recursion_limit = "1024"]
|
||||
|
||||
pub(crate) mod assets;
|
||||
pub(crate) mod assets_metadata;
|
||||
pub mod config;
|
||||
pub(crate) mod controller;
|
||||
mod decorations;
|
||||
@@ -19,5 +20,6 @@ mod terminal;
|
||||
pub(crate) mod wrap;
|
||||
|
||||
pub use assets::HighlightingAssets;
|
||||
pub use assets_metadata::AssetsMetadata;
|
||||
pub use controller::Controller;
|
||||
pub use printer::{InteractivePrinter, Printer, SimplePrinter};
|
||||
|
Reference in New Issue
Block a user