From 25fa577cd06c9753756d116410aaa19735c95085 Mon Sep 17 00:00:00 2001
From: Martin Nordholts <enselic@gmail.com>
Date: Tue, 10 Aug 2021 22:18:47 +0200
Subject: [PATCH] Make 'build-assets' an optional capability for application

Also structure features a bit more clever to avoid duplication of
feature dependency declarations.
---
 .github/workflows/CICD.yml |  4 ++--
 Cargo.toml                 | 22 ++++++++--------------
 src/assets.rs              | 20 +++++++++-----------
 src/assets_metadata.rs     |  2 ++
 src/bin/bat/main.rs        | 36 +++++++++++++++++++++---------------
 src/config.rs              |  2 +-
 src/error.rs               |  2 +-
 src/lib.rs                 |  1 +
 8 files changed, 45 insertions(+), 44 deletions(-)

diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml
index 45f2054c..220a12d2 100644
--- a/.github/workflows/CICD.yml
+++ b/.github/workflows/CICD.yml
@@ -246,12 +246,12 @@ jobs:
         command: check
         args: --locked --target=${{ matrix.job.target }} --verbose --lib --no-default-features --features regex-onig,git,paging
 
-    - name: "Feature check: quick-build-application"
+    - name: "Feature check: minimal-application"
       uses: actions-rs/cargo@v1
       with:
         use-cross: ${{ matrix.job.use-cross }}
         command: check
-        args: --locked --target=${{ matrix.job.target }} --verbose --no-default-features --features quick-build-application
+        args: --locked --target=${{ matrix.job.target }} --verbose --no-default-features --features minimal-application
 
     - name: Create tarball
       id: package
diff --git a/Cargo.toml b/Cargo.toml
index 7a199080..349972ca 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,25 +12,18 @@ build = "build.rs"
 edition = '2018'
 
 [features]
-default = ["full-application"]
+default = ["application"]
 # Feature required for bat the application. Should be disabled when depending on
 # bat as a library.
