feat: update dependencies and remove binary and command meta plugins

This commit is contained in:
Andrew Phillips
2025-08-28 13:11:19 -03:00
committed by Andrew Phillips (aider)
parent 40e4fcc74a
commit 52dc8cea32
5 changed files with 78 additions and 399 deletions

52
Cargo.lock generated
View File

@@ -403,7 +403,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68578f196d2a33ff61b27fae256c3164f65e36382648e30666dde05b8cc9dfdf" checksum = "68578f196d2a33ff61b27fae256c3164f65e36382648e30666dde05b8cc9dfdf"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"convert_case", "convert_case 0.6.0",
"json5", "json5",
"nom", "nom",
"pathdiff", "pathdiff",
@@ -444,6 +444,15 @@ dependencies = [
"unicode-segmentation", "unicode-segmentation",
] ]
[[package]]
name = "convert_case"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7"
dependencies = [
"unicode-segmentation",
]
[[package]] [[package]]
name = "core-foundation-sys" name = "core-foundation-sys"
version = "0.8.7" version = "0.8.7"
@@ -561,6 +570,28 @@ dependencies = [
"syn 2.0.105", "syn 2.0.105",
] ]
[[package]]
name = "derive_more"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678"
dependencies = [
"derive_more-impl",
]
[[package]]
name = "derive_more-impl"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
dependencies = [
"convert_case 0.7.1",
"proc-macro2",
"quote",
"syn 2.0.105",
"unicode-xid",
]
[[package]] [[package]]
name = "digest" name = "digest"
version = "0.9.0" version = "0.9.0"
@@ -1331,6 +1362,7 @@ dependencies = [
"chrono", "chrono",
"clap", "clap",
"config", "config",
"derive_more",
"directories", "directories",
"dns-lookup", "dns-lookup",
"enum-map", "enum-map",
@@ -1361,6 +1393,7 @@ dependencies = [
"serde_json", "serde_json",
"serde_yaml", "serde_yaml",
"sha2 0.10.9", "sha2 0.10.9",
"smart-default",
"stderrlog", "stderrlog",
"strum", "strum",
"strum_macros", "strum_macros",
@@ -2290,6 +2323,17 @@ version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "smart-default"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eb01866308440fc64d6c44d9e86c5cc17adfe33c4d6eed55da9145044d0ffc1"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.105",
]
[[package]] [[package]]
name = "socket2" name = "socket2"
version = "0.6.0" version = "0.6.0"
@@ -2741,6 +2785,12 @@ version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
[[package]]
name = "unicode-xid"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]] [[package]]
name = "unsafe-libyaml" name = "unsafe-libyaml"
version = "0.2.11" version = "0.2.11"

View File

