misc

Unnamed repository; edit this file 'description' to name the repository.
git clone git://git.alexwennerberg.com/misc
Log | Files | Refs | README | LICENSE

arg.rs (3302B) - raw


      1 // Extremely minimalist command line interface, inspired by
      2 // [sbase](https://git.suckless.org/sbase/)'s
      3 // [arg.h](https://git.suckless.org/sbase/file/arg.h.html)
      4 //
      5 // I believe this has basically the same behavior, which is:
      6 // * flags can be grouped (-abc)
      7 // * missing arg -> print usage, exit
      8 // * invalid flag -> print usage, exit
      9 //
     10 // Unlike arg.h, does not support -kvalue arguments (although it could with
     11 // some modification
     12 //
     13 // This is, of course, aggressively minimalist, perhaps even too much so.
     14 // Feel free to modify to meet your needs.
     15 //
     16 // Goals are:
     17 // 1. As simple as possible
     18 // 2. No use of macros
     19 //
     20 // Copy/paste this code and you have a CLI! No library needed!
     21 //
     22 // This file is licensed under the terms of 0BSD:
     23 //
     24 // Permission to use, copy, modify, and/or distribute this software for any purpose with or without
     25 // fee is hereby granted.
     26 //
     27 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
     28 // SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
     29 // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     30 // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
     31 // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
     32 // OF THIS SOFTWARE.
     33 
     34 use std::env;
     35 use std::ffi::OsString;
     36 use std::os::unix::ffi::OsStrExt;
     37 use std::path::PathBuf;
     38 use std::process::exit;
     39 use std::str::FromStr;
     40 
     41 // Example usage
     42 fn main() {
     43     let args = Args::from_env();
     44     println!(
     45         "a: {}, b: {}, f: {:?}, positional: {:?}",
     46         args.a, args.b, args.f, args.positional
     47     )
     48 }
     49 
     50 fn usage() -> ! {
     51     let name = env::args().next().unwrap();
     52     eprintln!(
     53         "usage: {} [-a] [-f FOO] [-b BAR] FOOBAR...
     54 
     55 FOOBAR: Variable number of FOOBARs
     56 
     57 FLAGS: 
     58 -a  turn on a flag
     59 
     60 ARGS:
     61 -f  filepath arg f set to FOO
     62 -b  integer arg b set to BAR. Default: 7
     63 ",
     64         name
     65     );
     66     exit(1)
     67 }
     68 
     69 #[derive(Default)]
     70 pub struct Args {
     71     pub a: bool,
     72     pub f: PathBuf,
     73     pub b: i32,
     74     pub positional: Vec<OsString>,
     75 }
     76 
     77 impl Args {
     78     pub fn default() -> Self {
     79         Args {
     80             b: 7,
     81             ..Default::default()
     82         }
     83     }
     84 
     85     pub fn from_env() -> Self {
     86         let mut out = Self::default();
     87         let mut args = env::args_os().skip(1);
     88         while let Some(arg) = args.next() {
     89             let s = arg.to_string_lossy();
     90             let mut ch_iter = s.chars();
     91             if ch_iter.next() != Some('-') {
     92                 out.positional.push(arg);
     93                 continue;
     94             }
     95             ch_iter.for_each(|m| match m {
     96                 // Edit these lines //
     97                 'a' => out.a = true,
     98                 'f' => out.f = parse_os_arg(args.next()),
     99                 'b' => out.b = parse_arg(args.next()),
    100                 // Stop editing //
    101                 _ => usage(),
    102             })
    103         }
    104         out
    105     }
    106 }
    107 
    108 fn parse_arg<T: FromStr>(a: Option<OsString>) -> T {
    109     a.and_then(|a| a.into_string().ok())
    110         .and_then(|a| a.parse().ok())
    111         .unwrap_or_else(|| usage())
    112 }
    113 
    114 fn parse_os_arg<T: From<OsString>>(a: Option<OsString>) -> T {
    115     match a {
    116         Some(s) => T::from(s),
    117         None => usage(),
    118     }
    119 }