Files
bat/src/assets/build_assets.rs
T
Mathjk a9b112b6fe Exit quietly on broken pipe in auxiliary output paths
The main output path has exited with status 0 on EPIPE since #232, but
auxiliary outputs still used println!/print!/io::stdout().flush().unwrap()
and panicked (exit code 101) when the reader of a piped stdout went away
early, e.g. 'bat --completion bash | head -n0'.

Route them through the existing convention instead: write!/writeln! to
io::stdout() and '?' so that Error::Io(BrokenPipe) reaches
default_error_handler, which exits with status 0. This covers
--completion, --config-file, --diagnostic (bugreport's print() becomes
format() + writeln!), --generate-config-file, the 'cache' subcommand
progress messages, the OSC title sequence of --set-terminal-title, and
the 'built without feature' notices.

Closes #4034
2026-10-02 02:23:08 +08:00

181 lines
4.9 KiB
Rust

use std::convert::TryInto;
use std::io::{self, Write};
use std::path::Path;
use syntect::highlighting::ThemeSet;
use syntect::parsing::{SyntaxSet, SyntaxSetBuilder};
use crate::assets::*;
use acknowledgements::build_acknowledgements;
mod acknowledgements;
pub fn build(
source_dir: &Path,
include_integrated_assets: bool,
include_acknowledgements: bool,
target_dir: &Path,
current_version: &str,
) -> Result<()> {
let theme_set = build_theme_set(source_dir, include_integrated_assets)?;
let syntax_set_builder = build_syntax_set_builder(source_dir, include_integrated_assets)?;
let syntax_set = syntax_set_builder.build();
let acknowledgements = build_acknowledgements(source_dir, include_acknowledgements)?;
print_unlinked_contexts(&syntax_set)?;
write_assets(
&theme_set,
&syntax_set,
&acknowledgements,
target_dir,
current_version,
)
}
fn build_theme_set(source_dir: &Path, include_integrated_assets: bool) -> Result<LazyThemeSet> {
let mut theme_set = if include_integrated_assets {
crate::assets::get_integrated_themeset().try_into()?
} else {
ThemeSet::new()
};
let theme_dir = source_dir.join("themes");
if theme_dir.exists() {
let res = theme_set.add_from_folder(&theme_dir);
if let Err(err) = res {
writeln!(
io::stdout(),
"Failed to load one or more themes from '{}' (reason: '{err}')",
theme_dir.to_string_lossy(),
)?;
}
} else {
writeln!(
io::stdout(),
"No themes were found in '{}', using the default set",
theme_dir.to_string_lossy()
)?;
}
theme_set.try_into()
}
fn build_syntax_set_builder(
source_dir: &Path,
include_integrated_assets: bool,
) -> Result<SyntaxSetBuilder> {
let mut syntax_set_builder = if !include_integrated_assets {
let mut builder = syntect::parsing::SyntaxSetBuilder::new();
builder.add_plain_text_syntax();
builder
} else {
from_binary::<SyntaxSet>(get_serialized_integrated_syntaxset(), COMPRESS_SYNTAXES)
.into_builder()
};
let syntax_dir = source_dir.join("syntaxes");
if syntax_dir.exists() {
syntax_set_builder.add_from_folder(syntax_dir, true)?;
} else {
writeln!(
io::stdout(),
"No syntaxes were found in '{}', using the default set.",
syntax_dir.to_string_lossy()
)?;
}
Ok(syntax_set_builder)
}
fn print_unlinked_contexts(syntax_set: &SyntaxSet) -> Result<()> {
let missing_contexts = syntax_set.find_unlinked_contexts();
if !missing_contexts.is_empty() {
writeln!(io::stdout(), "Some referenced contexts could not be found!")?;
for context in missing_contexts {
writeln!(io::stdout(), "- {context}")?;
}
}
Ok(())
}
fn write_assets(
theme_set: &LazyThemeSet,
syntax_set: &SyntaxSet,
acknowledgements: &Option<String>,
target_dir: &Path,
current_version: &str,
) -> Result<()> {
let _ = std::fs::create_dir_all(target_dir);
asset_to_cache(
theme_set,
&target_dir.join("themes.bin"),
"theme set",
COMPRESS_THEMES,
)?;
asset_to_cache(
syntax_set,
&target_dir.join("syntaxes.bin"),
"syntax set",
COMPRESS_SYNTAXES,
)?;
if let Some(acknowledgements) = acknowledgements {
asset_to_cache(
acknowledgements,
&target_dir.join("acknowledgements.bin"),
"acknowledgements",
COMPRESS_ACKNOWLEDGEMENTS,
)?;
}
write!(
io::stdout(),
"Writing metadata to folder {} ... ",
target_dir.to_string_lossy()
)?;
crate::assets_metadata::AssetsMetadata::new(current_version).save_to_folder(target_dir)?;
writeln!(io::stdout(), "okay")?;
Ok(())
}
pub(crate) fn asset_to_contents<T: serde::Serialize>(
asset: &T,
description: &str,
compressed: bool,
) -> Result<Vec<u8>> {
let mut contents = vec![];
if compressed {
bincode::serialize_into(
flate2::write::ZlibEncoder::new(&mut contents, flate2::Compression::best()),
asset,
)
} else {
bincode::serialize_into(&mut contents, asset)
}
.map_err(|_| format!("Could not serialize {description}"))?;
Ok(contents)
}
fn asset_to_cache<T: serde::Serialize>(
asset: &T,
path: &Path,
description: &str,
compressed: bool,
) -> Result<()> {
write!(
io::stdout(),
"Writing {description} to {} ... ",
path.to_string_lossy()
)?;
let contents = asset_to_contents(asset, description, compressed)?;
std::fs::write(path, &contents[..])
.map_err(|_| format!("Could not save {description} to {}", path.to_string_lossy()))?;
writeln!(io::stdout(), "okay")?;
Ok(())
}