use anyhow::Result;
use serde_json;
use syntect::{
easy::HighlightLines,
highlighting::Style,
util::{LinesWithEndings, as_24_bit_terminal_escaped},
};
use two_face::{syntax, theme};
use crate::{cli::banner, cli::output::*, config::CONFIG};
fn apply_syntax_highlighting(output: &str, format: &str) -> Result<String> {
if !is_terminal::IsTerminal::is_terminal(&std::io::stdout()) {
return Ok(output.to_string());
}
let ps = syntax::extra_newlines();
let ts = theme::extra();
let syntax = match format {
"json" => ps
.find_syntax_by_extension("json")
.unwrap_or_else(|| ps.find_syntax_plain_text()),
"yaml" => ps
.find_syntax_by_extension("yaml")
.unwrap_or_else(|| ps.find_syntax_plain_text()),
"toml" => ps
.find_syntax_by_extension("toml")
.unwrap_or_else(|| ps.find_syntax_plain_text()),
_ => return Ok(output.to_string()),
};
use two_face::theme::EmbeddedThemeName;
let theme = ts.get(EmbeddedThemeName::Base16OceanDark);
let mut h = HighlightLines::new(syntax, theme);
let mut highlighted = String::new();
for line in LinesWithEndings::from(output) {
let ranges: Vec<(Style, &str)> = h.highlight_line(line, &ps)?;
let escaped = as_24_bit_terminal_escaped(&ranges[..], false);
highlighted.push_str(&escaped);
}
highlighted.push_str("\x1b[0m");
Ok(highlighted)
}
pub async fn dump_hooks_config(format: Option<String>) -> Result<()> {
banner::print_banner(None);
let hooks_config = &CONFIG.hooks;
let output_format = format.as_deref().unwrap_or("yaml");
let output = match output_format {
"json" => {
let json_output = serde_json::to_string_pretty(hooks_config)
.map_err(|e| anyhow::anyhow!("Failed to serialize to JSON: {}", e))?;
apply_syntax_highlighting(&json_output, "json")?
}
"yaml" => {
let yaml_output = serde_yaml_bw::to_string(hooks_config)
.map_err(|e| anyhow::anyhow!("Failed to serialize to YAML: {}", e))?;
apply_syntax_highlighting(&yaml_output, "yaml")?
}
"toml" => {
let toml_output = toml::to_string_pretty(hooks_config)
.map_err(|e| anyhow::anyhow!("Failed to serialize to TOML: {}", e))?;
apply_syntax_highlighting(&toml_output, "toml")?
}
_ => {
styled!(
"{} Unsupported format: {}",
("❌", "error_symbol"),
(output_format, "error")
);
styled!("Supported formats: json, yaml, toml");
return Err(anyhow::anyhow!(
"Unsupported output format: {}",
output_format
));
}
};
println!("{output}");
Ok(())
}
pub async fn dump_lefthook_config() -> Result<()> {
banner::print_banner(None);
use std::collections::HashMap;
let hooks_config = &CONFIG.hooks;
let mut lefthook_config: HashMap<String, serde_json::Value> = HashMap::new();
if !hooks_config.pre_commit.commands.is_empty() || !hooks_config.pre_commit.scripts.is_empty() {
let mut hook_config = HashMap::new();
if hooks_config.pre_commit.skip {
hook_config.insert("skip".to_string(), serde_json::Value::Bool(true));
}
if !hooks_config.pre_commit.parallel {
hook_config.insert("parallel".to_string(), serde_json::Value::Bool(false));
}
if !hooks_config.pre_commit.commands.is_empty() {
let commands: HashMap<String, serde_json::Value> = hooks_config
.pre_commit
.commands
.iter()
.map(|(name, cmd)| {
let mut cmd_obj = HashMap::new();
cmd_obj.insert(
"run".to_string(),
serde_json::Value::String(cmd.run.clone()),
);
if !cmd.description.is_empty() {
cmd_obj.insert(
"description".to_string(),
serde_json::Value::String(cmd.description.clone()),
);
}
if cmd.continue_on_error {
cmd_obj.insert(
"fail_text".to_string(),
serde_json::Value::String("continue".to_string()),
);
}
if cmd.all_files {
cmd_obj.insert(
"files".to_string(),
serde_json::Value::String("git ls-files".to_string()),
);
}
if cmd.stage_fixed {
cmd_obj.insert("stage_fixed".to_string(), serde_json::Value::Bool(true));
}
if !cmd.glob.is_empty() {
let glob_value = if cmd.glob.len() == 1 {
serde_json::Value::String(cmd.glob[0].clone())
} else {
serde_json::Value::Array(
cmd.glob
.iter()
.map(|g| serde_json::Value::String(g.clone()))
.collect(),
)
};
cmd_obj.insert("glob".to_string(), glob_value);
}
(
name.clone(),
serde_json::Value::Object(cmd_obj.into_iter().collect()),
)
})
.collect();
hook_config.insert(
"commands".to_string(),
serde_json::Value::Object(commands.into_iter().collect()),
);
}
if !hooks_config.pre_commit.scripts.is_empty() {
let scripts: HashMap<String, serde_json::Value> = hooks_config
.pre_commit
.scripts
.iter()
.map(|(name, script)| {
let mut script_obj = HashMap::new();
script_obj.insert(
"runner".to_string(),
serde_json::Value::String(script.runner.clone()),
);
if !script.env.is_empty() {
let env_obj: HashMap<String, serde_json::Value> = script
.env
.iter()
.map(|(k, v)| (k.clone(), serde_json::Value::String(v.clone())))
.collect();
script_obj.insert(
"env".to_string(),
serde_json::Value::Object(env_obj.into_iter().collect()),
);
}
(
name.clone(),
serde_json::Value::Object(script_obj.into_iter().collect()),
)
})
.collect();
hook_config.insert(
"scripts".to_string(),
serde_json::Value::Object(scripts.into_iter().collect()),
);
}
lefthook_config.insert(
"pre-commit".to_string(),
serde_json::Value::Object(hook_config.into_iter().collect()),
);
}
if !hooks_config.commit_msg.commands.is_empty() || !hooks_config.commit_msg.scripts.is_empty() {
let mut hook_config = HashMap::new();
if hooks_config.commit_msg.skip {
hook_config.insert("skip".to_string(), serde_json::Value::Bool(true));
}
if !hooks_config.commit_msg.commands.is_empty() {
let commands: HashMap<String, serde_json::Value> = hooks_config
.commit_msg
.commands
.iter()
.map(|(name, cmd)| {
let mut cmd_obj = HashMap::new();
cmd_obj.insert(
"run".to_string(),
serde_json::Value::String(cmd.run.clone()),
);
if !cmd.description.is_empty() {
cmd_obj.insert(
"description".to_string(),
serde_json::Value::String(cmd.description.clone()),
);
}
(
name.clone(),
serde_json::Value::Object(cmd_obj.into_iter().collect()),
)
})
.collect();
hook_config.insert(
"commands".to_string(),
serde_json::Value::Object(commands.into_iter().collect()),
);
}
if !hooks_config.commit_msg.scripts.is_empty() {
let scripts: HashMap<String, serde_json::Value> = hooks_config
.commit_msg
.scripts
.iter()
.map(|(name, script)| {
let mut script_obj = HashMap::new();
script_obj.insert(
"runner".to_string(),
serde_json::Value::String(script.runner.clone()),
);
if !script.env.is_empty() {
let env_obj: HashMap<String, serde_json::Value> = script
.env
.iter()
.map(|(k, v)| (k.clone(), serde_json::Value::String(v.clone())))
.collect();
script_obj.insert(
"env".to_string(),
serde_json::Value::Object(env_obj.into_iter().collect()),
);
}
(
name.clone(),
serde_json::Value::Object(script_obj.into_iter().collect()),
)
})
.collect();
hook_config.insert(
"scripts".to_string(),
serde_json::Value::Object(scripts.into_iter().collect()),
);
}
lefthook_config.insert(
"commit-msg".to_string(),
serde_json::Value::Object(hook_config.into_iter().collect()),
);
}
let yaml_output = serde_yaml_bw::to_string(&lefthook_config)
.map_err(|e| anyhow::anyhow!("Failed to serialize lefthook config to YAML: {}", e))?;
let highlighted_output = apply_syntax_highlighting(&yaml_output, "yaml")?;
println!("{highlighted_output}");
Ok(())
}