<!-- .slide: data-background="#fff" --> # Build a CLI Program ### Speaker: @tigercosmos --- <!-- .slide: data-background="#fff" --> ## What is CLI? command-line interface, aka none-GUI https://github.com/agarrharr/awesome-cli-apps ---- <!-- .slide: data-background="#fff" --> ### exa A modern version of ‘ls’. Written in Rust. https://github.com/ogham/exa ![](https://raw.githubusercontent.com/ogham/exa/master/screenshots.png) ---- <!-- .slide: data-background="#fff" --> ## Meow Create meow image with weather infomation https://github.com/weather-bot/meow ---- ![](https://i.imgur.com/zWk2hNJ.png) --- <!-- .slide: data-background="#fff" --> ## A CLI program ```bash $ ./program_name [arguments] [flags] [options] ``` ---- <!-- .slide: data-background="#fff" --> ```bash $ ./meow -h meow 0.0.1 tigercosmos <phy.tiger@gmail.com> Create meow image with weather infomation USAGE: meow [FLAGS] [OPTIONS] <mode> <image> <info_json> FLAGS: -h, --help Prints help information -l, --lang-en image in English -V, --version Prints version information OPTIONS: -o, --output <output> path of output image [default: out.jpg] ARGS: <mode> What kind of the image. [possible values: corner-mode, bottom-mode] <image> Input a kitty image. 800x800 better. If not, it would be cropped. <info_json> Weather infomation json as string. Including title, time, location, temp, humd, overview, overview2. ``` --- <!-- .slide: data-background="#fff" --> ## Build a CLI program from scratch Take `Meow` for example ![](https://i.imgur.com/GgfeS90.jpg) --- <!-- .slide: data-background="#fff" --> ### Create project ```bash $ cargo new meow $ cd meow ``` --- <!-- .slide: data-background="#fff" --> ## Arguments ```rust use std::env; fn main() { let args: Vec<String> = env::args().collect(); println!("{:?}", args); } ``` ```bash $./foo a1 a2 a3 a1 a2 a3 ``` ---- <!-- .slide: data-background="#fff" --> inconvenient to use - arguments might have default value - flags cannot exchange positions - what if you need options - `arg1` binds `arg2` ```bash $ ./foo -g -e a1 a3 a4 $ ./foo a1 -e -1 --path=~/test/123 ``` ---- <!-- .slide: data-background="#fff" --> ### Clap A full featured, fast Command Line Argument Parser for Rust https://github.com/clap-rs/clap ---- <!-- .slide: data-background="#fff" --> ```yml name: myapp version: "1.0" author: Kevin K. <kbknapp@gmail.com> about: Does awesome things args: - config: short: c long: config value_name: FILE help: Sets a custom config file takes_value: true - INPUT: help: Sets the input file to use required: true index: 1 - verbose: short: v multiple: true help: Sets the level of verbosity subcommands: - test: about: controls testing features version: "1.3" author: Someone E. <someone_else@other.com> args: - debug: short: d help: print debug information ``` ---- <!-- .slide: data-background="#fff" --> ```rust #[macro_use] extern crate clap; use clap::App; fn main() { // The YAML file is found relative to the current file, similar to how modules are found let yaml = load_yaml!("cli.yml"); let matches = App::from_yaml(yaml).get_matches(); // Same as previous examples... } ``` ---- <!-- .slide: data-background="#fff" --> ```bash $ myprog --help My Super Program 1.0 Kevin K. <kbknapp@gmail.com> Does awesome things USAGE: MyApp [FLAGS] [OPTIONS] <INPUT> [SUBCOMMAND] FLAGS: -h, --help Prints help information -v Sets the level of verbosity -V, --version Prints version information OPTIONS: -c, --config <FILE> Sets a custom config file ARGS: INPUT The input file to use SUBCOMMANDS: help Prints this message or the help of the given subcommand(s) test Controls testing features ``` --- <!-- .slide: data-background="#fff" --> ## Configuration ```env PORT = 8000 PATH = "home/foo/bar" MODE = "happy mode" ZONE = 8 AREA = "Taipei" ``` you could write by hands - read file `.env` - split `\n` - split `=` and add to `HashMap` or use a crate ---- <!-- .slide: data-background="#fff" --> ### dotenv_codegen https://crates.io/crates/dotenv_codegen the crate reads `.env` ```rust fn main() { println!("{}", dotenv!("PORT")); } ``` --- <!-- .slide: data-background="#fff" --> ## Environment Variables `std::env` ```rust use std::env; let key = "HOME"; match env::var_os(key) { Some(val) => println!("{}: {:?}", key, val), None => println!("{} is not defined in the environment.", key) } ``` --- ## Error handling ![](https://i.imgur.com/tE6KjSn.png) ---- <!-- .slide: data-background="#fff" --> ### panic ```rust panic!("this is panic"); ``` - break the program - exit without code - better use in script ---- <!-- .slide: data-background="#fff" --> ### Result ```rust enum MyErr { Reason1, Reason2, } fn foo() -> Result<(), MyErr> { match bar { Some(_)=>{} Nono => Err(MyErr::Reason1) } } fn hoo() { match foo() { Ok(_) => reply(), Err(e) => show_error(e) // `e` not work yet // we need `fmt` to tranlate to the message } } ``` `Result` pass the error without crash. ---- <!-- .slide: data-background="#fff" --> ### Error Message ```rust enum MyErr { Reason1(String), Reason2(String, u32), } impl fmt::Display for DrawError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { MyErr::Reason1(ref s) => write!(f, "`{}` is the error", s), MyErr::Reason2(ref s, ref num) => write!(f, "`{}` and `{}` are error", s, num), } } } ``` ```rust Err(e) => println!("{}", e) // `XXX` is the error ``` ---- <!-- .slide: data-background="#fff" --> ### Standard error ```bash $ cargo run > output.txt ``` we don't want to write error messages in logs. ```rust eprintln!(); // just like `println!()` ``` ---- <!-- .slide: data-background="#fff" --> ### Exit Code `std::process` ```rust process::exit(1); ``` none-zero code lets other programs know it failed --- ## WxKitty ![](https://i.imgur.com/p93EKgB.png) ---- ![](https://raw.githubusercontent.com/weather-bot/weather-bot/master/img/qrcode.png)
{"metaMigratedAt":"2023-06-14T18:56:20.329Z","metaMigratedFrom":"Content","title":"Build a CLI Program","breaks":true,"contributors":"[{\"id\":\"b4ba298e-5cbb-419b-a0c9-4f2e4612e8bf\",\"add\":21325,\"del\":14851}]"}
    883 views