blob: aba505196462e0db3abfc4e6fd3a99168f6c44c6 [file] [log] [blame]
//! Methods for dumping serializable structs to a compressed binary format
//! These are used to load and store the dumps used for fast startup times.
//!
//! Currently syntect serializes `SyntaxSet` structs with `dump_to_file`
//! into `.packdump` files and likewise `ThemeSet` structs to `.themedump` files.
//!
//! You can use these methods to manage your own caching of compiled syntaxes and
//! themes. And even your own `serde::Serialize` structures if you want to
//! be consistent with your format.
use bincode::Result;
#[cfg(any(feature = "dump-load", feature = "dump-load-rs"))]
use bincode::deserialize_from;
#[cfg(any(feature = "dump-create", feature = "dump-create-rs"))]
use bincode::serialize_into;
use std::fs::File;
#[cfg(any(feature = "dump-load", feature = "dump-load-rs"))]
use std::io::{BufReader, Read};
#[cfg(any(feature = "dump-create", feature = "dump-create-rs"))]
use std::io::{BufWriter, Write};
#[cfg(all(feature = "parsing", feature = "assets", any(feature = "dump-load", feature = "dump-load-rs")))]
use parsing::SyntaxSet;
#[cfg(all(feature = "assets", any(feature = "dump-load", feature = "dump-load-rs")))]
use highlighting::ThemeSet;
use std::path::Path;
#[cfg(feature = "dump-create")]
use flate2::write::ZlibEncoder;
#[cfg(any(feature = "dump-load", feature = "dump-load-rs"))]
use flate2::read::ZlibDecoder;
#[cfg(any(feature = "dump-create", feature = "dump-create-rs"))]
use flate2::Compression;
#[cfg(any(feature = "dump-create", feature = "dump-create-rs"))]
use serde::Serialize;
#[cfg(any(feature = "dump-load", feature = "dump-load-rs"))]
use serde::de::DeserializeOwned;
#[cfg(any(feature = "dump-create", feature = "dump-create-rs"))]
pub fn dump_to_writer<T: Serialize, W: Write>(to_dump: &T, output: W) -> Result<()> {
let mut encoder = ZlibEncoder::new(output, Compression::best());
serialize_into(&mut encoder, to_dump)
}
/// Dumps an object to a binary array in the same format as `dump_to_file`
#[cfg(any(feature = "dump-create", feature = "dump-create-rs"))]
pub fn dump_binary<T: Serialize>(o: &T) -> Vec<u8> {
let mut v = Vec::new();
dump_to_writer(o, &mut v).unwrap();
v
}
/// Dumps an encodable object to a file at a given path. If a file already exists at that path
/// it will be overwritten. The files created are encoded with the `bincode` crate and then
/// compressed with the `flate2` crate.
#[cfg(any(feature = "dump-create", feature = "dump-create-rs"))]
pub fn dump_to_file<T: Serialize, P: AsRef<Path>>(o: &T, path: P) -> Result<()> {
let out = BufWriter::new(File::create(path)?);
dump_to_writer(o, out)
}
#[cfg(any(feature = "dump-load", feature = "dump-load-rs"))]
pub fn from_reader<T: DeserializeOwned, R: Read>(input: R) -> Result<T> {
let mut decoder = ZlibDecoder::new(input);
deserialize_from(&mut decoder)
}
/// Returns a fully loaded syntax set from
/// a binary dump. Panics if the dump is invalid.
#[cfg(any(feature = "dump-load", feature = "dump-load-rs"))]
pub fn from_binary<T: DeserializeOwned>(v: &[u8]) -> T {
from_reader(v).unwrap()
}
/// Returns a fully loaded syntax set from a binary dump file.
#[cfg(any(feature = "dump-load", feature = "dump-load-rs"))]
pub fn from_dump_file<T: DeserializeOwned, P: AsRef<Path>>(path: P) -> Result<T> {
let f = File::open(path)?;
let reader = BufReader::new(f);
from_reader(reader)
}
#[cfg(all(feature = "parsing", feature = "assets", any(feature = "dump-load", feature = "dump-load-rs")))]
impl SyntaxSet {
/// Instantiates a new syntax set from a binary dump of
/// Sublime Text's default open source syntax definitions and then links it.
/// These dumps are included in this library's binary for convenience.
///
/// This method loads the version for parsing line strings with no `\n` characters at the end.
/// If you're able to efficiently include newlines at the end of strings, use `load_defaults_newlines`
/// since it works better. See `SyntaxSet#load_syntaxes` for more info on this issue.
///
/// This is the recommended way of creating a syntax set for
/// non-advanced use cases. It is also significantly faster than loading the YAML files.
///
/// Note that you can load additional syntaxes after doing this,
/// you'll just have to link again. If you want you can even
/// use the fact that SyntaxDefinitions are serializable with
/// the bincode crate to cache dumps of additional syntaxes yourself.
pub fn load_defaults_nonewlines() -> SyntaxSet {
let mut ps: SyntaxSet = from_binary(include_bytes!("../assets/default_nonewlines.\
packdump"));
ps.link_syntaxes();
ps
}
/// Same as `load_defaults_nonewlines` but for parsing line strings with newlines at the end.
/// These are separate methods because thanks to linker garbage collection, only the serialized
/// dumps for the method(s) you call will be included in the binary (each is ~200kb for now).
pub fn load_defaults_newlines() -> SyntaxSet {
let mut ps: SyntaxSet = from_binary(include_bytes!("../assets/default_newlines.packdump"));
ps.link_syntaxes();
ps
}
}
#[cfg(all(feature = "assets", any(feature = "dump-load", feature = "dump-load-rs")))]
impl ThemeSet {
/// Loads the set of default themes
/// Currently includes (these are the keys for the map):
///
/// - `base16-ocean.dark`,`base16-eighties.dark`,`base16-mocha.dark`,`base16-ocean.light`
/// - `InspiredGitHub` from [here](https://github.com/sethlopezme/InspiredGitHub.tmtheme)
/// - `Solarized (dark)` and `Solarized (light)`
pub fn load_defaults() -> ThemeSet {
from_binary(include_bytes!("../assets/default.themedump"))
}
}
#[cfg(test)]
mod tests {
#[cfg(all(feature = "yaml-load", any(feature = "dump-create", feature = "dump-create-rs"), any(feature = "dump-load", feature = "dump-load-rs")))]
#[test]
fn can_dump_and_load() {
use super::*;
use parsing::SyntaxSet;
let mut ps = SyntaxSet::new();
ps.load_syntaxes("testdata/Packages", false).unwrap();
let bin = dump_binary(&ps);
println!("{:?}", bin.len());
let ps2: SyntaxSet = from_binary(&bin[..]);
assert_eq!(ps.syntaxes().len(), ps2.syntaxes().len());
}
#[cfg(all(feature = "yaml-load", any(feature = "dump-create", feature = "dump-create-rs"), any(feature = "dump-load", feature = "dump-load-rs")))]
#[test]
fn dump_is_deterministic() {
use super::*;
use parsing::SyntaxSet;
let mut ps1 = SyntaxSet::new();
ps1.load_syntaxes("testdata/Packages", false).unwrap();
let bin1 = dump_binary(&ps1);
let mut ps2 = SyntaxSet::new();
ps2.load_syntaxes("testdata/Packages", false).unwrap();
let bin2 = dump_binary(&ps2);
assert_eq!(bin1, bin2);
}
#[cfg(all(feature = "assets", any(feature = "dump-load", feature = "dump-load-rs")))]
#[test]
fn has_default_themes() {
use highlighting::ThemeSet;
let themes = ThemeSet::load_defaults();
assert!(themes.themes.len() > 4);
}
}