on master help sign in
Add metadata information to cached assets
72618dbsharkdp
CHANGELOG.md+4
@@ -16,6 +16,10 @@
16
16
17## Other
17## Other
18
18
19+- When saving/reading user-provided syntaxes or themes, `bat` will now maintain a
20+ `metadata.yaml` file which includes information about the `bat` version which was
21+ used to create the cached files. When loading cached files, we now print an error
22+ if they have been created with an incompatible version. See #882
19- Updated `liquid` dependency to 0.20, see #880 (@ignatenkobrain)
23- Updated `liquid` dependency to 0.20, see #880 (@ignatenkobrain)
20
24
21## `bat` as a library
25## `bat` as a library
Cargo.lock+36
@@ -130,6 +130,9 @@ dependencies = [
130 "globset 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
130 "globset 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
131 "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
131 "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
132 "liquid 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
132 "liquid 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
133+ "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
134+ "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)",
135+ "serde_yaml 0.8.11 (registry+https://github.com/rust-lang/crates.io-index)",
133 "shell-words 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
136 "shell-words 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
134 "syntect 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
137 "syntect 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
135 "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
138 "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -384,6 +387,11 @@ name = "doc-comment"
384version = "0.3.3"
387version = "0.3.3"
385source = "registry+https://github.com/rust-lang/crates.io-index"
388source = "registry+https://github.com/rust-lang/crates.io-index"
386
389
390+[[package]]
391+name = "dtoa"
392+version = "0.4.5"
393+source = "registry+https://github.com/rust-lang/crates.io-index"
394+
387[[package]]
395[[package]]
388name = "either"
396name = "either"
389version = "1.5.3"
397version = "1.5.3"
@@ -1117,6 +1125,19 @@ dependencies = [
1117 "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
1125 "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
1118]
1126]
1119
1127
1128+[[package]]
1129+name = "semver"
1130+version = "0.9.0"
1131+source = "registry+https://github.com/rust-lang/crates.io-index"
1132+dependencies = [
1133+ "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
1134+]
1135+
1136+[[package]]
1137+name = "semver-parser"
1138+version = "0.7.0"
1139+source = "registry+https://github.com/rust-lang/crates.io-index"
1140+
1120[[package]]
1141[[package]]
1121name = "serde"
1142name = "serde"
1122version = "1.0.106"
1143version = "1.0.106"
@@ -1145,6 +1166,17 @@ dependencies = [
1145 "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)",
1166 "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)",
1146]
1167]
1147
1168
1169+[[package]]
1170+name = "serde_yaml"
1171+version = "0.8.11"
1172+source = "registry+https://github.com/rust-lang/crates.io-index"
1173+dependencies = [
1174+ "dtoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
1175+ "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
1176+ "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)",
1177+ "yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
1178+]
1179+
1148[[package]]
1180[[package]]
1149name = "sha-1"
1181name = "sha-1"
1150version = "0.8.2"
1182version = "0.8.2"
@@ -1494,6 +1526,7 @@ dependencies = [
1494"checksum dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3"
1526"checksum dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3"
1495"checksum dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b"
1527"checksum dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b"
1496"checksum doc-comment 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
1528"checksum doc-comment 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
1529+"checksum dtoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3"
1497"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
1530"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
1498"checksum encode_unicode 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
1531"checksum encode_unicode 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
1499"checksum encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "6b0d943856b990d12d3b55b359144ff341533e516d94098b1d3fc1ac666d36ec"
1532"checksum encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "6b0d943856b990d12d3b55b359144ff341533e516d94098b1d3fc1ac666d36ec"
@@ -1585,9 +1618,12 @@ dependencies = [
1585"checksum ryu 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535622e6be132bccd223f4bb2b8ac8d53cda3c7a6394944d3b2b33fb974f9d76"
1618"checksum ryu 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535622e6be132bccd223f4bb2b8ac8d53cda3c7a6394944d3b2b33fb974f9d76"
1586"checksum safemem 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"
1619"checksum safemem 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"
1587"checksum same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
1620"checksum same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
1621+"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
1622+"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
1588"checksum serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)" = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399"
1623"checksum serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)" = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399"
1589"checksum serde_derive 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)" = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c"
1624"checksum serde_derive 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)" = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c"
1590"checksum serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)" = "da07b57ee2623368351e9a0488bb0b261322a15a6e0ae53e243cbdc0f4208da9"
1625"checksum serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)" = "da07b57ee2623368351e9a0488bb0b261322a15a6e0ae53e243cbdc0f4208da9"
1626+"checksum serde_yaml 0.8.11 (registry+https://github.com/rust-lang/crates.io-index)" = "691b17f19fc1ec9d94ec0b5864859290dff279dbd7b03f017afda54eb36c3c35"
1591"checksum sha-1 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"
1627"checksum sha-1 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"
1592"checksum shell-words 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "39acde55a154c4cd3ae048ac78cc21c25f3a0145e44111b523279113dce0d94a"
1628"checksum shell-words 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "39acde55a154c4cd3ae048ac78cc21c25f3a0145e44111b523279113dce0d94a"
1593"checksum shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2"
1629"checksum shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2"
Cargo.toml+3
@@ -48,6 +48,9 @@ encoding = "0.2"
48shell-words = { version = "0.1.0", optional = true }
48shell-words = { version = "0.1.0", optional = true }
49unicode-width = "0.1.7"
49unicode-width = "0.1.7"
50globset = "0.4"
50globset = "0.4"
51+serde = "1.0"
52+serde_yaml = "0.8"
53+semver = "0.9"
51
54
52[dependencies.git2]
55[dependencies.git2]
53version = "0.13"
56version = "0.13"
src/assets.rs+13−2
@@ -8,6 +8,7 @@ use syntect::dumps::{dump_to_file, from_binary, from_reader};
8use syntect::highlighting::{Theme, ThemeSet};
8use syntect::highlighting::{Theme, ThemeSet};
9use syntect::parsing::{SyntaxReference, SyntaxSet, SyntaxSetBuilder};
9use syntect::parsing::{SyntaxReference, SyntaxSet, SyntaxSetBuilder};
10
10
11+use crate::assets_metadata::AssetsMetadata;
11use crate::errors::*;
12use crate::errors::*;
12use crate::inputfile::{InputFile, InputFileReader};
13use crate::inputfile::{InputFile, InputFileReader};
13use crate::syntax_mapping::{MappingTarget, SyntaxMapping};
14use crate::syntax_mapping::{MappingTarget, SyntaxMapping};
@@ -68,8 +69,11 @@ impl HighlightingAssets {
68 })
69 })
69 }
70 }
70
71
71− pub fn from_cache(theme_set_path: &Path, syntax_set_path: &Path) -> Result<Self> {
72+ pub fn from_cache(cache_path: &Path) -> Result<Self> {
73+ let syntax_set_path = cache_path.join("syntaxes.bin");
74+ let theme_set_path = cache_path.join("themes.bin");
75+
72− let syntax_set_file = File::open(syntax_set_path).chain_err(|| {
76+ let syntax_set_file = File::open(&syntax_set_path).chain_err(|| {
73 format!(
77 format!(
74 "Could not load cached syntax set '{}'",
78 "Could not load cached syntax set '{}'",
75 syntax_set_path.to_string_lossy()
79 syntax_set_path.to_string_lossy()
@@ -142,6 +146,13 @@ impl HighlightingAssets {
142 })?;
146 })?;
143 println!("okay");
147 println!("okay");
144
148
149+ print!(
150+ "Writing metadata to folder {} ... ",
151+ target_dir.to_string_lossy()
152+ );
153+ AssetsMetadata::new().save_to_folder(target_dir)?;
154+ println!("okay");
155+
145 Ok(())
156 Ok(())
146 }
157 }
147
158
src/assets_metadata.rsnew file+82
@@ -0,0 +1,82 @@
1+use std::fs::File;
2+use std::path::Path;
3+use std::time::SystemTime;
4+
5+use clap::crate_version;
6+use semver::Version;
7+use serde::{Deserialize, Serialize};
8+
9+use crate::errors::*;
10+
11+#[derive(Debug, PartialEq, Default, Serialize, Deserialize)]
12+pub struct AssetsMetadata {
13+ bat_version: Option<String>,
14+ creation_time: Option<SystemTime>,
15+}
16+
17+const FILENAME: &'static str = "metadata.yaml";
18+
19+impl AssetsMetadata {
20+ pub(crate) fn new() -> AssetsMetadata {
21+ AssetsMetadata {
22+ bat_version: Some(crate_version!().into()),
23+ creation_time: Some(SystemTime::now()),
24+ }
25+ }
26+
27+ pub(crate) fn save_to_folder(&self, path: &Path) -> Result<()> {
28+ let file = File::create(path.join(FILENAME))?;
29+ serde_yaml::to_writer(file, self)?;
30+
31+ Ok(())
32+ }
33+
34+ fn try_load_from_folder(path: &Path) -> Result<Self> {
35+ let file = File::open(path.join(FILENAME))?;
36+ Ok(serde_yaml::from_reader(file)?)
37+ }
38+
39+ /// Load metadata about the stored cache file from the given folder.
40+ ///
41+ /// There are several possibilities:
42+ /// - We find a metadata.yaml file and are able to parse it
43+ /// => return the contained information
44+ /// - We find a metadata.yaml file and but are not able to parse it
45+ /// => return a SerdeYamlError
46+ /// - We do not find a metadata.yaml file but a syntaxes.bin or themes.bin file
47+ /// => assume that these were created by an old version of bat and return
48+ /// AssetsMetadata::default() without version information
49+ /// - We do not find a metadata.yaml file and no cached assets
50+ /// => no user provided assets are available, return None
51+ pub fn load_from_folder(path: &Path) -> Result<Option<Self>> {
52+ match Self::try_load_from_folder(path) {
53+ Ok(metadata) => Ok(Some(metadata)),
54+ Err(e) => match e.kind() {
55+ ErrorKind::SerdeYamlError(_) => Err(e),
56+ _ => {
57+ if path.join("syntaxes.bin").exists() || path.join("themes.bin").exists() {
58+ Ok(Some(Self::default()))
59+ } else {
60+ Ok(None)
61+ }
62+ }
63+ },
64+ }
65+ }
66+
67+ pub fn is_compatible(&self) -> bool {
68+ let current_version =
69+ Version::parse(crate_version!()).expect("bat follows semantic versioning");
70+ let stored_version = self
71+ .bat_version
72+ .as_ref()
73+ .and_then(|ver| Version::parse(ver).ok());
74+
75+ if let Some(stored_version) = stored_version {
76+ current_version.major == stored_version.major
77+ && current_version.minor == stored_version.minor
78+ } else {
79+ false
80+ }
81+ }
82+}
src/bin/bat/assets.rs+33−15
@@ -1,18 +1,12 @@
1use std::borrow::Cow;
1use std::borrow::Cow;
2use std::fs;
2use std::fs;
3−use std::path::PathBuf;
4
3
5−use crate::directories::PROJECT_DIRS;
4+use clap::crate_version;
6−
7−use bat::HighlightingAssets;
8
5
9−fn theme_set_path() -> PathBuf {
6+use crate::directories::PROJECT_DIRS;
10− PROJECT_DIRS.cache_dir().join("themes.bin")
11−}
12
7
13−fn syntax_set_path() -> PathBuf {
8+use bat::errors::*;
14− PROJECT_DIRS.cache_dir().join("syntaxes.bin")
9+use bat::{AssetsMetadata, HighlightingAssets};
15−}
16
10
17pub fn config_dir() -> Cow<'static, str> {
11pub fn config_dir() -> Cow<'static, str> {
18 PROJECT_DIRS.config_dir().to_string_lossy()
12 PROJECT_DIRS.config_dir().to_string_lossy()
@@ -23,16 +17,40 @@ pub fn cache_dir() -> Cow<'static, str> {
23}
17}
24
18
25pub fn clear_assets() {
19pub fn clear_assets() {
20+ let theme_set_path = PROJECT_DIRS.cache_dir().join("themes.bin");
21+ let syntax_set_path = PROJECT_DIRS.cache_dir().join("syntaxes.bin");
22+ let metadata_file = PROJECT_DIRS.cache_dir().join("metadata.yaml");
23+
26 print!("Clearing theme set cache ... ");
24 print!("Clearing theme set cache ... ");
27− fs::remove_file(theme_set_path()).ok();
25+ fs::remove_file(theme_set_path).ok();
28 println!("okay");
26 println!("okay");
29
27
30 print!("Clearing syntax set cache ... ");
28 print!("Clearing syntax set cache ... ");
31− fs::remove_file(syntax_set_path()).ok();
29+ fs::remove_file(syntax_set_path).ok();
30+ println!("okay");
31+
32+ print!("Clearing metadata file ... ");
33+ fs::remove_file(metadata_file).ok();
32 println!("okay");
34 println!("okay");
33}
35}
34
36
35−pub fn assets_from_cache_or_binary() -> HighlightingAssets {
37+pub fn assets_from_cache_or_binary() -> Result<HighlightingAssets> {
36− HighlightingAssets::from_cache(&theme_set_path(), &syntax_set_path())
38+ let cache_dir = PROJECT_DIRS.cache_dir();
39+ if let Some(metadata) = AssetsMetadata::load_from_folder(&cache_dir)? {
40+ if !metadata.is_compatible() {
41+ return Err(format!(
42+ "The binary caches for the user-customized syntaxes and themes \
43+ in '{}' are not compatible with this version of bat ({}). To solve this, \
44+ either rebuild the cache (bat cache --build) or remove \
45+ the custom syntaxes/themes (bat cache --clear).\n\
46+ For more information, see:\n\n \
47+ https://github.com/sharkdp/bat#adding-new-syntaxes--language-definitions",
48+ cache_dir.to_string_lossy(),
49+ crate_version!()
50+ )
51+ .into());
52+ }
53+ }
54+
37− .unwrap_or(HighlightingAssets::from_binary())
55+ Ok(HighlightingAssets::from_cache(&cache_dir).unwrap_or(HighlightingAssets::from_binary()))
38}
56}
src/bin/bat/main.rs+3−3
@@ -54,7 +54,7 @@ fn run_cache_subcommand(matches: &clap::ArgMatches) -> Result<()> {
54}
54}
55
55
56pub fn list_languages(config: &Config) -> Result<()> {
56pub fn list_languages(config: &Config) -> Result<()> {
57− let assets = assets_from_cache_or_binary();
57+ let assets = assets_from_cache_or_binary()?;
58 let mut languages = assets
58 let mut languages = assets
59 .syntaxes()
59 .syntaxes()
60 .iter()
60 .iter()
@@ -116,7 +116,7 @@ pub fn list_languages(config: &Config) -> Result<()> {
116}
116}
117
117
118pub fn list_themes(cfg: &Config) -> Result<()> {
118pub fn list_themes(cfg: &Config) -> Result<()> {
119− let assets = assets_from_cache_or_binary();
119+ let assets = assets_from_cache_or_binary()?;
120 let mut config = cfg.clone();
120 let mut config = cfg.clone();
121 let mut style = HashSet::new();
121 let mut style = HashSet::new();
122 style.insert(StyleComponent::Plain);
122 style.insert(StyleComponent::Plain);
@@ -147,7 +147,7 @@ pub fn list_themes(cfg: &Config) -> Result<()> {
147}
147}
148
148
149fn run_controller(config: &Config) -> Result<bool> {
149fn run_controller(config: &Config) -> Result<bool> {
150− let assets = assets_from_cache_or_binary();
150+ let assets = assets_from_cache_or_binary()?;
151 let controller = Controller::new(&config, &assets);
151 let controller = Controller::new(&config, &assets);
152 controller.run()
152 controller.run()
153}
153}
src/errors.rs+10−1
@@ -7,18 +7,27 @@ error_chain! {
7 SyntectError(::syntect::LoadingError);
7 SyntectError(::syntect::LoadingError);
8 ParseIntError(::std::num::ParseIntError);
8 ParseIntError(::std::num::ParseIntError);
9 GlobParsingError(::globset::Error);
9 GlobParsingError(::globset::Error);
10+ SerdeYamlError(::serde_yaml::Error);
10 }
11 }
11}
12}
12
13
13pub fn default_error_handler(error: &Error) {
14pub fn default_error_handler(error: &Error) {
15+ use ansi_term::Colour::Red;
16+
14 match error {
17 match error {
15 Error(ErrorKind::Io(ref io_error), _)
18 Error(ErrorKind::Io(ref io_error), _)
16 if io_error.kind() == ::std::io::ErrorKind::BrokenPipe =>
19 if io_error.kind() == ::std::io::ErrorKind::BrokenPipe =>
17 {
20 {
18 ::std::process::exit(0);
21 ::std::process::exit(0);
19 }
22 }
23+ Error(ErrorKind::SerdeYamlError(_), _) => {
24+ eprintln!(
25+ "{}: Error while parsing metadata.yaml file: {}",
26+ Red.paint("[bat error]"),
27+ error
28+ );
29+ }
20 _ => {
30 _ => {
21− use ansi_term::Colour::Red;
22 eprintln!("{}: {}", Red.paint("[bat error]"), error);
31 eprintln!("{}: {}", Red.paint("[bat error]"), error);
23 }
32 }
24 };
33 };
src/lib.rs+2
@@ -2,6 +2,7 @@
2#![recursion_limit = "1024"]
2#![recursion_limit = "1024"]
3
3
4pub(crate) mod assets;
4pub(crate) mod assets;
5+pub(crate) mod assets_metadata;
5pub mod config;
6pub mod config;
6pub(crate) mod controller;
7pub(crate) mod controller;
7mod decorations;
8mod decorations;
@@ -19,5 +20,6 @@ mod terminal;
19pub(crate) mod wrap;
20pub(crate) mod wrap;
20
21
21pub use assets::HighlightingAssets;
22pub use assets::HighlightingAssets;
23+pub use assets_metadata::AssetsMetadata;
22pub use controller::Controller;
24pub use controller::Controller;
23pub use printer::{InteractivePrinter, Printer, SimplePrinter};
25pub use printer::{InteractivePrinter, Printer, SimplePrinter};