I did this once. I used rustyline https://crates.io/crates/rustyline to read commands from the terminal (like libreadline), shell-words https://crates.io/crates/shell-words to tokenize command lines (like the Unix shell does), and clap to parse them.
My only complaint is that there doesn't appear to be any way for a background thread to produce output without mucking up the rustyline prompt.
There is another crate named rustyline-async https://crates.io/crates/rustyline-async that can kinda-sorta do that, though: it has a SharedWriter type with which to write to the terminal without breaking the prompt. Requires an async executor, though, and also requires that *all* terminal output be routed through the SharedWriter (so no using println!).