Wrote a cli tool in Rust
Tags: rust, cli
Published: 2020-05-23 14:26:17

---
title: Wrote a cli tool in Rust
published: true
description: I wrote a simple cli tool in rust, introduce it's usage, explain the motivation and describe the hard point of implementation
tags: rust, cli
---


## Intro

I wrote a simple cli tool named *github-cli-utils* in Rust. This tool suppports daily OSS activity in GitHub.

I'll introduce why and how write it!

## Usage

Install from crate.io

```
cargo install github-cli-utils
```

After you installed `github-cli-utils`, you run it and show following.

```
λ github-cli-utils help
github-cli-utils 0.1
Daiki Ihara <sasurau4@gmail.com>
Support developing OSS via cli

USAGE:
    github-cli-utils [FLAGS] <SUBCOMMAND>

FLAGS:
    -d, --debug      Turn debugging information on
    -h, --help       Prints help information
    -V, --version    Prints version information

SUBCOMMANDS:
    add-upstream    add upstream to your git repository
    help            Prints this message or the help of the given subcommand(s)
```

This tool provide add-upstream command. It add upstream repository to your forked repository!


## Motivation

My motivation of writing `github-cli-utils` is 2 following points.

1. Add upstream is a bit troublesome
2. Curiosity to Rust
3. Needs to platform and shell agonistic command

### Add upstream is a bit troublesome

When you find a bug or typo at readme about the OSS, you forked the repository, clone it and add upstream.

Adding upstream is a bit troublesome because it needs to copy parent repository's url in browser.

Going back and forth between terminal and browser is annoying, isn' it?



### Curiosity to Rust

I've been curious to Rust for about 2 years...

Time has passed and congrats five years from Rust's birth.

I began to write the simple cli tool to solve my daily little trouble.

### Needs to platform and shell agonistic command

I use macOS in my work and also use Ubuntu in my private coding. The enviroment is a bit different.

If I just want to achieve my usecase, I would done it by writing shell script like following.

```sh
git-remote-add-upstream() {
  if ! type jq > /dev/null 2>&1; then
    echo "'jq' is not installed." >&2
    exit 1
  fi
  if [ $# -ne 1 ]; then
    echo "Need repo name. e.g : 'user/repo'"
    exit 1
  fi
  local upstream=$(curl -L "https://api.github.com/repos/$1" | jq -r '.parent.full_name')
  if [ "$upstream" = "null" ]; then
    echo "upstream not found." >&2
    exit 1
  fi
  git remote add upstream "git@github.com:${upstream}.git"
}
alias grau='git-remote-add-upstream'
```

This script is quoted from https://tech-blog.sgr-ksmt.org/2016/03/04/git_remote_add_upstream/

It's very fine, but need to be `jq` command installed. To be worse, I'm using fish shell.
So I wrote a command that add upstream automatically without thinking about platform and shell.

## Hard point of implementation

The codes is https://github.com/sasurau4/github-cli-utils.

### Argument parsing

I used [clap](https://github.com/clap-rs/clap) to parse argument. clap is useful library for that purpose.

I implemented `add-upstream` by subcommand. I wanted to use debug argument after command like `github-cli-utils add-upstream --debug`.
In default, clap only allows flags place before subcommand like `github-cli-utils [FLAGS] <SUBCOMMAND>`. I had to use global(true) in argument setting to allow flags placed after subcommand.

### Https access

At first, I use [hyper](https://github.com/hyperium/hyper) as http client. It's very useful but a bit complicated when accessing some resource via https.[^https] 

[^https]: https://github.com/hyperium/hyper/issues/1025

I use [reqwest](https://github.com/seanmonstar/reqwest) instead of hyper. It's very easy to use.

## Conclusion

Rust is a good language to implement cli tool thanks to language itself and the ecosystem.
I hope this article help you implement cli tools.

Also, I'm welcome PRs and issues to `github-cli-utils`!