@@ -1,149 +0,0 @@
use crate::common::is_binary::is_binary;
use crate::common::PIPESIZE;
use crate::meta_plugin::{MetaPlugin, MetaPluginResponse, MetaPluginType};
#[derive(Debug, Clone, Default)]
pub struct BinaryMetaPlugin {
buffer: Vec<u8>,
max_buffer_size: usize,
is_finalized: bool,
base: crate::meta_plugin::BaseMetaPlugin,
}
impl BinaryMetaPlugin {
pub fn new(
options: Option<std::collections::HashMap<String, serde_yaml::Value>>,
outputs: Option<std::collections::HashMap<String, serde_yaml::Value>>,
) -> BinaryMetaPlugin {
let mut base = crate::meta_plugin::BaseMetaPlugin::new();
// Initialize with helper function
base.initialize_plugin(
&["binary"],
options,
outputs,
);
let max_buffer_size = base.options.get("max_buffer_size")
.and_then(|v| v.as_u64())
.unwrap_or(PIPESIZE as u64) as usize;
BinaryMetaPlugin {
buffer: Vec::new(),
max_buffer_size,
is_finalized: false,
base,
}
}
}
impl MetaPlugin for BinaryMetaPlugin {
fn is_finalized(&self) -> bool {
self.is_finalized
}
fn set_finalized(&mut self, finalized: bool) {
self.is_finalized = finalized;
}
fn update(&mut self, data: &[u8]) -> MetaPluginResponse {
// If already finalized, don't process more data
if self.is_finalized {
return MetaPluginResponse {
metadata: Vec::new(),
is_finalized: true,
};
}
// Calculate how much data we can still accept
let remaining_capacity = self.max_buffer_size.saturating_sub(self.buffer.len());
if remaining_capacity > 0 {
// Determine how much data to copy
let bytes_to_take = std::cmp::min(data.len(), remaining_capacity);
// Add data to our buffer
self.buffer.extend_from_slice(&data[..bytes_to_take]);
}
// If we've reached our buffer limit, return metadata
let mut metadata = Vec::new();
if self.buffer.len() >= self.max_buffer_size {
let is_binary_result = is_binary(&self.buffer);
let value = if is_binary_result { "true".to_string() } else { "false".to_string() };
// Use process_metadata_outputs to handle output mapping
if let Some(meta_data) = crate::meta_plugin::process_metadata_outputs(
"binary",
serde_yaml::Value::String(value),
self.base.outputs()
) {
metadata.push(meta_data);
}
// Mark as finalized
self.is_finalized = true;
}
let is_finalized = !metadata.is_empty();
MetaPluginResponse {
metadata,
is_finalized,
}
}
fn finalize(&mut self) -> MetaPluginResponse {
// If already finalized, don't process again
if self.is_finalized {
return MetaPluginResponse {
metadata: Vec::new(),
is_finalized: true,
};
}
let mut metadata = Vec::new();
// Save the binary detection result when finalizing
let is_binary_result = is_binary(&self.buffer);
let value = if is_binary_result { "true".to_string() } else { "false".to_string() };
// Use process_metadata_outputs to handle output mapping
if let Some(meta_data) = crate::meta_plugin::process_metadata_outputs(
"binary",
serde_yaml::Value::String(value),
self.base.outputs()
) {
metadata.push(meta_data);
}
// Mark as finalized
self.is_finalized = true;
MetaPluginResponse {
metadata,
is_finalized: true,
}
}
fn meta_type(&self) -> MetaPluginType {
MetaPluginType::Binary
}
fn outputs(&self) -> &std::collections::HashMap<String, serde_yaml::Value> {
self.base.outputs()
}
fn outputs_mut(&mut self) -> &mut std::collections::HashMap<String, serde_yaml::Value> {
self.base.outputs_mut()
}
fn options(&self) -> &std::collections::HashMap<String, serde_yaml::Value> {
self.base.options()
}
fn options_mut(&mut self) -> &mut std::collections::HashMap<String, serde_yaml::Value> {
self.base.options_mut()
}
}

View File