-full-application = [
-    "application",
-    "atty",
+application = [
     "bugreport",
-    "clap",
-    "dirs-next",
+    "build-assets",
     "git",
-    "lazy_static",
-    "paging",
-    "regex-onig",
-    "wild",
+    "minimal-application",
 ]
 # Mainly for developers that want to iterate quickly
 # Be aware that the included features might change in the future
-quick-build-application = [
-    "application",
+minimal-application = [
     "atty",
     "clap",
     "dirs-next",
@@ -39,9 +32,10 @@ quick-build-application = [
     "regex-onig",
     "wild",
 ]
-application = []
 git = ["git2"] # Support indicating git modifications
 paging = ["shell-words"] # Support applying a pager on the output
+# Add "syntect/plist-load" when https://github.com/trishume/syntect/pull/345 reaches us
+build-assets = ["syntect/yaml-load", "syntect/dump-create"]
 
 # You need to use one of these if you depend on bat as a library:
 regex-onig = ["syntect/regex-onig"] # Use the "oniguruma" regex engine
@@ -77,7 +71,7 @@ default-features = false
 [dependencies.syntect]
 version = "4.6.0"
 default-features = false
-features = ["parsing", "yaml-load", "dump-load", "dump-create"]
+features = ["parsing", "dump-load"]
 
 [dependencies.clap]
 version = "2.33"
diff --git a/src/assets.rs b/src/assets.rs
index 0d11e9f5..71a63bbc 100644
--- a/src/assets.rs
+++ b/src/assets.rs
@@ -4,13 +4,12 @@ use std::path::{Path, PathBuf};
 
 use lazycell::LazyCell;
 
-use syntect::dumps::{dump_to_file, from_binary, from_reader};
+use syntect::dumps::{from_binary, from_reader};
 use syntect::highlighting::{Theme, ThemeSet};
-use syntect::parsing::{SyntaxReference, SyntaxSet, SyntaxSetBuilder};
+use syntect::parsing::{SyntaxReference, SyntaxSet};
 
 use path_abs::PathAbs;
 
-use crate::assets_metadata::AssetsMetadata;
 use crate::bat_warning;
 use crate::error::*;
 use crate::input::{InputReader, OpenedInput, OpenedInputKind};
@@ -72,6 +71,7 @@ impl HighlightingAssets {
         "Monokai Extended"
     }
 
+    #[cfg(feature = "build-assets")]
     pub fn from_files(source_dir: &Path, include_integrated_assets: bool) -> Result<Self> {
         let mut theme_set = if include_integrated_assets {
             get_integrated_themeset()
@@ -97,11 +97,11 @@ impl HighlightingAssets {
         }
 
         let mut syntax_set_builder = if !include_integrated_assets {
-            let mut builder = SyntaxSetBuilder::new();
+            let mut builder = syntect::parsing::SyntaxSetBuilder::new();
             builder.add_plain_text_syntax();
             builder
         } else {
-            get_integrated_syntaxset().into_builder()
+            from_binary::<SyntaxSet>(get_serialized_integrated_syntaxset()).into_builder()
         };
 
         let syntax_dir = source_dir.join("syntaxes");
@@ -152,6 +152,7 @@ impl HighlightingAssets {
         )
     }
 
+    #[cfg(feature = "build-assets")]
     pub fn save_to_cache(&self, target_dir: &Path, current_version: &str) -> Result<()> {
         let _ = fs::create_dir_all(target_dir);
         asset_to_cache(
@@ -169,7 +170,7 @@ impl HighlightingAssets {
             "Writing metadata to folder {} ... ",
             target_dir.to_string_lossy()
         );
-        AssetsMetadata::new(current_version).save_to_folder(target_dir)?;
+        crate::assets_metadata::AssetsMetadata::new(current_version).save_to_folder(target_dir)?;
         println!("okay");
 
         Ok(())
@@ -416,17 +417,14 @@ fn get_serialized_integrated_syntaxset() -> &'static [u8] {
     include_bytes!("../assets/syntaxes.bin")
 }
 
-fn get_integrated_syntaxset() -> SyntaxSet {
-    from_binary(get_serialized_integrated_syntaxset())
-}
-
 fn get_integrated_themeset() -> ThemeSet {
     from_binary(include_bytes!("../assets/themes.bin"))
 }
 
+#[cfg(feature = "build-assets")]
 fn asset_to_cache<T: serde::Serialize>(asset: &T, path: &Path, description: &str) -> Result<()> {
     print!("Writing {} to {} ... ", description, path.to_string_lossy());
-    dump_to_file(asset, &path).chain_err(|| {
+    syntect::dumps::dump_to_file(asset, &path).chain_err(|| {
         format!(
             "Could not save {} to {}",
             description,
diff --git a/src/assets_metadata.rs b/src/assets_metadata.rs
index 91fc7679..c9d71858 100644
--- a/src/assets_metadata.rs
+++ b/src/assets_metadata.rs
@@ -16,6 +16,7 @@ pub struct AssetsMetadata {
 const FILENAME: &str = "metadata.yaml";
 
 impl AssetsMetadata {
+    #[cfg(feature = "build-assets")]
     pub(crate) fn new(current_version: &str) -> AssetsMetadata {
         AssetsMetadata {
             bat_version: Some(current_version.to_owned()),
@@ -23,6 +24,7 @@ impl AssetsMetadata {
         }
     }
 
+    #[cfg(feature = "build-assets")]
     pub(crate) fn save_to_folder(&self, path: &Path) -> Result<()> {
         let file = File::create(path.join(FILENAME))?;
         serde_yaml::to_writer(file, self)?;
diff --git a/src/bin/bat/main.rs b/src/bin/bat/main.rs
index c3daf949..6264bc9d 100644
--- a/src/bin/bat/main.rs
+++ b/src/bin/bat/main.rs
@@ -23,12 +23,10 @@ use crate::{
 };
 
 use assets::{assets_from_cache_or_binary, cache_dir, clear_assets, config_dir};
-use clap::crate_version;
 use directories::PROJECT_DIRS;
 use globset::GlobMatcher;
 
 use bat::{
-    assets::HighlightingAssets,
     config::Config,
     controller::Controller,
     error::*,
@@ -39,21 +37,29 @@ use bat::{
 
 const THEME_PREVIEW_DATA: &[u8] = include_bytes!("../../../assets/theme_preview.rs");
 
+#[cfg(feature = "build-assets")]
+fn build_assets(matches: &clap::ArgMatches) -> Result<()> {
+    let source_dir = matches
+        .value_of("source")
+        .map(Path::new)
+        .unwrap_or_else(|| PROJECT_DIRS.config_dir());
+    let target_dir = matches
+        .value_of("target")
+        .map(Path::new)
+        .unwrap_or_else(|| PROJECT_DIRS.cache_dir());
+
+    let blank = matches.is_present("blank");
+
+    let assets = bat::assets::HighlightingAssets::from_files(source_dir, !blank)?;
+    assets.save_to_cache(target_dir, clap::crate_version!())
+}
+
 fn run_cache_subcommand(matches: &clap::ArgMatches) -> Result<()> {
     if matches.is_present("build") {
-        let source_dir = matches
-            .value_of("source")
-            .map(Path::new)
-            .unwrap_or_else(|| PROJECT_DIRS.config_dir());
-        let target_dir = matches
-            .value_of("target")
-            .map(Path::new)
-            .unwrap_or_else(|| PROJECT_DIRS.cache_dir());
-
-        let blank = matches.is_present("blank");
-
-        let assets = HighlightingAssets::from_files(source_dir, !blank)?;
-        assets.save_to_cache(target_dir, crate_version!())?;
+        #[cfg(feature = "build-assets")]
+        build_assets(matches)?;
+        #[cfg(not(feature = "build-assets"))]
+        println!("bat has been built without the 'build-assets' feature. The 'cache --build' option is not available.");
     } else if matches.is_present("clear") {
         clear_assets();
     }
diff --git a/src/config.rs b/src/config.rs
index 2794d5f3..ac9a0972 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -88,7 +88,7 @@ pub struct Config<'a> {
     pub use_custom_assets: bool,
 }
 
-#[cfg(all(feature = "application", feature = "paging"))]
+#[cfg(all(feature = "minimal-application", feature = "paging"))]
 pub fn get_pager_executable(config_pager: Option<&str>) -> Option<String> {
     if let Ok(Some(pager)) = crate::pager::get_pager(config_pager) {
         Some(pager.bin)
diff --git a/src/error.rs b/src/error.rs
index 3e5b3711..0448fd07 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -3,7 +3,7 @@ use std::io::Write;
 
 error_chain! {
     foreign_links {
-        Clap(::clap::Error) #[cfg(feature = "application")];
+        Clap(::clap::Error) #[cfg(feature = "minimal-application")];
         Io(::std::io::Error);
         SyntectError(::syntect::LoadingError);
         ParseIntError(::std::num::ParseIntError);
diff --git a/src/lib.rs b/src/lib.rs
index daf241fc..f495c482 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -40,6 +40,7 @@ mod preprocessor;
 mod pretty_printer;
 pub(crate) mod printer;
 pub mod style;
+#[cfg(feature = "build-assets")]
 mod syntax_dependencies;
 pub(crate) mod syntax_mapping;
 mod terminal;