{
  "path": "/quicli.html",
  "site": "at://did:plc:x67qh7v3fd7znbdhauc45ng3/site.standard.publication/3mjcd2t6afe25",
  "$type": "site.standard.document",
  "title": "quicli - The ideas behind my small Rust CLI framework",
  "updatedAt": "2018-01-30T00:00:00.000Z",
  "publishedAt": "2018-01-30T00:00:00.000Z",
  "textContent": "I recently wrote a new Rust crate: [quicli].\nAs the name might suggest,\nit gives you a way of quickly writing CLI programs in Rust.\nThis post is about the ideas and underlying philosophies behind this project.\n\n[quicli]: https://github.com/killercup/quicli\n\n_Note:_\nIf you found this because you were looking for quicli itself,\nhave a look at [quicli's Getting Started guide][site]!\n\n[site]: https://killercup.github.io/quicli/\n\nSome history\n\nI've been writing CLI tools in Rust for a while now.\nIt really surprised me how this language\nthat works really well for systems programming\nquickly became my go-to solution for problems\nI'd previously write Shell or Ruby scripts for.\n\nI published the post\n[\"5 Tips for Writing Small CLI Tools in Rust\"][cli-tips]\nat the end of August 2017\nthat you may have read,\nwhere I describe some of my take-aways from that\nin the form of short tips.\nI've gotten some great feedback from that post,\nand there were people telling me\nthat they seriously started looking into Rust\nafter having read that article.\nThat totally made my day:\nSomeone took a chance at learning something new\nand becoming part of the great community that Rust has\nbecause of something I did!\nHow cool is that?\n\nEver since then,\nI wanted to condense this down into a small framework or library\nthat you can use when you are just starting out with Rust\nas well as when you just want to write a quick tool.\nSadly, like so often,\nI didn't have the time or concentration\nto sit down and really do this.\nAnd here's the good news:\nFor some reason[^9e-talk],\nafter all these months,\nI've now published a very early version.\n\n[cli-tips]: /rust-cli-tips.html\n\n[^9e-talk]: Okay, I admit, I promised to give a talk about Rust, and I wanted to present some nice, clean example code. Of course, in the end I didn't show quicli in the talk.\n\nIt was a Sunday evening when I published the code,\nand I did so without thinking much about it.\nBut then something very interesting happened the very next day:\nAfter a link to quicli's repo was posted to reddit,\nGarrett Berg ([vitiral]) [replied] saying that\ntheir [stdcli] project was very similar to my endeavor.\nThis was unexpected.\nAnd not only were out projects similar,\nbut we were both willing to join forces\nand try to make one great crate\nthat takes the best of both approaches.\nGarrett opened a few issues on [quicli's issue tracker][issues] (thanks!),\nwhich basically concluded in [#19],\nwhich was originally titled\n\"merge stdcli and this lib\".\n\n[vitiral]: https://github.com/vitiral\n[replied]: https://www.reddit.com/r/rust/comments/7s3zsd/quicli_quickly_build_cool_cli_apps_in_rust/dt28i2b/\n[stdcli]: https://github.com/vitiral/stdcli\n[issues]: https://github.com/killercup/quicli/issues\n[#19]: https://github.com/killercup/quicli/issues/19\n\nThat sounded pretty awesome and made me really proud:\nDid my library,\nwhich was barely a day old at this point,\nlook cool enough to compete with their existing effort?\n\nBut, before blindly agreeing to this,\nI needed to take a step back\nand evaluate what I want quicli to be.\nBecause, let's be honest,\nI hadn't really thought about a 'grand quicli vision'\nbefore.\nAnd why should I?\nI just wanted a small framework-like thing\nthat made writing CLI apps less of a pain.\nBut how exactly do I want to to this?\nThanks to Garrett's questions and comments\nI now have an answer to this.\n\nWhat do I want to quicli to be\n\n(The following are basically quotes from [#19].)\n\nMy goal is to provide\nan opinionated set\nof convenience functions\nto quickly build CLI apps.\n\nIn its implementation,\nyou get what I'd call\n\"a small framework around your main function\":\nquicli's main! macro set up some basic things,\nand its prelude gives a few tools\nthat are commonly required in CLI apps.\nThese tools are either re-exports from existing Rust crates,\nor abstractions around library functions that are more ergonomic\nthat better fit the CLI use-case\n(as well as my ergonomic requirements).\n\nWhat I want you to end up with is code that is\nconcise,\nwell-structured and boilerplate-free,\nand production-ready.\nOkay, that's nothing new; we all want that.\nBut what does this even mean for a CLI app?\n\nConcise\n\nIdeally: You type one line to get one thing done.\nIf you want to read a file\nand see if its text contains the word \"foobar\",\nyou can do:\n\nYou want to read a file, I give you a read_file function.\nThis skips allocating a String as buffer, opening File, and calling read_to_string on that file.\nWhile reading a file to a String this way may not be the most performant option,\nit is very concise.\n\nAnother aspect of conciseness:\nInstead of using unwrap to deal with errors,\nquicli's main! allows you to use ? to propagate errors,\nand shows human-readable messages when your program exited with an error state.\nSimilarly, instead of\nwriting return Err(SomeType::new(\"message\")),\nby using quicli you also get [failure]'s bail! and ensure!.\n\nWell-structured and boilerplate-free\n\nI just repeatedly wrote that this is a framework for _small_ code bases.\nNevertheless, it makes sense to structure your code well.\nActually, structuring your code in a clever way helps you keep the code size small.\n\nFor example, this is how you parse CLI arguments (incl. type checking):\n\nSee how you didn't write any code to convert whatever followed --count to an integer?\nBy the way:\nAdding doc comments to your CLI arguments structure is not just for when you write the code,\nstructopt will also show them when calling your program with --help.\n\nProduction-ready\n\nIn my experience,\nthe best way to ensure something ends up being used in production for years to come\nis to call it a prototype.\nI always want to get some basic stuff that will make using even the smallest tool less of a pain.\n\nFor example, CLI tools written with quicli (and structopt)\nalways have a --help message,\nas well as --version flag,\nand good error messages.\nSimilarly, you get logging for free,\nand if you add two more lines,\nyou also get a --verbose/-v CLI flag to control the log level\n(pass -vvv for \"debug\" level logging).\n\n[failure]: https://docs.rs/failure\n\nWhat I don't want quicli to be\n\nI want to use quicli to quickly build CLI apps\n– And I want to stop there!\nIf you are no longer writing a small CLI tool\n(I'd say something like 500 lines of code is no longer small),\nyour problems are likely more complicated\nthan what quicli gives you tools for.\nAt that point,\nyou should consider replacing your usage of (parts of) quicli\nwith more sophisticated approaches.\n\nLet's contrast that with Garrett's [stdcli].\nIt (re-)exports a lot of sophisticated tools!\nAs Garrett writes:\n\n> stdcli's goal is basically to make creating a CLI in rust more like creating one in python from a user experience and ergonomics point of view.\n> This means:\n> batteries [are] included,\n> almost everything you need is already imported […]\n\nquicli also re-exports a bunch of stuff,\nand I agree that these re-exports make the experience of writing CLIs quickly very smooth.\nThere are some traits that are very useful, and deserve to re-exported!\nI want to be very deliberate in what I re-export though\nand ideally I want to provide abstractions instead of full-featured tools.\nLet me provide some reasoning for this.\n\nFor example:\nHaving Read and Write in the prelude seems like a good idea, but AtomicBool?\nI've never used that one the last 4 years I've written Rust code.\nLet's take a step back and look at it from another perspective:\nI'm pretty sure even Read and Write are only used in a few specific use cases.\nI say: We should try to identify these use cases,\nand provide convenience functions for those!\n\nI didn't want to re-export env_logger;\ninstead I changed the main! macro\n(that quicli provides to set give you nice error handling\nand the ability to use ? in your \"main\" function)\nto initialize the logger automatically.\nSimilarly, I added simple file read/write functions.\n\nWriting this, the following dawned on me:\n\nI ship leaky abstractions\n\nI'd rather introduce some (simple and leaky) abstractions\n(that promise to only be useful 80% of the time)\ninstead trying to give the user everything they need.\nI'm not delivering building blocks here,\nI deliver concrete tools.\n(This is hard for me -- I really like abstract and generic things!)\n\nAnother example (which I haven't implemented) that works this way:\nA regex_matches function\nthat automatically uses lazy_static and only covers the simplest case\n(maybe even return a very simple type\ninstead of a powerful iterator over capture results).\nWhy?\nSimple: Have you seen the regex docs?\nThey are _wonderful._\nSadly, for newcomers/forgetful people/drunk programmers/etc.\nthey are also _wonderfully complex._\n\nSo, instead of offering the user \"everything\",\nI want to introduce some abstractions that are simple to use,\nand have simple but useful examples.\nTo not get stuck,\nand to give user the ability to grow, learn, and discover new stuff,\nthe documentation should point exactly to where to look\nwhen you want to use some of the features on a more complex level.\n\nIndeed, instead of adding any feature flags to this crate\n(that enable additional components to be loaded/exposed),\nI want to have a clear line\nwhere a user is supposed to stop relying on quicli.\nI'd rather have an \"eject\" option\nto switch from quicli to \"all crates imported manually\"\nthan add and re-export a whole bunch crates\n(which will at some point lead to extern crate kitchensink;).\n\nAnd now add some good documentation\n\nSo far I've written what I want quicli to contain,\nbut another important aspect to me is to prove that it is useful\nfor writing small tools\n-- even if you've only just started with Rust.\nThis is why I initially wrote the Readme file\nin the form of a How To:\nIn a few simple steps I explain\nhow to set up a Cargo project,\nwhat you need to add to parse CLI arguments,\nand how to write the few lines necessary\nfor implementing a simple head tool.\n\nMy hope was that this not only shows\nhow easy it is to get started with quicli\nbut to ",
  "canonicalUrl": "https://deterministic.space//quicli.html"
}