@@ -1,227 +0,0 @@
use log::*;
use std::io::Write;
use std::process::{Command, Stdio, Child};
use which::which;
use crate::meta_plugin::{MetaPlugin, MetaPluginResponse, MetaPluginType};
pub struct MetaPluginExec {
pub program: String,
pub args: Vec<String>,
pub supported: bool,
pub split_whitespace: bool,
process: Option<Child>,
writer: Option<Box<dyn Write>>,
result: Option<String>,
outputs: std::collections::HashMap<String, serde_yaml::Value>,
options: std::collections::HashMap<String, serde_yaml::Value>,
}
// Manual Debug implementation because Box<dyn Write> doesn't implement Debug
impl std::fmt::Debug for MetaPluginExec {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("MetaPluginExec")
.field("program", &self.program)
.field("args", &self.args)
.field("supported", &self.supported)
.field("split_whitespace", &self.split_whitespace)
.field("process", &self.process)
.field("writer", &self.writer.as_ref().map(|_| "Box<dyn Write>"))
.field("result", &self.result)
.field("outputs", &self.outputs)
.field("options", &self.options)
.finish()
}
}
impl MetaPluginExec {
pub fn new(
program: &str,
args: Vec<&str>,
meta_name: String,
split_whitespace: bool,
_options: Option<std::collections::HashMap<String, serde_yaml::Value>>,
outputs: Option<std::collections::HashMap<String, serde_yaml::Value>>,
) -> MetaPluginExec {
let program_path = which(program);
let supported = program_path.is_ok();
// Start with default outputs
let mut final_outputs = std::collections::HashMap::new();
final_outputs.insert(meta_name.clone(), serde_yaml::Value::String(meta_name.clone()));
if let Some(outs) = outputs {
for (key, value) in outs {
final_outputs.insert(key, value);
}
}
// Start with default options
let mut final_options = std::collections::HashMap::new();
if let Some(opts) = _options {
for (key, value) in opts {
final_options.insert(key, value);
}
}
MetaPluginExec {
program: program_path.map_or_else(|_| program.to_string(), |p| p.to_string_lossy().to_string()),
args: args.iter().map(|s| s.to_string()).collect(),
supported,
split_whitespace,
process: None,
writer: None,
result: None,
outputs: final_outputs,
options: final_options,
}
}
}
impl MetaPlugin for MetaPluginExec {
fn is_supported(&self) -> bool {
self.supported
}
fn is_internal(&self) -> bool {
false
}
fn initialize(&mut self) -> MetaPluginResponse {
debug!("META: Initializing program plugin: {:?}", self);
let program = self.program.clone();
let args = self.args.clone();
debug!("META: Executing command: {:?} {:?}", program, args);
let mut process = match Command::new(program.clone())
.args(args.clone())
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
{
Ok(process) => process,
Err(e) => {
debug!("META: Failed to spawn process: {}", e);
return MetaPluginResponse {
metadata: Vec::new(),
is_finalized: true,
};
}
};
let stdin = process.stdin.take().unwrap();
self.writer = Some(Box::new(stdin));
self.process = Some(process);
MetaPluginResponse {
metadata: Vec::new(),
is_finalized: false,
}
}
fn finalize(&mut self) -> MetaPluginResponse {
debug!("META: Finalizing program plugin");
let mut metadata = Vec::new();
if let Some(process) = self.process.take() {
// Close stdin to signal end of input
drop(self.writer.take());
// Wait for the process to complete
let output = match process.wait_with_output() {
Ok(output) => output,
Err(e) => {
debug!("META: Failed to get process output: {}", e);
return MetaPluginResponse {
metadata: Vec::new(),
is_finalized: true,
};
}
};
if output.status.success() {
// Process the output
let output_str = String::from_utf8_lossy(&output.stdout);
let result = if self.split_whitespace {
output_str.split_whitespace().next().unwrap_or("").to_string()
} else {
output_str.trim().to_string()
};
if !result.is_empty() {
debug!("META: Program output: {}", result);
self.result = Some(result.clone());
// Use process_metadata_outputs to handle output mapping
if let Some(meta_data) = crate::meta_plugin::process_metadata_outputs(
&self.meta_type().to_string(),
serde_yaml::Value::String(result),
&self.outputs
) {
metadata.push(meta_data);
}
}
} else {
debug!("META: Program failed with status: {:?}", output.status);
let stderr = String::from_utf8_lossy(&output.stderr);
if !stderr.is_empty() {
debug!("META: Program stderr: {}", stderr);
}
}
}
MetaPluginResponse {
metadata,
is_finalized: true,
}
}
fn update(&mut self, data: &[u8]) -> MetaPluginResponse {
if let Some(ref mut writer) = self.writer {
if let Err(e) = writer.write_all(data) {
debug!("META: Failed to write to process stdin: {}", e);
}
}
MetaPluginResponse {
metadata: Vec::new(),
is_finalized: false,
}
}
fn meta_type(&self) -> MetaPluginType {
MetaPluginType::Exec
}
fn program_info(&self) -> Option<(&str, Vec<&str>)> {
if self.supported {
Some((&self.program, self.args.iter().map(|s| s.as_str()).collect()))
} else {
None
}
}
fn outputs(&self) -> &std::collections::HashMap<String, serde_yaml::Value> {
&self.outputs
}
fn outputs_mut(&mut self) -> &mut std::collections::HashMap<String, serde_yaml::Value> {
&mut self.outputs
}
fn default_outputs(&self) -> Vec<String> {
vec![self.meta_type().to_string()]
}
fn options(&self) -> &std::collections::HashMap<String, serde_yaml::Value> {
&self.options
}
fn options_mut(&mut self) -> &mut std::collections::HashMap<String, serde_yaml::Value> {
&mut self.options
}
}

