Skip to content

Instantly share code, notes, and snippets.

@alloc33
Last active September 29, 2024 21:04
Show Gist options
  • Select an option

  • Save alloc33/3c6a45b5af6cceb0fa309450422140e9 to your computer and use it in GitHub Desktop.

Select an option

Save alloc33/3c6a45b5af6cceb0fa309450422140e9 to your computer and use it in GitHub Desktop.
Simple rust script to print out obsidian hotkeys in a readable format. Path to the <VAULT>/.obsidian/hotkeys.json should be provided as argument
#!/usr/bin/env rust-script
//! ```cargo
//! [dependencies]
//! serde = { version = "1.0", features = ["derive"] }
//! serde_json = "1.0"
//! colored = "2.0"
//! cli-tables = "0.1.0"
//! ```
use cli_tables::Table;
use colored::*;
use serde::Deserialize;
use std::env;
use std::fs::File;
use std::io::BufReader;
#[derive(Debug, Deserialize)]
struct KeyBinding {
modifiers: Option<Vec<String>>,
key: Option<String>,
}
fn format_modifiers(mut modifiers: Vec<String>) -> Vec<String> {
// Ensure "Mod" (or "Cmd" in macOS) is first
if let Some(pos) = modifiers.iter().position(|x| x == "Mod") {
let mod_item = modifiers.remove(pos);
modifiers.insert(0, mod_item);
}
// If the OS is macOS, replace "Mod" with "Cmd" and "Alt" with "Opt"
if cfg!(target_os = "macos") {
for modifier in &mut modifiers {
if modifier == "Mod" {
*modifier = "Cmd".to_string();
} else if modifier == "Alt" {
*modifier = "Opt".to_string();
}
}
}
modifiers
}
fn print_action(action: &str, bindings: &Vec<KeyBinding>) {
println!("\n{}:", action.bold().magenta());
for binding in bindings {
let modifiers = binding.modifiers.clone().unwrap_or_else(Vec::new);
let formatted_modifiers = format_modifiers(modifiers).join(" + ").bold().yellow();
let key = binding.key.as_deref().unwrap_or("None").bold().cyan();
println!(" {} + {}", formatted_modifiers, key);
}
}
fn parse_and_print_json(
file_path: &str,
use_table: bool,
) -> Result<(), Box<dyn std::error::Error>> {
// Open and parse the file
let file = File::open(file_path)?;
let reader = BufReader::new(file);
let json_data: serde_json::Value = serde_json::from_reader(reader)?;
let mut no_bindings_list = Vec::new();
let mut table_data: Vec<Vec<String>> = vec![vec![
"Action".to_string(),
"Modifiers".to_string(),
"Key".to_string(),
]];
// Iterate and process JSON data
if let serde_json::Value::Object(map) = json_data {
for (action, bindings) in map {
if let serde_json::Value::Array(bindings_array) = bindings {
if bindings_array.is_empty() {
no_bindings_list.push(action);
} else {
let parsed_bindings: Vec<KeyBinding> = bindings_array
.into_iter()
.filter_map(|b| serde_json::from_value(b).ok())
.collect();
if use_table {
for binding in parsed_bindings {
let modifiers =
format_modifiers(binding.modifiers.unwrap_or_else(Vec::new))
.join(" + ");
let key = binding.key.unwrap_or("None".to_string());
table_data.push(vec![action.clone(), modifiers, key]);
}
} else {
if !parsed_bindings.is_empty() {
print_action(&action, &parsed_bindings);
}
}
}
}
}
// Print actions with no bindings at the end in table or regular format
if !no_bindings_list.is_empty() {
if use_table {
for action in no_bindings_list {
table_data.push(vec![action, "None".to_string(), "None".to_string()]);
}
} else {
println!(
"\n{}",
"Default actions with no bindings:".bold().bright_black()
);
for action in no_bindings_list {
println!(" {}", action.bright_black());
}
}
}
}
// If the table flag is used, print the table
if use_table {
let mut table = Table::new(&table_data);
println!("{}", table.to_string());
}
Ok(())
}
fn main() {
// Get the command line arguments
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
eprintln!("Usage: {} <path to json file> [-t]", args[0]);
std::process::exit(1);
}
let file_path = &args[1];
let use_table = args.len() > 2 && args[2] == "-t";
// Parse and print the JSON file
if let Err(e) = parse_and_print_json(file_path, use_table) {
eprintln!("Error: {}", e);
std::process::exit(1);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment