use std::{ collections::HashMap, env::args, fs, io::{Read, Write}, mem, os::unix::net::{UnixListener, UnixStream}, path::PathBuf, process, str::FromStr, }; use anyhow::{Context, bail}; use clap::Parser; use keymaps::map_tree::Trie; use serde::{Deserialize, Serialize}; macro_rules! is_open { ($is_open:ident, @set_element $value:ident[$idx:expr]) => {{ if let Some(value) = $value.get($idx - 1) && $value.len() > $idx { if let Some(io) = &mut $is_open { io.truncate($idx); io.push(value.clone()); println!("{}\"{}\" = {{", " ".repeat($idx + 1), value); eprintln!("Setting io element {} to '{}' (io: '{}')", $idx, value, io.join(",")); } else if $idx == 1 { $is_open = Some(vec![value.clone()]); println!(" \"{}\" = {{", value); eprintln!("Initializing io with element '{}'", value); } else { unreachable!() } } }}; ($is_open:ident, @query $value:ident [..$till:expr]) => {{ if $value.len() <= $till { false } else { if let Some(io) = &$is_open { let mut output = true; for (idx, val) in $value.iter().take($till).enumerate() { output = output && io.get(idx).map(|got| got == val).unwrap_or(false); } if output { eprintln!("Query for '{}' (io: '{}') matched at element {}", $value.join(","), io.join(","), $till); } output } else { false } } }}; ($is_open:ident, @close $num:expr) => {{ if let Some(io) = &mut $is_open { eprintln!("Performing close for {} elements (io: '{}') ..", $num, io.join(",")); for idx in (0..io.len()).rev().take($num) { io.pop(); eprintln!(" Emitting close at element {} (io now: '{}')", idx + 1, io.join(",")); println!("{}}};", " ".repeat(idx + 2)); } } else { unreachable!() } }}; ($is_open:ident, @close_till $value:ident $num:expr) => {{ if let Some(io) = $is_open.clone() { is_open!($is_open, @close io.len() - $num) } else { } }}; ($is_open:ident, @close_all) => {{ if let Some(io) = $is_open.clone() { is_open!($is_open, @close io.len()); } else { } }}; } fn print_key(key: Vec, node: Mapping) { print!( "{}\"{}\" = {{ cmd = \"{}\"; ", " ".repeat(key.len() + 1), key.last().expect("That's the len"), node.command.join(" "), ); if let Some(sets_mode_to) = node.sets_mode_to { println!("sets_mode_to = \"{}\";}};", sets_mode_to); } else { println!("}};"); } } #[derive(clap::Parser, Debug)] #[clap(rename_all = "snake_case")] pub(crate) struct Cli { #[arg(long)] preset: bool, #[arg(short = 'M', default_value = "default")] mode: MapMode, #[arg(short = 'm', default_value = "default")] sets_mode_to: MapMode, key: String, command: Vec, } #[derive( Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord, Hash, Clone, Copy, Debug, Default, )] #[expect(non_camel_case_types)] enum MapMode { #[default] Default, Insert, Operator, ReplaceOne, Replace, Visual, f, F, t, T, } impl std::fmt::Display for MapMode { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { MapMode::Default => f.write_str("default")?, MapMode::Insert => f.write_str("insert")?, MapMode::Operator => f.write_str("operator")?, MapMode::ReplaceOne => f.write_str("replace_one")?, MapMode::Replace => f.write_str("replace")?, MapMode::Visual => f.write_str("visual")?, MapMode::f => f.write_str("f")?, MapMode::F => f.write_str("F")?, MapMode::t => f.write_str("t")?, MapMode::T => f.write_str("T")?, } Ok(()) } } impl FromStr for MapMode { type Err = anyhow::Error; fn from_str(s: &str) -> Result { let me = match s { "operator" => Self::Operator, "insert" => Self::Insert, "replace_one" => Self::ReplaceOne, "replace" => Self::Replace, "visual" => Self::Visual, "default" => Self::Default, "f" => Self::f, "F" => Self::F, "t" => Self::t, "T" => Self::T, other => bail!("Mapping mode '{other}' not yet supported"), }; Ok(me) } } enum Message { NewBinding(usize), End(usize), } type Key = String; #[derive(Debug, Deserialize, Serialize)] struct Binding { key: Vec, command: Vec, sets_mode_to: Option, mode: MapMode, } struct MapStore { map: HashMap>, } #[derive(Clone, PartialEq, PartialOrd, Debug)] struct Mapping { command: Vec, sets_mode_to: Option, } fn main() -> anyhow::Result<()> { let communication_socket: PathBuf = PathBuf::from("/tmp/fish_map_store.socket"); match PathBuf::from(args().next().unwrap()) .file_name() .unwrap() .to_str() .unwrap() { "bind-end" => { let mut stream = UnixStream::connect(communication_socket)?; let message_raw: [u8; size_of::()] = unsafe { mem::transmute(Message::End(0)) }; stream.write_all(&message_raw)?; stream.flush()?; } "bind" => { let args = Cli::parse(); assert!(args.preset); let binding = Binding { key: args.key.split(',').map(|s| s.to_string()).collect(), command: args.command, sets_mode_to: if args.mode != args.sets_mode_to { Some(args.sets_mode_to) } else { None }, mode: args.mode, }; let binding_msg = serde_json::to_string(&binding)?; let mut stream = UnixStream::connect(communication_socket)?; let length = binding_msg.len(); let message_raw: [u8; size_of::()] = unsafe { mem::transmute(Message::NewBinding(length)) }; stream.write_all(&message_raw)?; stream.write_all(binding_msg.as_bytes())?; stream.flush()?; drop(stream); } "bind-server" => bind_server_main(communication_socket)?, other => { eprintln!("Personality '{other}' not supported."); process::exit(2); } } // let mappings = store.map.entry(args.mode).or_insert(Vec::default()); // mappings.push(mapping); // // { // let mut file = fs::OpenOptions::new() // .create(true) // .truncate(true) // .append(false) // .write(true) // .open(storage_file)?; // // file.write_all(serde_json::to_string(&store)?.as_bytes())?; // } // // { // eprintln!("Added {}", args.key); // } Ok(()) } fn bind_server_main(communication_socket: PathBuf) -> anyhow::Result<()> { if communication_socket.exists() { fs::remove_file(&communication_socket)?; } let mut store = MapStore { map: HashMap::new(), }; let listener = UnixListener::bind(communication_socket)?; for stream in listener.incoming() { match stream { Ok(mut stream) => { let mut next_message_raw = [0; size_of::()]; stream.read_exact(&mut next_message_raw)?; let next_message: Message = unsafe { mem::transmute(next_message_raw) }; match next_message { Message::NewBinding(length) => { let mut binding_str = String::new(); stream.read_to_string(&mut binding_str)?; let binding: Binding = serde_json::from_str(&binding_str[..length]) .with_context(|| { format!("Failed to parse '{binding_str:?}' as json") })?; let trie = store.map.entry(binding.mode).or_default(); trie.insert( &binding.key, Mapping { command: binding.command, sets_mode_to: binding.sets_mode_to, }, )?; } Message::End(_pointless_value) => { println!("{{"); for (mode, trie) in store.map { println!(" \"{mode}\" = {{"); { let mut io: Option> = None; for (key, node) in trie.iter() { if is_open!(io, @query key[..4]) { is_open!(io, @close_till key 4); print_key(key, node); } else if is_open!(io, @query key[..3]) { is_open!(io, @close_till key 3); is_open!(io, @set_element key[4]); print_key(key, node); } else if is_open!(io, @query key[..2]) { is_open!(io, @close_till key 2); is_open!(io, @set_element key[3]); is_open!(io, @set_element key[4]); print_key(key, node); } else if is_open!(io, @query key[..1]) { is_open!(io, @close_till key 1); is_open!(io, @set_element key[2]); is_open!(io, @set_element key[3]); is_open!(io, @set_element key[4]); print_key(key, node); } else { { eprint!("No query matched: '{}'", key.join(",")); if let Some(io) = &io { eprintln!(" (io: '{}')", io.join(",")); } else { eprintln!(" (io empty)"); } } is_open!(io, @close_all); is_open!(io, @set_element key[1]); is_open!(io, @set_element key[2]); is_open!(io, @set_element key[3]); is_open!(io, @set_element key[4]); print_key(key, node); } } is_open!(io, @close_all); } println!(" }};"); } println!("}}"); break; } } } Err(_) => { /* connection failed */ break; } } } Ok(()) }