// Std use std::io::Write; // Internal use app::parser::Parser; use args::OptBuilder; use completions; pub struct BashGen<'a, 'b> where 'a: 'b, { p: &'b Parser<'a, 'b>, } impl<'a, 'b> BashGen<'a, 'b> { pub fn new(p: &'b Parser<'a, 'b>) -> Self { BashGen { p: p } } pub fn generate_to(&self, buf: &mut W) { w!( buf, format!( r#"_{name}() {{ local i cur prev opts cmds COMPREPLY=() cur="${{COMP_WORDS[COMP_CWORD]}}" prev="${{COMP_WORDS[COMP_CWORD-1]}}" cmd="" opts="" for i in ${{COMP_WORDS[@]}} do case "${{i}}" in {name}) cmd="{name}" ;; {subcmds} *) ;; esac done case "${{cmd}}" in {name}) opts="{name_opts}" if [[ ${{cur}} == -* || ${{COMP_CWORD}} -eq 1 ]] ; then COMPREPLY=( $(compgen -W "${{opts}}" -- "${{cur}}") ) return 0 fi case "${{prev}}" in {name_opts_details} *) COMPREPLY=() ;; esac COMPREPLY=( $(compgen -W "${{opts}}" -- "${{cur}}") ) return 0 ;; {subcmd_details} esac }} complete -F _{name} -o bashdefault -o default {name} "#, name = self.p.meta.bin_name.as_ref().unwrap(), name_opts = self.all_options_for_path(self.p.meta.bin_name.as_ref().unwrap()), name_opts_details = self.option_details_for_path(self.p.meta.bin_name.as_ref().unwrap()), subcmds = self.all_subcommands(), subcmd_details = self.subcommand_details() ).as_bytes() ); } fn all_subcommands(&self) -> String { debugln!("BashGen::all_subcommands;"); let mut subcmds = String::new(); let scs = completions::all_subcommand_names(self.p); for sc in &scs { subcmds = format!( r#"{} {name}) cmd+="__{fn_name}" ;;"#, subcmds, name = sc, fn_name = sc.replace("-", "__") ); } subcmds } fn subcommand_details(&self) -> String { debugln!("BashGen::subcommand_details;"); let mut subcmd_dets = String::new(); let mut scs = completions::get_all_subcommand_paths(self.p, true); scs.sort(); scs.dedup(); for sc in &scs { subcmd_dets = format!( r#"{} {subcmd}) opts="{sc_opts}" if [[ ${{cur}} == -* || ${{COMP_CWORD}} -eq {level} ]] ; then COMPREPLY=( $(compgen -W "${{opts}}" -- "${{cur}}") ) return 0 fi case "${{prev}}" in {opts_details} *) COMPREPLY=() ;; esac COMPREPLY=( $(compgen -W "${{opts}}" -- "${{cur}}") ) return 0 ;;"#, subcmd_dets, subcmd = sc.replace("-", "__"), sc_opts = self.all_options_for_path(&*sc), level = sc.split("__").map(|_| 1).fold(0, |acc, n| acc + n), opts_details = self.option_details_for_path(&*sc) ); } subcmd_dets } fn option_details_for_path(&self, path: &str) -> String { debugln!("BashGen::option_details_for_path: path={}", path); let mut p = self.p; for sc in path.split("__").skip(1) { debugln!("BashGen::option_details_for_path:iter: sc={}", sc); p = &find_subcmd!(p, sc).unwrap().p; } let mut opts = String::new(); for o in p.opts() { if let Some(l) = o.s.long { opts = format!( "{} --{}) COMPREPLY=({}) return 0 ;;", opts, l, self.vals_for(o) ); } if let Some(s) = o.s.short { opts = format!( "{} -{}) COMPREPLY=({}) return 0 ;;", opts, s, self.vals_for(o) ); } } opts } fn vals_for(&self, o: &OptBuilder) -> String { debugln!("BashGen::vals_for: o={}", o.b.name); use args::AnyArg; if let Some(vals) = o.possible_vals() { format!(r#"$(compgen -W "{}" -- "${{cur}}")"#, vals.join(" ")) } else { String::from(r#"$(compgen -f "${cur}")"#) } } fn all_options_for_path(&self, path: &str) -> String { debugln!("BashGen::all_options_for_path: path={}", path); let mut p = self.p; for sc in path.split("__").skip(1) { debugln!("BashGen::all_options_for_path:iter: sc={}", sc); p = &find_subcmd!(p, sc).unwrap().p; } let mut opts = shorts!(p).fold(String::new(), |acc, s| format!("{} -{}", acc, s)); opts = format!( "{} {}", opts, longs!(p).fold(String::new(), |acc, l| format!("{} --{}", acc, l)) ); opts = format!( "{} {}", opts, p.positionals .values() .fold(String::new(), |acc, p| format!("{} {}", acc, p)) ); opts = format!( "{} {}", opts, p.subcommands .iter() .fold(String::new(), |acc, s| format!("{} {}", acc, s.p.meta.name)) ); for sc in &p.subcommands { if let Some(ref aliases) = sc.p.meta.aliases { opts = format!( "{} {}", opts, aliases .iter() .map(|&(n, _)| n) .fold(String::new(), |acc, a| format!("{} {}", acc, a)) ); } } opts } }