From 0f83f5dcc71489428d3f2afa44cf7d29b9438289 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Fri, 11 Jun 2021 12:38:12 +0200 Subject: Iterator adaptor for package printing mechanic This implements a new interface for printing packages from an iterator. It can be used to implement parallel print-preparation. The new interface is used in the command implementation of the "find-pkg" subcommand. Signed-off-by: Matthias Beyer Tested-by: Matthias Beyer --- src/commands/find_pkg.rs | 16 ++- src/ui/package.rs | 270 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 285 insertions(+), 1 deletion(-) create mode 100644 src/ui/package.rs diff --git a/src/commands/find_pkg.rs b/src/commands/find_pkg.rs index d45c946..8982911 100644 --- a/src/commands/find_pkg.rs +++ b/src/commands/find_pkg.rs @@ -16,10 +16,13 @@ use anyhow::Context; use anyhow::Result; use clap::ArgMatches; use log::trace; +use futures::stream::StreamExt; +use futures::stream::TryStreamExt; use crate::config::Configuration; use crate::package::PackageVersionConstraint; use crate::repository::Repository; +use crate::ui::*; /// Implementation of the "find_pkg" subcommand pub async fn find_pkg( @@ -85,6 +88,17 @@ pub async fn find_pkg( }; let format = config.package_print_format(); - crate::ui::print_packages(&mut outlock, format, iter, config, &flags) + let hb = crate::ui::handlebars_for_package_printing(format)?; + + tokio_stream::iter({ + iter.enumerate() + .map(|(i, p)| p.prepare_print(config, &flags, &hb, i)) + }) + .map(|pp| pp.into_displayable()) + .try_for_each(|p| { + let r = writeln!(&mut outlock, "{}", p).map_err(anyhow::Error::from); + futures::future::ready(r) + }) + .await } } diff --git a/src/ui/package.rs b/src/ui/package.rs new file mode 100644 index 0000000..7794b05 --- /dev/null +++ b/src/ui/package.rs @@ -0,0 +1,270 @@ +use std::borrow::Borrow; +use std::collections::BTreeMap; +use std::io::Write; + +use anyhow::anyhow; +use anyhow::Context; +use anyhow::Error; +use anyhow::Result; +use handlebars::Handlebars; + +use crate::config::Configuration; +use crate::package::Package; +use crate::package::ScriptBuilder; +use crate::package::Shebang; + +pub struct PackagePrintFlags { + pub print_all: bool, + pub print_runtime_deps: bool, + pub print_build_deps: bool, + pub print_sources: bool, + pub print_dependencies: bool, + pub print_patches: bool, + pub print_env: bool, + pub print_flags: bool, + pub print_allowed_images: bool, + pub print_denied_images: bool, + pub print_phases: bool, + pub print_script: bool, + pub script_line_numbers: bool, + pub script_highlighting: bool, +} + +impl PackagePrintFlags { + // Helper to check whether any of the CLI args requested one of these flags. + // + // The print_build_deps and print_runtime_deps as well as the script_highlighting and + // script_line_numbers is not included, because these only modify what to print and not whether + // to print. + fn print_any(&self) -> bool { + self.print_all || { + self.print_sources + || self.print_dependencies + || self.print_patches + || self.print_env + || self.print_flags + || self.print_allowed_images + || self.print_denied_images + || self.print_phases + || self.print_script + } + } +} + + +pub trait PreparePrintable<'a> + where Self: Borrow + Sized +{ + fn prepare_print(self, config: &'a Configuration, flags: &'a PackagePrintFlags, handlebars: &'a Handlebars<'a>, i: usize) -> PreparePrintPackage<'a, Self>; +} + +impl<'a, P> PreparePrintable<'a> for P + where P: Borrow +{ + fn prepare_print(self, config: &'a Configuration, flags: &'a PackagePrintFlags, handlebars: &'a Handlebars<'a>, i: usize) -> PreparePrintPackage<'a, P> { + PreparePrintPackage { + package: self, + config, + flags, + handlebars, + i, + } + } +} + +pub struct PreparePrintPackage<'a, P: Borrow> { + package: P, + config: &'a Configuration, + flags: &'a PackagePrintFlags, + handlebars: &'a Handlebars<'a>, + i: usize, +} + + +pub fn handlebars_for_package_printing(format: &str) -> Result { + let mut hb = Handlebars::new(); + hb.register_escape_fn(handlebars::no_escape); + hb.register_template_string("package", format)?; + Ok(hb) +} + +impl<'a, P: Borrow> PreparePrintPackage<'a, P> { + pub fn into_displayable(self) -> Result { + let script = ScriptBuilder::new(&Shebang::from(self.config.shebang().clone())).build( + self.package.borrow(), + self.config.available_phases(), + *self.config.strict_script_interpolation(), + ).context("Rendering script for printing it failed")?; + + let script = crate::ui::script_to_printable( + &script, + self.flags.script_highlighting, + self.config + .script_highlight_theme() + .as_ref() + .ok_or_else(|| anyhow!("Highlighting for script enabled, but no theme configured"))?, + self.flags.script_line_numbers, + )?; + + let mut data = BTreeMap::new(); + data.insert("i", serde_json::Value::Number(serde_json::Number::from(self.i))); + data.insert("p", serde_json::to_value(self.package.borrow())?); + data.insert("script", serde_json::Value::String(script)); + data.insert("print_any", serde_json::Value::Bool(self.flags.print_any())); + data.insert( + "print_runtime_deps", + serde_json::Value::Bool(self.flags.print_runtime_deps), + ); + data.insert( + "print_build_deps", + serde_json::Value::Bool(self.flags.print_build_deps), + ); + + data.insert( + "print_sources", + serde_json::Value::Bool(self.flags.print_all || self.flags.print_sources), + ); + data.insert( + "print_dependencies", + serde_json::Value::Bool(self.flags.print_all || self.flags.print_dependencies), + ); + data.insert( + "print_patches", + serde_json::Value::Bool(self.flags.print_all || self.flags.print_patches), + ); + data.insert( + "print_env", + serde_json::Value::Bool(self.flags.print_all || self.flags.print_env), + ); + data.insert( + "print_flags", + serde_json::Value::Bool(self.flags.print_all || self.flags.print_flags), + ); + data.insert( + "print_allowed_images", + serde_json::Value::Bool(self.flags.print_all || self.flags.print_allowed_images), + ); + data.insert( + "print_denied_images", + serde_json::Value::Bool(self.flags.print_all || self.flags.print_denied_images), + ); + data.insert( + "print_phases", + serde_json::Value::Bool(self.flags.print_all || self.flags.print_phases), + ); + data.insert( + "print_script", + serde_json::Value::Bool(self.flags.print_all || self.flags.print_script), + ); + + let string = self.handlebars.render("package", &data)?; + Ok(PrintablePackage { string }) + } +} + +pub struct PrintablePackage { string: String } + +impl std::fmt::Display for PrintablePackage { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.string) + } +} + + +pub fn print_packages<'a, I>( + out: &mut dyn Write, + format: &str, + iter: I, + config: &Configuration, + flags: &PackagePrintFlags, +) -> Result<()> +where + I: Iterator, +{ + let mut hb = Handlebars::new(); + hb.register_escape_fn(handlebars::no_escape); + hb.register_template_string("package", format)?; + + iter.enumerate() + .try_for_each(|(i, package)| print_package(out, &hb, i, package, config, flags)) +} + +fn print_package( + out: &mut dyn Write, + hb: &Handlebars, + i: usize, + package: &Package, + config: &Configuration, + flags: &PackagePrintFlags, +) -> Result<()> { + let script = ScriptBuilder::new(&Shebang::from(config.shebang().clone())).build( + package, + config.available_phases(), + *config.strict_script_interpolation(), + ).context("Rendering script for printing it failed")?; + + let script = crate::ui::script_to_printable( + &script, + flags.script_highlighting, + config + .script_highlight_theme() + .as_ref() + .ok_or_else(|| anyhow!("Highlighting for script enabled, but no theme configured"))?, + flags.script_line_numbers, + )?; + + let mut data = BTreeMap::new(); + data.insert("i", serde_json::Value::Number(serde_json::Number::from(i))); + data.insert("p", serde_json::to_value(package)?); + data.insert("script", serde_json::Value::String(script)); + data.insert("print_any", serde_json::Value::Bool(flags.print_any())); + data.insert( + "print_runtime_deps", + serde_json::Value::Bool(flags.print_runtime_deps), + ); + data.insert( + "print_build_deps", + serde_json::Value::Bool(flags.print_build_deps), + ); + + data.insert( + "print_sources", + serde_json::Value::Bool(flags.print_all || flags.print_sources), + ); + data.insert( + "print_dependencies", + serde_json::Value::Bool(flags.print_all || flags.print_dependencies), + ); + data.insert( + "print_patches", + serde_json::Value::Bool(flags.print_all || flags.print_patches), + ); + data.insert( + "print_env", + serde_json::Value::Bool(flags.print_all || flags.print_env), + ); + data.insert( + "print_flags", + serde_json::Value::Bool(flags.print_all || flags.print_flags), + ); + data.insert( + "print_allowed_images", + serde_json::Value::Bool(flags.print_all || flags.print_allowed_images), + ); + data.insert( + "print_denied_images", + serde_json::Value::Bool(flags.print_all || flags.print_denied_images), + ); + data.insert( + "print_phases", + serde_json::Value::Bool(flags.print_all || flags.print_phases), + ); + data.insert( + "print_script", + serde_json::Value::Bool(flags.print_all || flags.print_script), + ); + + hb.render("package", &data) + .map_err(Error::from) + .and_then(|r| writeln!(out, "{}", r).map_err(Error::from)) +} -- cgit v1.2.3