{"id":13495876,"url":"https://github.com/ssokolow/rust-cli-boilerplate","last_synced_at":"2025-04-09T20:41:27.333Z","repository":{"id":41534445,"uuid":"81766259","full_name":"ssokolow/rust-cli-boilerplate","owner":"ssokolow","description":"Rust project boilerplate for CLI applications","archived":false,"fork":false,"pushed_at":"2021-04-18T19:48:05.000Z","size":352,"stargazers_count":183,"open_issues_count":0,"forks_count":13,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-03-23T22:37:19.853Z","etag":null,"topics":["boilerplate","boilerplate-application","boilerplate-template","cli","cli-app","cli-command","rust","rust-lang","rust-language","rustlang"],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ssokolow.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-02-12T23:37:17.000Z","updated_at":"2025-02-07T10:28:16.000Z","dependencies_parsed_at":"2022-09-17T03:11:46.504Z","dependency_job_id":null,"html_url":"https://github.com/ssokolow/rust-cli-boilerplate","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ssokolow%2Frust-cli-boilerplate","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ssokolow%2Frust-cli-boilerplate/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ssokolow%2Frust-cli-boilerplate/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ssokolow%2Frust-cli-boilerplate/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ssokolow","download_url":"https://codeload.github.com/ssokolow/rust-cli-boilerplate/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248109809,"owners_count":21049380,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["boilerplate","boilerplate-application","boilerplate-template","cli","cli-app","cli-command","rust","rust-lang","rust-language","rustlang"],"created_at":"2024-07-31T19:01:39.151Z","updated_at":"2025-04-09T20:41:27.310Z","avatar_url":"https://github.com/ssokolow.png","language":"Rust","funding_links":[],"categories":["Rust","cli"],"sub_categories":[],"readme":"# Rust CLI Project Template\n\n![MIT/Apache 2.0](https://img.shields.io/badge/license-MIT%2FApache_2.0-blue.svg)\n![POSIX-only build tooling](https://img.shields.io/badge/dev_platform-POSIX-lightgrey.svg)\n\nA base project template for comfortably building small but reliable utilities in\nthe [Rust](https://rust-lang.org/) programming language.\n\n## Rationale\n\nHistorically, I have used Python for quick little scripts (from which my big,\nlong-lived projects tend to unexpectedly develop), but maintaining large things\nwritten in Python risks turning into a\n[gumption trap](https://en.wikipedia.org/wiki/Gumption_trap) due to the need to\neither manually test every code path I affect when making changes or pour a lot\nof effort into writing an automated test suite to do it for me.\n\nRust's stronger compile-time guarantees remove most of that motivation-sapping\nbusywork, but, since I've spent so many years getting comfortable with Python,\nworking in a new language risks being a chicken-and-egg problem which kills off\nthe will to change tasks. (Once I start something, it's easy to keep going.)\n\nThe purpose of this repository is to lower the friction for Rust-based\ndevelopment of such \"scripts of unknown potential\" as much as possible. (Hence\nthe inclusion of aliases like `just run` which serve only to remove the need to\nthink about whether to type `just` or `cargo`, and the wrappers for `cargo-edit`\ncommands which regenerate the API documentation I'll have open in a browser tab\nafter adding/removing/updating dependencies.)\n\nIn short, this is a \"do it properly\" answer to the workflow that evolved around\nthe\n[`boiler` snippet](https://gist.github.com/ssokolow/ef59f97673693d717f5096d7987afd2c)\nfor Python that I keep in my [UltiSnips](https://github.com/SirVer/ultisnips).\n\n## License\n\nThis repository is licensed under your choice of the\n[MIT](http://opensource.org/licenses/MIT) or\n[Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0) licenses with the\nexception of the license texts themselves. Please replace the `LICENSE` file in\nthe `template` folder with your preferred license if you do not habitually start\nyour new projects under the GNU GPLv3.\n\n\u003c!-- SPDX-License-Identifier: Apache-2.0 OR MIT --\u003e\n\n## Features\n\n- Uses [StructOpt](https://github.com/TeXitoi/structopt) (with colourized\n  `--help` output and \"Did you mean...?\" suggestions enabled) for argument\n  parsing because [gumdrop](https://github.com/murarth/gumdrop) doesn't support\n  `OsString`-based parsing for correct handling of\n  [non-UTF8 paths](https://en.wikipedia.org/wiki/Mojibake).\n- Uses [anyhow](https://github.com/dtolnay/anyhow) for unified error handling.\n- Presents an `app::main(opts: CliOpts) -\u003e Result\u003c()\u003e` function to keep your\n  application logic cleanly separated from argument parsing and handling of\n  fatal errors.\n- Exposes clap's support for generating shell completions by providing a\n  `--dump-completions \u003cshell\u003e` option.\n- Enables almost all rustc and\n  [clippy](https://rust-lang.github.io/rust-clippy/current/) lints without\n  making clippy mandatory.\n- A comprehensive set of [just](https://github.com/casey/just) commands, easily\n  customized via variables (eg. for cross-compilation), including `install` and\n  `uninstall`, which also take care of shell completions and a manpage.\n- `just build-dist` for a 100% static x86_64 binary that starts at roughly\n  `272KiB` (`248KiB` with `panic=\"abort\"`) in new projects.\n- `just install-deps` to install all but two optional dependencies on\n  Debian-family distros.\n- `just install-cargo-deps` to install all distro-agnostic dependencies.\n- A basic `.travis.yml` for use with [Travis-CI](https://travis-ci.org/) and\n  [Nightli.es](https://nightli.es/).\n- The `just fmt` command always calls the nightly version of rustfmt to ensure\n  access to the excessive number of customization options which are gated away\n  as unstable.\n\n## Usage\n\n1. Clone a copy of this repository (`.git` must exist, so no archive downloads)\n2. Run `apply.py path/to/new/project`\n3. Edit `src/app.rs` to implement your application logic\n\n**NOTE:** As a safety measure, `apply.py` generates new projects from the git\n`HEAD` of the template repository (not the working tree), so any local changes\nyou make will not be picked up until you commit them.\n\n## Supplementary Files\n\n\u003chtml\u003e\n\u003ctable\u003e\n\u003ctr\u003e\u003cth colspan=\"2\"\u003eMetadata\u003c/th\u003e\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003eLICENSE\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003eA copy of the \u003ca href=\"https://www.gnu.org/licenses/gpl-3.0.html\"\u003eGNU GPLv3\u003c/a\u003e\n as my \"until I've had time to think about it\"\nlicense of choice. Make a local commit which replaces this with your preferred\ndefault for new projects.\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003eCONTRIBUTING\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003eA copy of the\n  \u003ca href=\"https://developercertificate.org/\"\u003eDeveloper Certificate of Origin\u003c/a\u003e,\n  which is the Linux kernel developers' more ideologically appropriate\n  alternative to CLAs as a means of legally armouring themselves against\n  bad-faith contributions\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\u003cth colspan=\"2\"\u003eConfiguration\u003c/th\u003e\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003e.gitignore\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003eIgnore \u003ccode\u003e/target\u003c/code\u003e and other generated files\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003eclippy.toml\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003eWhitelist for CamelCase names which trigger Clippy's \"identifier needs\n  backticks\" lint\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003erustfmt.toml\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003eA custom rustfmt configuration which shows\n  \u003ccode\u003eTODO\u003c/code\u003e/\u003ccode\u003eFIXME\u003c/code\u003e comments and attempts to make it conform\n  to the style I'm willing to enforce at the expense of not using rustfmt if\n  necessary.\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\u003cth colspan=\"2\"\u003eDevelopment Automation\u003c/th\u003e\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003eapply.py\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003eRun this to generate new projects as a workaround for cargo-generate's\n  incompatibility with justfile syntax\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003ejustfile\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003eBuild/development-automation commands via\n\u003ca href=\"https://github.com/casey/just\"\u003ejust\u003c/a\u003e (a pure-Rust make-alike)\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\u003cth colspan=\"2\"\u003eSupport Code You May Borrow\u003c/th\u003e\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003egen_justfile_reference.py\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003eCode which is used to regenerate the reference charts for\n  \u003ccode\u003ejustfile\u003c/code\u003e variables and commands in this README so it's easy\n  to keep them up to date.\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003etest_justfile.py\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003eA test suite for my \u003ccode\u003ejustfile\u003c/code\u003e which you may want to adapt\n  for your own projects.\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\u003c/html\u003e\n\n## Justfile Reference\n\n### Variables (`just --evaluate`)\n\n\u003chtml\u003e\n\u003c!-- BEGIN JUSTFILE TABLE: variables --\u003e\n\u003ctable\u003e\n\u003ctr\u003e\u003cth\u003eVariable\u003c/th\u003e\u003cth\u003eDefault Value\u003c/th\u003e\u003cth\u003eDescription\u003c/th\u003e\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003eCARGO_BUILD_TARGET\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003e\u003ccode\u003ex86_64-unknown-linux-musl\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003eThe target for \u003ccode\u003ecargo\u003c/code\u003e commands to use and\n  \u003ccode\u003einstall-rustup-deps\u003c/code\u003e to install\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003ebuild_flags\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003e\u003c/td\u003e\n  \u003ctd\u003eAn easy place to modify the build flags used\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003echannel\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003e\u003ccode\u003estable\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003eAn easy way to override the \u003ccode\u003ecargo\u003c/code\u003e channel for just this project\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003efeatures\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003e\u003c/td\u003e\n  \u003ctd\u003eExtra cargo features to enable\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\u003cth colspan=\"3\"\u003e\u003ccode\u003ebuild-dist\u003c/code\u003e\u003c/th\u003e\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003esstrip_bin\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003e\u003ccode\u003esstrip\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003eSet this if you need to override it for a cross-compiling \u003ccode\u003esstrip\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003estrip_bin\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003e\u003ccode\u003estrip\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003eSet this to the cross-compiler's \u003ccode\u003estrip\u003c/code\u003e when cross-compiling\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003estrip_flags\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003e\u003ccode\u003e--strip-unneeded\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003eFlags passed to \u003ccode\u003estrip_bin\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003eupx_flags\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003e\u003ccode\u003e--ultra-brute\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003eFlags passed to \u003ca href=\"https://upx.github.io/\"\u003eUPX\u003c/a\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\u003cth colspan=\"3\"\u003e\u003ccode\u003ekcachegrind\u003c/code\u003e\u003c/th\u003e\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003ecallgrind_args\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003e\u003c/td\u003e\n  \u003ctd\u003eExtra arguments to pass to \u003ca\n  href=\"http://valgrind.org/docs/manual/cl-manual.html\"\u003ecallgrind\u003c/a\u003e.\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003ecallgrind_out_file\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003e\u003ccode\u003ecallgrind.out.justfile\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003eTemporary file used by \u003ccode\u003ejust kcachegrind\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003ekcachegrind\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003e\u003ccode\u003ekcachegrind\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003eSet this to override how \u003ccode\u003ekcachegrind\u003c/code\u003e is called\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\u003cth colspan=\"3\"\u003e\u003ccode\u003einstall\u003c/code\u003e and \u003ccode\u003euninstall\u003c/code\u003e\u003c/th\u003e\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003ebash_completion_dir\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003e\u003ccode\u003e~/.bash_completion.d\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003eWhere to \u003ccode\u003einstall\u003c/code\u003e bash completions. \u003cstrong\u003eYou'll need to manually\n  add some lines to source these files in \u003ccode\u003e.bashrc\u003c/code\u003e\u003c/strong\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003efish_completion_dir\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003e\u003ccode\u003e~/.config/fish/completions\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003eWhere to \u003ccode\u003einstall\u003c/code\u003e fish completions. You'll probably never need to\n  change this.\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003emanpage_dir\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003e\u003ccode\u003e~/.cargo/share/man/man1\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003eWhere to \u003ccode\u003einstall\u003c/code\u003e manpages. As long as \u003ccode\u003e~/.cargo/bin\u003c/code\u003e is\n  in your \u003ccode\u003ePATH\u003c/code\u003e, \u003ccode\u003eman\u003c/code\u003e should automatically pick up this\n  location.\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003ezsh_completion_dir\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003e\u003ccode\u003e~/.zsh/functions\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003eWhere to \u003ccode\u003einstall\u003c/code\u003e zsh completions. \u003cstrong\u003eYou'll need to add this\n  to your \u003ccode\u003efpath\u003c/code\u003e manually\u003c/strong\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\u003c!-- END JUSTFILE TABLE: variables --\u003e\n\u003c/html\u003e\n\n### Commands (`just --list`)\n\n\u003chtml\u003e\n\u003c!-- BEGIN JUSTFILE TABLE: commands --\u003e\n\u003ctable\u003e\n\u003ctr\u003e\u003cth\u003eCommand\u003c/th\u003e\u003cth\u003eArguments\u003c/th\u003e\u003cth\u003eDescription\u003c/th\u003e\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003eDEFAULT\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003e\u003c/td\u003e\n  \u003ctd\u003eShorthand for \u003ccode\u003ejust fulltest\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\u003cth colspan=\"3\"\u003eDevelopment\u003c/th\u003e\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003eadd\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003eargs\u0026nbsp;(optional)\u003c/td\u003e\n  \u003ctd\u003eAlias for cargo-edit's \u003ccode\u003ecargo add\u003c/code\u003e which regenerates local API docs\n  afterwards\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003ebloat\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003eargs\u0026nbsp;(optional)\u003c/td\u003e\n  \u003ctd\u003eAlias for \u003ccode\u003ecargo bloat\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003echeck\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003eargs\u0026nbsp;(optional)\u003c/td\u003e\n  \u003ctd\u003eAlias for \u003ccode\u003ecargo check\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003eclean\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003eargs\u0026nbsp;(optional)\u003c/td\u003e\n  \u003ctd\u003eSuperset of \u003ccode\u003ecargo clean -v\u003c/code\u003e which deletes other stuff this justfile\n  builds\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003eclippy\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003eargs\u0026nbsp;(optional)\u003c/td\u003e\n  \u003ctd\u003eAlias for \u003ccode\u003ecargo clippy\u003c/code\u003e which touches \u003ccode\u003esrc/*\u003c/code\u003e to work\n  around clippy bug\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003edoc\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003eargs\u0026nbsp;(optional)\u003c/td\u003e\n  \u003ctd\u003eRun rustdoc with \u003ccode\u003e--document-private-items\u003c/code\u003e and then run\n  cargo-deadlinks\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003efmt\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003eargs\u0026nbsp;(optional)\u003c/td\u003e\n  \u003ctd\u003eAlias for \u003ccode\u003ecargo +nightly fmt -- {{args}}\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003efmt-check\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003eargs\u0026nbsp;(optional)\u003c/td\u003e\n  \u003ctd\u003eAlias for \u003ccode\u003ecargo +nightly fmt -- --check {{args}}\u003c/code\u003e which un-bloats\n  TODO/FIXME warnings\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003efulltest\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003e\u003c/td\u003e\n  \u003ctd\u003eRun all installed static analysis, plus \u003ccode\u003ecargo test\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003egeiger\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003eargs\u0026nbsp;(optional)\u003c/td\u003e\n  \u003ctd\u003eAlias for \u003ccode\u003ecargo geiger\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003ekcachegrind\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003eargs\u0026nbsp;(optional)\u003c/td\u003e\n  \u003ctd\u003eRun a debug build under \u003ca\n  href=\"http://valgrind.org/docs/manual/cl-manual.html\"\u003ecallgrind\u003c/a\u003e, then open\n  the profile in \u003ca href=\"https://kcachegrind.github.io/\"\u003eKCachegrind\u003c/a\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003ekcov\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003e\u003c/td\u003e\n  \u003ctd\u003eGenerate a statement coverage report in \u003ccode\u003etarget/cov/\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003erm\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003eargs\u0026nbsp;(optional)\u003c/td\u003e\n  \u003ctd\u003eAlias for cargo-edit's \u003ccode\u003ecargo rm\u003c/code\u003e which regenerates local API docs\n  afterwards\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003esearch\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003eargs\u0026nbsp;(optional)\u003c/td\u003e\n  \u003ctd\u003eConvenience alias for opening a crate search on lib.rs in the browser\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003etest\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003eargs\u0026nbsp;(optional)\u003c/td\u003e\n  \u003ctd\u003eAlias for \u003ccode\u003ecargo test\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003eupdate\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003eargs\u0026nbsp;(optional)\u003c/td\u003e\n  \u003ctd\u003eAlias for cargo-edit's \u003ccode\u003ecargo update\u003c/code\u003e which regenerates local API\n  docs afterwards\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\u003cth colspan=\"3\"\u003eLocal Builds\u003c/th\u003e\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003ebuild\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003e\u003c/td\u003e\n  \u003ctd\u003eAlias for \u003ccode\u003ecargo build\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003einstall\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003e\u003c/td\u003e\n  \u003ctd\u003eInstall the un-packed binary, shell completions, and a manpage\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003erun\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003eargs\u0026nbsp;(optional)\u003c/td\u003e\n  \u003ctd\u003eAlias for \u003ccode\u003ecargo run -- {{args}}\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003euninstall\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003e\u003c/td\u003e\n  \u003ctd\u003eRemove any files installed by the \u003ccode\u003einstall\u003c/code\u003e task (but leave any\n  parent directories created)\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\u003cth colspan=\"3\"\u003eRelease Builds\u003c/th\u003e\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003ebuild-dist\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003e\u003c/td\u003e\n  \u003ctd\u003eMake a release build and then strip and compress the resulting binary\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003edist\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003e\u003c/td\u003e\n  \u003ctd\u003eCall \u003ccode\u003edist-supplemental\u003c/code\u003e and \u003ccode\u003ebuild-dist\u003c/code\u003e and copy the\n  packed binary to \u003ccode\u003edist/\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003edist-supplemental\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003e\u003c/td\u003e\n  \u003ctd\u003eBuild the shell completions and a manpage, and put them in \u003ccode\u003edist/\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\u003cth colspan=\"3\"\u003eDependencies\u003c/th\u003e\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003einstall-apt-deps\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003e\u003c/td\u003e\n  \u003ctd\u003eUse \u003ccode\u003eapt-get\u003c/code\u003e to install dependencies \u003ccode\u003ecargo\u003c/code\u003e can't\n  (except \u003ccode\u003ekcov\u003c/code\u003e and \u003ccode\u003esstrip\u003c/code\u003e)\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003einstall-cargo-deps\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003e\u003c/td\u003e\n  \u003ctd\u003e\u003ccode\u003einstall-rustup-deps\u003c/code\u003e and then \u003ccode\u003ecargo install\u003c/code\u003e tools\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003einstall-deps\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003e\u003c/td\u003e\n  \u003ctd\u003eRun \u003ccode\u003einstall-apt-deps\u003c/code\u003e and \u003ccode\u003einstall-cargo-deps\u003c/code\u003e. List what\n  remains.\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003ccode\u003einstall-rustup-deps\u003c/code\u003e\u003c/td\u003e\n  \u003ctd\u003e\u003c/td\u003e\n  \u003ctd\u003eInstall (don't update) nightly and \u003ccode\u003echannel\u003c/code\u003e toolchains, plus\n  \u003ccode\u003eCARGO_BUILD_TARGET\u003c/code\u003e, clippy, and rustfmt\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\u003c!-- END JUSTFILE TABLE: commands --\u003e\n\u003c/html\u003e\n\n### Tips\n\n- Edit the `DEFAULT` command. That's what it's there for.\n- You can use `just` from any subdirectory in your project. It's like `git` that\n  way.\n- `just path/to/project/` (note the trailing slash) is equivalent to\n  `(cd path/to/project; just)`\n- `just path/to/project/command` is equivalent to\n  `(cd path/to/project; just command)`\n\n- The simplest way to activate the bash completion installed by `just install`\n  is to add this to your `.bashrc`:\n\n  ```sh\n  for script in ~/.bash_completion.d/*; do\n    if [ -e \"$script\" ]; then\n      . \"$script\"\n    fi\n  done\n  ```\n\n      foo\n\n- The simplest way to activate the zsh completion installed by `just install` is\n  to add this to your `.zshrc`:\n\n  ```zsh\n  fpath=(~/.zsh/functions(:A) $fpath)\n  ```\n\n- When using clap/StructOpt validators for inputs such as filesystem paths, only\n  use them to bail out early on bad input, not as your only check. They're\n  conceptually similar to raw pointers and can be invalidated between when you\n  check them and when you try to use them because Rust can't control what the OS\n  and other programs do in the interim. See\n  [this blog post](http://blog.ssokolow.com/archives/2016/10/17/a-more-formal-way-to-think-about-validity-of-input-data/)\n  for more on this idea of references versus values in command-line arguments.\n\n## Build Behaviour\n\nIn order to be as suitable as possible for building compact, easy-to-distribute,\nhigh-reliability replacements for shell scripts, the following build options are\ndefined:\n\n### If built via `just build`:\n\n1. The default `CARGO_BUILD_TARGET` defined in the `justfile` will specify a\n   32-bit x86 build, statically linked against\n   [musl-libc](http://www.musl-libc.org/) for portability comparable to a\n   [Go](\u003chttps://en.wikipedia.org/wiki/Go_(programming_language)\u003e) binary.\n   (Unless `musl-gcc` is installed, this will cause build failures if you depend\n   on any crates which link to C or C++ code.)\n\n### If built via `cargo build --release`:\n\n1. Full LTO (Link-Time Optimization) will be enabled. (`lto = true`)\n2. The binary will be built with `opt-level = \"z\"` to further reduce file size.\n3. If `panic=\"abort\"` is uncommented in `Cargo.toml`, LTO will prune away the\n   unwinding machinery to save even more space, but panics will not cause `Drop`\n   implementations to be run and will be uncatchable.\n\n### If built via `just build-dist`:\n\n1. Unless otherwise noted, all optimizations listed above.\n   [[1]](https://lifthrasiir.github.io/rustlog/why-is-a-rust-executable-large.html)\n2. The binary will be stripped with\n   [`--strip-unneeded`](https://www.technovelty.org/linux/stripping-shared-libraries.html)\n   and then with\n   [`sstrip`](http://www.muppetlabs.com/~breadbox/software/elfkickers.html) (a\n   [more aggressive](https://github.com/BR903/ELFkickers/tree/master/sstrip)\n   companion used in embedded development) to produce the smallest possible\n   pre-compression size.\n3. The binary will be compressed via\n   [`upx --ultra-brute`](https://upx.github.io/). In my experience, this makes a\n   file about 1/3rd the size of the input.\n\n**NOTE:** `--strip-unneeded` removes all symbols that `readelf --syms` sees from\nthe `just build` output, so it's not different from `--strip-all` in this case,\nbut it's a good idea to get in the habit of using the safe option that's smart\nenough to just Do What I Mean™.\n\n### If built by `just dist`:\n\n1. A packed binary will be built via `build-dist` and copied into `dist/`\n2. Shell completion files and a manpage will also be built and saved into\n   `dist/`\n\n**NOTE:** Depending on who you're distributing precompiled binaries to, you may\nwant get an overview of how virus scanners react to your binary using\n[VirusTotal](https://www.virustotal.com/).\n\nEspecially with anything involving compression, small numbers of false positives\nare a fact of life in the world of virus detection. For example, when I tested\nthe official installer for the\n[NSIS](https://en.wikipedia.org/wiki/Nullsoft_Scriptable_Install_System)\nauthoring tools, which is used by various major companies including McAfeee, two\nor three no-name entries in the list of 60+ virus scanners they test reported it\nto have a virus.\n\nIf this proves problematic, you can either uninstall UPX or modify the\n`justfile` so the `dist` command always prefers the `.stripped` copy of the\nbinary over the `.packed` one.\n\n## Experimental Cross-Compilation Support\n\nI am currently in the process of extending this template to support generating\nWindows binaries, though I have no immediate plans to replace the `justfile`\ntasks so, for now, Windows-hosted development will have to settle for calling\n`cargo` commands directly.\n\n**NOTE:** I haven't yet used a fresh Ubuntu install under VirtualBox to verify\nthat I've correctly listed all the steps needed to achieve a working build\nenvironment.\n\nTo set up an environment where setting `CARGO_BUILD_TARGET` to\n`x86_64-pc-windows-gnu` will complete successfully and produce a `.exe` file\nwhich appears to work under my preliminary testing:\n\n1. Install a MinGW package like Ubuntu's `mingw-w64`\n2. Run `rustup target add x86_64-pc-windows-gnu`\n3. Add the following two lines to `~/.cargo/config`:\n\n```toml\n[target.x86_64-pc-windows-gnu]\nlinker = \"/usr/bin/x86_64-w64-mingw32-gcc\"\n```\n\nTo make `cargo test` also work cross-platform:\n\n1. Make sure your `~/.wine` is 64-bit (indicated by an `#arch=win64` comment in\n   `system.reg`)\n2. Make sure your kernel's `binfmt_misc` support is configured to allow running\n   `.exe` files in the terminal via Wine as if they were native binaries.\n\n**NOTE:** Wine is only suitable for \"rapid iteration, approximate compatibility\"\ntesting. For proper testing of Windows binaries, the only reliable solution is\nto download one of the specially licensed \"only for testing\"\n[Windows VMs](https://developer.microsoft.com/en-us/microsoft-edge/tools/vms/)\nthat Microsoft offers for download from [http://modern.ie/](http://modern.ie/)\nand those cannot be used to make legally redistributable builds.\n\nIf you want to set up a Continuous Deployment-style workflow with testing\nagainst real Windows targets, the only viable option is to bypass `just` and\ncall `cargo` directly under real Windows. I suggest a CI service like\n[AppVeyor](appveyor.com) for this. (See also\n[rust-cross](https://github.com/japaric/rust-cross).)\n\n## Dependencies\n\nIn order to use the full functionality offered by this boilerplate, the\nfollowing dependencies must be installed:\n\n- `just add`:\n  - [cargo-edit](https://github.com/killercup/cargo-edit)\n    (`cargo install cargo-edit`)\n- `just bloat`:\n  - [cargo-bloat](https://github.com/RazrFalcon/cargo-bloat)\n    (`cargo install cargo-bloat`)\n- `just build-dist`:\n  - The toolchain specified by the \u003ccode\u003echannel\u003c/code\u003e variable.\n  - The target specified by the \u003ccode\u003eCARGO_BUILD_TARGET\u003c/code\u003e variable.\n  - `strip` (Included with binutils)\n  - [`sstrip`](http://www.muppetlabs.com/~breadbox/software/elfkickers.html)\n    **(optional)**\n  - [`upx`](https://upx.github.io/) (**optional**, `sudo apt-get install upx`)\n- `just fmt` and `just fmt-check`:\n  - A nightly Rust toolchain (`rustup toolchain install nightly`)\n  - [rustfmt](https://github.com/rust-lang/rustfmt) for the nightly toolchain\n    (`rustup component add rustfmt --toolchain nightly`)\n- `just dist-supplemental`:\n  - [help2man](https://www.gnu.org/software/help2man/)\n    (`sudo apt-get install help2man`)\n- `just kcachegrind`:\n  - [Valgrind](http://valgrind.org/) (`sudo apt-get install valgrind`)\n  - [KCachegrind](https://kcachegrind.github.io/)\n    (`sudo apt-get install kcachegrind`)\n- `just kcov`:\n  - A [Rust-compatible build](http://sunjay.ca/2016/07/25/rust-code-coverage) of\n    kcov\n- `just rm`:\n  - [cargo-edit](https://github.com/killercup/cargo-edit)\n    (`cargo install cargo-edit`)\n- `just test`:\n  - [clippy](https://github.com/rust-lang/rust-clippy)\n    (`rustup component add clippy`)\n  - [cargo-audit](https://github.com/RustSec/cargo-audit)\n    (`cargo install cargo-audit`)\n  - [cargo-deadlinks](https://github.com/deadlinks/cargo-deadlinks)\n    (`cargo install cargo-deadlinks`)\n  - [cargo-outdated](https://github.com/kbknapp/cargo-outdated)\n    (`cargo install cargo-outdated`)\n- `just update`:\n  - [cargo-edit](https://github.com/killercup/cargo-edit)\n    (`cargo install cargo-edit`)\n\n### Dependency Installation\n\n- **Debian/Ubuntu/Mint:**\n\n  ```sh\n  export PATH=\"$HOME/.cargo/bin:$PATH\"\n  cargo install just\n  just install-deps\n\n  # ...and now manually install the following optional tools:\n  #  - sstrip (from ELFkickers)\n  #  - kcov (version 31 or higher with --verify support)\n  ```\n\n- **Other distros:**\n\n  ```sh\n  export PATH=\"$HOME/.cargo/bin:$PATH\"\n  cargo install just\n  just install-cargo-deps\n\n  # ...and now manually install the following optional tools:\n  #  - help2man\n  #  - kcachegrind\n  #  - kcov (version 31 or higher with --verify support)\n  #  - strip (from binutils)\n  #  - sstrip (from ELFkickers)\n  #  - upx\n  #  - valgrind\n  ```\n\n## TODO\n\n- Add a `.travis.yml` at the top level to plumb the various test suites\n  (template repo and generated project) into CI and then add a badge.\n- Add a `#[cfg(windows)]` version of the `path_output_dir` validator and make\n  the `libc` dependency conditional on `not(windows)` so that cross-compiling\n  for Windows using the `x86_64-pc-windows-gnu` target can be a viable way to\n  quickly fire off alpha/beta-testing builds to Windows-using peers.\n- Investigate how flexible [QuiCLI](https://github.com/killercup/quicli) and its\n  dependency on env_logger are and whether it'd be useful to rebase on it or\n  whether I'd just be reinventing most of it anyway to force the exact look and\n  feel I achieved with stderrlog. (eg. The `Verbosity` struct doesn't implement\n  \"`-v` and `-q` are mirrors of each other\" and I'm rather fond of stderrlog's\n  approach to timestamp toggling.)\n  - What effect does QuiCLI have on the final binary size? (not a huge concern)\n- Investigate why [cargo-cov](https://github.com/kennytm/cov) isn't hiding the\n  components of the rust standard library and whether it can be induced to\n  generate coverage despite some tests failing. If so, add a command for it.\n- Read the [callgrind docs](http://valgrind.org/docs/manual/cl-manual.html) and\n  figure out how to exclude the Rust standard library from what Kcachegrind\n  displays.\n  - I may need to filter the output.\n    [[1]](https://stackoverflow.com/questions/7761448/filter-calls-to-libc-from-valgrinds-callgrind-output)\n  - Figure out how to add a `just` task for a faster but less precise profiler\n    like [gprof](https://en.wikipedia.org/wiki/Gprof)\n    [[1]](http://www.thegeekstuff.com/2012/08/gprof-tutorial/)\n    [[2]](https://sourceware.org/binutils/docs/gprof/),\n    [OProfile](http://oprofile.sourceforge.net/)\n    [[1]](https://llogiq.github.io/2015/07/15/profiling.html), or\n    [perf](https://perf.wiki.kernel.org/index.php/Main_Page)\n    [[1]](http://blog.adamperry.me/rust/2016/07/24/profiling-rust-perf-flamegraph/)\n    to make it easy to leverage the various trade-offs. (And make sure to\n    provide convenient access to flame graphs and at least one perf inspector\n    GUI or TUI.)\n  - Include references to these resources on how profilers can mislead in\n    different ways.\n    [[1]](http://yosefk.com/blog/how-profilers-lie-the-cases-of-gprof-and-kcachegrind.html)\n    [[2]](http://www.catb.org/~esr/writings/taoup/html/optimizationchapter.html)\n    [[3]](http://blog.adamperry.me/rust/2016/07/24/profiling-rust-perf-flamegraph/)\n  - Look into options for making it as easy as possible to optimize and\n    regression-test runtime performance.\n    [[1]](https://github.com/rust-lang/rust/issues/31265https://github.com/rust-lang/rust/issues/31265)\n    [[2]](https://crates.io/crates/bencher)\n    [[3]](https://github.com/japaric/criterion.rshttps://github.com/japaric/criterion.rs)\n    [[4]](https://github.com/BurntSushi/cargo-benchcmp)\n- Test and enhance `.travis.yml`\n\n  - Consider officially supporting Windows as a target (probably using\n    [cargo-make](https://crates.io/crates/cargo-make) instead of Just) and, if I\n    do, come up with an `appveyor.yml`... possibly the one from\n    [this project](https://github.com/starkat99/appveyor-rust).\n\n- Add a `run-memstats` Just task which swaps in jemalloc and sets\n  `MALLOC_CONF=stats_print:true`\n- Investigate commit hooks\n  [[1]](https://stackoverflow.com/questions/3462955/putting-git-hooks-into-repository)\n  [[2]](https://stackoverflow.com/questions/427207/can-git-hook-scripts-be-managed-along-with-the-repository)\n  [[3]](https://mpdaugherty.wordpress.com/2010/04/06/how-to-include-git-hooks-in-a-repository-and-still-personalize-your-machine/)\n- Once I've cleared out these TODOs, consider using this space for a reminder\n  list of best practices for avoiding \"higher-level footguns\" noted in my pile\n  of assorted advice. (Things like \"If you can find a way to not need path\n  manipulation beyond 'pass this opaque token around', then you can eliminate\n  entire classes of bugs\")\n- At least _list_ a snip of example code for something like\n  [rustyline](https://lib.rs/crates/rustyline) as the suggested way to do simple\n  user prompting.\n- Gather my custom clap validators into a crate, add some more, and have this\n  depend on it:\n  - Self-Contained data:\n    - Boolean is `1`/`y`/`yes`/`t`/`true` or `0`/`n`/`no`/`f`/`false`\n      (case-insensitive, include a utility function for actual parsing)\n    - Integers:\n      - Can be parsed as a decimal integer `\u003e 0` (eg. number of volumes)\n      - Can be parsed as a decimal integer `\u003e= 0` (eg. number of bytes)\n      - Number of bytes, with optional SI mebi- unit suffix (eg. `16m`,\n        including optional `b`, case-insensitive)\n    - Floats:\n      - Can be parsed as a float in the range `0.0 \u003c= x \u003c= 1.0`\n  - Invalidatable/Referential data:\n    - Input files:\n      - Directory exists and is browsable (`+rX`)\n      - Path is a readable file or browsable directory (ie. read or recurse)\n    - Output files:\n      - Integers:\n        - Augmented \"number of bytes, with optional SI mebi- unit suffix\"\n          validator with upper limit for producing files representable by\n          ISO9660/FAT32 filesystems on removable media. (2GiB, since some\n          implementations use 32-bit signed offsets)\n      - Paths:\n        - File path is probably FAT32 writable\n          - If file exists, `access()` says it's probably writable\n          - If file does not exist, name is FAT32-valid and within a probably\n            writable directory.\n        - File path is probably FAT32 writable, with `mkdir -p`\n          - Nonexistent path components are FAT32-valid\n          - Closest existing ancestor is a probably writable directory\n    - Network I/O:\n      - Integers:\n        - Successfully parses into a valid listening TCP/UDP port number\n          (0-65535, I think)\n        - Successfully parses into a valid, non-root, listening TCP/UDP port\n          number (0 or 1024-65535, I think)\n        - Successfully parses into a valid connecting TCP/UDP port number\n          (1-65535, I think)\n      - Strings:\n        - Successfully parses into a\n          [`SocketAddr`](https://doc.rust-lang.org/std/net/enum.SocketAddr.html)\n          (IP+port, may perform DNS lookup?)\n        - Successfully parses into an\n          [`IpAddr`](https://doc.rust-lang.org/std/net/enum.IpAddr.html) (may\n          perform DNS lookup?)\n      - URLs:\n        - Is well-formed relative URL\n          ([external dependency](https://docs.rs/url/1.4.0/url/) behind a cargo\n          feature)\n        - Is well-formed absolute URL\n          ([external dependency](https://docs.rs/url/1.4.0/url/) behind a cargo\n          feature)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fssokolow%2Frust-cli-boilerplate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fssokolow%2Frust-cli-boilerplate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fssokolow%2Frust-cli-boilerplate/lists"}