Before we get into this week’s regular program, please check out this survey on writing CLI apps in Rust and share it with whoever you think might be interested! (It’s open until March 23, and we’ll publish the results here at the beginning of April.)
As you may have read, a big focus this year is to make Rust awesome for a whole bunch of domains – WASM, Embedded, Networking Services, and CLIs.
Today, it’s my great honor to announce the CLI working group¹! The goal of this working group is to make writing cross-platform, tested, modern command line applications frictionless.
How are we going to achieve this? Mostly by coordinating efforts. The working group’s job is to look at the bigger picture and give valuable input into what needs to be done to have one smooth “Writing CLI apps in Rust” story from top to bottom. (This absolutely means contributing to crates and creating new ones, too!)
While the other new working groups make absolutely sense to me, I’m not sure why a working group is necessary for CLI applications. A workgroup for GUI would be much more suited.
CLI Apps are already working great and it does not seems to me a huge coordinated effort is required to make it better. Have you examples on what the group will work?
Not sure about this. When I started studying of Rust, I decided to rewrite
my C++ CLI application on Rust, because of third-party dependencies were causing problems for users on some OSes and the most of these dependicies
I can replace with stdlib or with crates. And that will make the life of users easier, because of such great tool as cargo.
My C++ CLI application take input as command line arguments or from stdin,
and output results to stdout. All looks simple, I rewrite almost 90% of busness logic and stuck on output results to user.
My C++ application have been translated on several languges,
thanks to users and that was a huge problem. I went beyond the time allotted for work and that is end of story. And I don't see any significant changes in gettext crate sinse then.
I believe stabilizing TryFrom will go a long way to help with this. Whether consuming file input in configurations chunks or individual settings TryFrom is a good way to do “try to read in configuration or work with a default”.
For me including a library for my non-standard configuration loader was too big of a solution for my simple feature. The configuration loader I was building was a “Resume Previous Work” system where each grouping of written settings was loaded in as a unique key and any progress made would overwrite that key/config and write back out to the file. Now this system was written because I would have occasional power outages over big work loads so I made a redundant configuration system where if the first config file failed a backup one would be used. The concept is simple; I use the Display trait to format the output of my config, separate entries by two new lines, and use TryFrom for reading from the file to try to convert each string config block into a struct or simply ignore it on failure.
For this same project I used the Clap library which I really like and for that library I have two recommendations. When allowing dynamic ordering of parameters and have a specific last argument it would be really nice to do away with the -- requirement before the last argument. And getting started took a few tries. I think the documentation needs a minimal getting started implementation (the least you can get away with). And from there go with first steps. I ended up needing to copy and paste the main example for it to work and I modified that.
What I'd really like to see in the Rust CLI ecosystem is better libraries for ncurses window & menu management. And I would like elegant spinners (like with Heroku's CLI) & progress bars (which look best with many going at once like with npm).
I recommend you Cursive crate.
It's an awesome TUI library with possible ncurses and termion backends and many more. I'm using Cursive to build fui crate.
I had some issues with Cursive owning my data when it needed to be updated from another thread, but I agree it’s pretty good if you’re fine with its thread handling the main the application processing and owning the data.
I think that config files aren’t a CLI domain, and I just use serializers to disk most of the time.
I feel this is a fairly well solved domain, but I’ve hit problems in the past writing services in Rust. I’ll go look at the other working groups to see if that complaint fits better there.
Not a rust dev but interested in the topic.
What about different styles of pathnames support?
E.g. UNIX, Windows, different styles of UNIX-on-Windows (e.g. WSL, Cygwin, Git bash)
@gene-pavlovsky Are you familiar with Rust’s Path and OsStr types? I believe they already solve many of these issues, though possibly not all. Do you have some specific issues in mind?
As far as I understand, choosing the path separator is done at compile time, in Path::MAIN_SEPARATOR. Thing is, you may be compiling for windows, but the environment you are running in is cygwin. While windows will automatically convert forward slashes to backslashes (according to the link in that thread), cygwin won’t.
So I wonder if we should add another compile platform called cygwin, or always use forward slashes, or allow for a runtime check (right now the workaround is checking for the HOME env variable during runtime).
But I’m not really familiar with this kinda stuff so I’m sure there are other solutions.
Yeah, this is a real issue. That’s why ripgrep has a --path-separator flag:
--path-separator SEPARATOR
Set the path separator to use when printing file paths. This defaults to your platform’s path
separator, which is / on Unix and \ on Windows. This flag is intended for overriding the default
when the environment demands it (e.g., cygwin). A path separator is limited to a single byte.
There are many other issues related to cygwin and paths, some of which are difficult to solve because cygwin has its own special magic layer to translate UNIX paths to Windows path. Rust programs don’t implement that layer, so people used to using programs from cygwin with UNIX style paths get a frustrating UX.
In the spirit of shamelessly stealing great ideas, I would recommend taking a look at the golang cobra library. It’s very well designed and has a lot of thoughtful touches that would be great to see in a Rust CLI lib.
The only thing I can think of that could use improvement are improving the liner and termion crates. Beyond that, clap has everything extremely covered in the non-interactive department.
When writing CLIs I have always strived to implement a git-like UI. The things I always reimplemented/copied were the following.
Config file placement: Three different files: /etc/<command>/config, ~/.config/<command>/config, ./.<command>config,
Internal subcommands and subcommands that look for executables as <command>-<subcommand>, so it can be easily extended,
a <command> config command that can set options, for one project I even had it validate the options (with <command>-<subcommand> --is-config-valid or similar).
It would be great to have a crate that helps with that scaffolding.
Why doesn’t Rust have something like „scan!“ or other simple input-macros in standard-library?!
Any plans or official proposals to merge text_io with std::io??? That would be nice and simply development of CLI-apps, especially for beginners respectively and teaching of language.