Repository: sharkdp/bat
File: src/assets.rs (Rust)

Code before:
    fn get_first_line_syntax(&self, reader: &mut InputReader) -> Option<&SyntaxReference> {
        String::from_utf8(reader.first_line.clone())
            .ok()
            .and_then(|l| self.get_syntax_set().find_syntax_by_first_line(&l))
    }
}

/// A SyntaxSet in serialized form, i.e. bincoded and flate2 compressed.
/// We keep it in this format since we want to load it lazily.
#[derive(Debug)]
enum SerializedSyntaxSet {
    /// The data comes from a user-generated cache file.
    FromFile(PathBuf),

    /// The data to use is embedded into the bat binary.
    FromBinary(&'static [u8]),
}

impl SerializedSyntaxSet {
    fn deserialize(&self) -> SyntaxSet {
        match self {
            SerializedSyntaxSet::FromBinary(data) => {
                from_binary(data)
            },
            SerializedSyntaxSet::FromFile(ref path) => {
                asset_from_cache(&path, "syntax set").expect("cache corrupt, consider rebuilding or clearing, see https://github.com/sharkdp/bat#adding-new-syntaxes--language-definitions on how")
            },
        }
    }
}

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"))
}

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(|| {
        format!(
            "Could not save {} to {}",
            description,
            path.to_string_lossy()
        )

Review comment (bug):
One downside of lazy loading is that we can't fail "nicely" early on. We could return a Result here and propagate it onwards, but it would cascade into very unpleasant code since then get_syntax_set() would also need to return a Result. It is enough of an edge case for this to fail to justify .expect() here, IMHO.

Code after:
        if syntax.is_none() {
            syntax = self.find_syntax_by_file_name_extension(file_name)?;
        }
        if syntax.is_none() {
            syntax = self.get_extension_syntax_with_stripped_suffix(file_name)?;
        }
        Ok(syntax)
    }

    fn find_syntax_by_file_name(&self, file_name: &OsStr) -> Result<Option<&SyntaxReference>> {
        Ok(self
            .get_syntax_set()?
            .find_syntax_by_extension(file_name.to_str().unwrap_or_default()))
    }

    fn find_syntax_by_file_name_extension(
        &self,
        file_name: &OsStr,
    ) -> Result<Option<&SyntaxReference>> {
        let file_path = Path::new(file_name);
        Ok(self.get_syntax_set()?.find_syntax_by_extension(
            file_path
                .extension()
                .and_then(|x| x.to_str())
                .unwrap_or_default(),
        ))
    }

    /// If we find an ignored suffix on the file name, e.g. '~', we strip it and
    /// then try again to find a syntax without it. Note that we do this recursively.
    fn get_extension_syntax_with_stripped_suffix(
        &self,
        file_name: &OsStr,
    ) -> Result<Option<&SyntaxReference>> {
        let file_path = Path::new(file_name);
        let mut syntax = None;
        if let Some(file_str) = file_path.to_str() {
            for suffix in IGNORED_SUFFIXES.iter() {
                if let Some(stripped_filename) = file_str.strip_suffix(suffix) {
                    syntax = self.get_extension_syntax(OsStr::new(stripped_filename))?;
                    break;
                }
            }
        }
        Ok(syntax)
    }

    fn get_first_line_syntax(&self, reader: &mut InputReader) -> Result<Option<&SyntaxReference>> {
        let syntax_set = self.get_syntax_set()?;
        Ok(String::from_utf8(reader.first_line.clone())
            .ok()