View File

@@ -4,6 +4,7 @@ use std::io::Read;
use std::os::fd::FromRawFd; use std::os::fd::FromRawFd;
use crate::config; use crate::config;
use crate::services::item_service::ItemService; use crate::services::item_service::ItemService;
use log::debug;
fn validate_diff_args(cmd: &mut Command, ids: &Vec<i64>, tags: &Vec<String>) { fn validate_diff_args(cmd: &mut Command, ids: &Vec<i64>, tags: &Vec<String>) {
if !tags.is_empty() { if !tags.is_empty() {
@@ -35,8 +36,8 @@ fn fetch_and_validate_items(
.get_item(conn, ids[1]) .get_item(conn, ids[1])
.with_context(|| format!("Unable to find second item (ID: {}) in database", ids[1]))?; .with_context(|| format!("Unable to find second item (ID: {}) in database", ids[1]))?;
log::debug!("MAIN: Found item A {:?}", item_a.item); debug!("MAIN: Found item A {:?}", item_a.item);
log::debug!("MAIN: Found item B {:?}", item_b.item); debug!("MAIN: Found item B {:?}", item_b.item);
Ok((item_a, item_b)) Ok((item_a, item_b))
} }
@@ -94,7 +95,7 @@ fn spawn_diff_process(
fd_a_read: libc::c_int, fd_a_read: libc::c_int,
fd_b_read: libc::c_int, fd_b_read: libc::c_int,
) -> Result<std::process::Child, anyhow::Error> { ) -> Result<std::process::Child, anyhow::Error> {
log::debug!("MAIN: Creating child process for diff"); debug!("MAIN: Creating child process for diff");
let mut diff_command = std::process::Command::new("diff"); let mut diff_command = std::process::Command::new("diff");
diff_command diff_command
.arg("-u") .arg("-u")
@@ -157,10 +158,10 @@ fn write_item_to_pipe(
let mut reader = engine.open(item_path) let mut reader = engine.open(item_path)
.map_err(|e| anyhow::anyhow!("Failed to open item: {}", e))?; .map_err(|e| anyhow::anyhow!("Failed to open item: {}", e))?;
log::debug!("THREAD: Sending item to diff"); debug!("THREAD: Sending item to diff");
std::io::copy(&mut reader, &mut buffered_pipe_writer) std::io::copy(&mut reader, &mut buffered_pipe_writer)
.map_err(|e| anyhow::anyhow!("Failed to copy item to pipe: {}", e))?; .map_err(|e| anyhow::anyhow!("Failed to copy item to pipe: {}", e))?;
log::debug!("THREAD: Done sending item to diff"); debug!("THREAD: Done sending item to diff");
Ok(()) Ok(())
} }
@@ -188,12 +189,12 @@ fn execute_diff_command(
.take() .take()
.expect("BUG: Failed to capture diff stderr pipe"); .expect("BUG: Failed to capture diff stderr pipe");
log::debug!("MAIN: Creating threads for diff I/O"); debug!("MAIN: Creating threads for diff I/O");
// Thread to read diff's standard output // Thread to read diff's standard output
let stdout_reader_thread = std::thread::spawn(move || { let stdout_reader_thread = std::thread::spawn(move || {
let mut output_buffer = Vec::new(); let mut output_buffer = Vec::new();
log::debug!("STDOUT_READER: Reading diff stdout"); debug!("STDOUT_READER: Reading diff stdout");
// child_stdout_pipe is a ChildStdout, which implements std::io::Read // child_stdout_pipe is a ChildStdout, which implements std::io::Read
child_stdout_pipe child_stdout_pipe
.read_to_end(&mut output_buffer) .read_to_end(&mut output_buffer)
@@ -204,7 +205,7 @@ fn execute_diff_command(
// Thread to read diff's standard error // Thread to read diff's standard error
let stderr_reader_thread = std::thread::spawn(move || { let stderr_reader_thread = std::thread::spawn(move || {
let mut error_buffer = Vec::new(); let mut error_buffer = Vec::new();
log::debug!("STDERR_READER: Reading diff stderr"); debug!("STDERR_READER: Reading diff stderr");
child_stderr_pipe child_stderr_pipe
.read_to_end(&mut error_buffer) .read_to_end(&mut error_buffer)
.map_err(|e| anyhow::anyhow!("Failed to read diff stderr: {}", e)) .map_err(|e| anyhow::anyhow!("Failed to read diff stderr: {}", e))
@@ -238,7 +239,7 @@ fn handle_diff_output(
match diff_status.code() { match diff_status.code() {
Some(0) => { Some(0) => {
// Exit code 0: No differences // Exit code 0: No differences
log::debug!("MAIN: Diff successful, no differences found."); debug!("MAIN: Diff successful, no differences found.");
// Typically, diff -u doesn't print to stdout if no differences. // Typically, diff -u doesn't print to stdout if no differences.
// But if it did, it would be shown here. // But if it did, it would be shown here.
if !stdout_capture_result.is_empty() { if !stdout_capture_result.is_empty() {
@@ -247,7 +248,7 @@ fn handle_diff_output(
} }
Some(1) => { Some(1) => {
// Exit code 1: Differences found // Exit code 1: Differences found
log::debug!("MAIN: Diff successful, differences found."); debug!("MAIN: Diff successful, differences found.");
println!("{}", String::from_utf8_lossy(&stdout_capture_result)); println!("{}", String::from_utf8_lossy(&stdout_capture_result));
} }
Some(error_code) => { Some(error_code) => {
@@ -335,10 +336,10 @@ pub fn mode_diff(
spawn_writer_thread(item_path_b.clone(), compression_type_b.clone(), fd_b_write); spawn_writer_thread(item_path_b.clone(), compression_type_b.clone(), fd_b_write);
// Wait for writer threads to complete (meaning all input has been sent to diff) // Wait for writer threads to complete (meaning all input has been sent to diff)
log::debug!("MAIN: Waiting on writer thread for item A"); debug!("MAIN: Waiting on writer thread for item A");
match writer_thread_a.join() { match writer_thread_a.join() {
Ok(Ok(())) => { Ok(Ok(())) => {
log::debug!("MAIN: Writer thread for item A completed successfully."); debug!("MAIN: Writer thread for item A completed successfully.");
} }
Ok(Err(e)) => { Ok(Err(e)) => {
return Err(anyhow::anyhow!("Writer thread for item A failed: {}", e)); return Err(anyhow::anyhow!("Writer thread for item A failed: {}", e));
@@ -352,10 +353,10 @@ pub fn mode_diff(
} }
} }
log::debug!("MAIN: Waiting on writer thread for item B"); debug!("MAIN: Waiting on writer thread for item B");
match writer_thread_b.join() { match writer_thread_b.join() {
Ok(Ok(())) => { Ok(Ok(())) => {
log::debug!("MAIN: Writer thread for item B completed successfully."); debug!("MAIN: Writer thread for item B completed successfully.");
} }
Ok(Err(e)) => { Ok(Err(e)) => {
return Err(anyhow::anyhow!("Writer thread for item B failed: {}", e)); return Err(anyhow::anyhow!("Writer thread for item B failed: {}", e));
@@ -369,18 +370,18 @@ pub fn mode_diff(
} }
} }
log::debug!("MAIN: Done waiting on input-writer threads."); debug!("MAIN: Done waiting on input-writer threads.");
// Now that all input has been sent, the diff process will run to completion. // Now that all input has been sent, the diff process will run to completion.
// We can read its output. This will block until the process is finished. // We can read its output. This will block until the process is finished.
let (stdout_capture_result, stderr_capture_result) = execute_diff_command(&mut child_process)?; let (stdout_capture_result, stderr_capture_result) = execute_diff_command(&mut child_process)?;
// wait for the diff child process to terminate. // wait for the diff child process to terminate.
log::debug!("MAIN: Waiting for diff child process to finish..."); debug!("MAIN: Waiting for diff child process to finish...");
let diff_status = child_process let diff_status = child_process
.wait() .wait()
.map_err(|e| anyhow::anyhow!("Failed to wait on diff command: {}", e))?; .map_err(|e| anyhow::anyhow!("Failed to wait on diff command: {}", e))?;
log::debug!( debug!(
"MAIN: Diff child process finished with status: {}", "MAIN: Diff child process finished with status: {}",
diff_status diff_status
); );

View File

@@ -2,6 +2,7 @@ use clap::*;
use is_terminal::IsTerminal; use is_terminal::IsTerminal;
use std::path::PathBuf; use std::path::PathBuf;
use std::str::FromStr; use std::str::FromStr;
use log::debug;
use crate::modes::common::{get_format_box_chars_no_border_line_separator, OutputFormat}; use crate::modes::common::{get_format_box_chars_no_border_line_separator, OutputFormat};
use crate::config; use crate::config;
@@ -344,12 +345,12 @@ pub fn mode_status(
data_path: PathBuf, data_path: PathBuf,
db_path: PathBuf, db_path: PathBuf,
) -> Result<(), anyhow::Error> { ) -> Result<(), anyhow::Error> {
log::debug!("STATUS: Starting mode_status function"); debug!("STATUS: Starting mode_status function");
// Determine which meta plugins would be enabled for a save operation // Determine which meta plugins would be enabled for a save operation
log::debug!("STATUS: Getting meta plugin types from settings"); debug!("STATUS: Getting meta plugin types from settings");
let mut meta_plugin_types: Vec<MetaPluginType> = crate::modes::common::settings_meta_plugin_types(_cmd, settings); let mut meta_plugin_types: Vec<MetaPluginType> = crate::modes::common::settings_meta_plugin_types(_cmd, settings);
log::debug!("STATUS: Got {} meta plugin types", meta_plugin_types.len()); debug!("STATUS: Got {} meta plugin types", meta_plugin_types.len());
// Always add the Digest plugin if not present // Always add the Digest plugin if not present
if !meta_plugin_types.contains(&MetaPluginType::Digest) { if !meta_plugin_types.contains(&MetaPluginType::Digest) {
@@ -364,9 +365,9 @@ pub fn mode_status(
}; };
let output_format = crate::modes::common::settings_output_format(settings); let output_format = crate::modes::common::settings_output_format(settings);
log::debug!("STATUS: About to call generate_status_info"); debug!("STATUS: About to call generate_status_info");
let status_info = generate_status_info(data_path, db_path, &meta_plugin_types, enabled_compression_type); let status_info = generate_status_info(data_path, db_path, &meta_plugin_types, enabled_compression_type);
log::debug!("STATUS: generate_status_info completed successfully"); debug!("STATUS: generate_status_info completed successfully");
match output_format { match output_format {
OutputFormat::Table => { OutputFormat::Table => {
@@ -377,11 +378,14 @@ pub fn mode_status(
println!("PATHS:"); println!("PATHS:");
build_path_table(&status_info.paths).printstd(); build_path_table(&status_info.paths).printstd();
println!(); println!();
println!("COMPRESSION:"); println!("COMPRESSION:");
build_compression_table(&status_info.compression).printstd(); build_compression_table(&status_info.compression).printstd();
println!(); println!();
println!("META PLUGINS AVAILABLE:"); println!("META PLUGINS AVAILABLE:");
build_meta_plugin_table(&status_info.meta_plugins).printstd(); build_meta_plugin_table(&status_info.meta_plugins).printstd();
println!();
// Print META PLUGINS CONFIGURED if they exist // Print META PLUGINS CONFIGURED if they exist
if let Some(meta_plugins_table) = build_meta_plugins_configured_table(settings) { if let Some(meta_plugins_table) = build_meta_plugins_configured_table(settings) {