In Part 6 (the final episode of this mini series) we turn TypeGym from a hard coded demo into a proper command line tool.
Up to this point many settings lived in source code: which text source we used, how much text we generated, how wide paragraphs were, etc. Now we move all of that into a clean CLI interface powered by clap.
What We Covered
- Added
clapwith derive support and switchedConfigto#[derive(Parser)] - Exposed runtime flags for UI + content configuration:
--fg-miss,--fg-empty,--fg-results--text-source SOURCE(static / nonsense / weighted / file:/ markov: ) --max-words N--reflowand--width N
- Implemented
FromStrforTextSourceso clap can parse it directly - Updated all generators (nonsense / weighted / markov) to respect
max_words - Added a simple
reflow()function to wrap generated text into lines of a given width
Design Insights
Clap works best when you lean into types
Instead of parsing strings manually in main(), we let clap do the heavy lifting:
Config::parse()gives us a strongly typed config structTextSourceimplementsFromStr, so--text-sourcecan map directly into an enum variant
That makes the “front door” of the application clean and it scales nicely as we add more options.
Reflow is a usability feature, not just formatting
Generated text often has awkward line breaks (or none at all). A terminal typing trainer feels much nicer when lines:
- don’t overflow the screen
- have consistent widths
- form readable paragraphs
So we add optional reflow that wraps words into lines up to --width.
Implementation Highlights
1) Config becomes a real CLI parser
Config is now the single source of truth for configuration and clap derives it:
And in main we simply do:
let config = parse;
No more Config::new(), no more TODOs.
2) Parsing TextSource from --text-source
To make clap understand TextSource we implement FromStr:
static→TextSource::Staticnonsense→TextSource::GenerateNonsenseweighted→TextSource::GenerateWeightedNonsensefile:<path>→TextSource::File(path)markov:<path>→TextSource::MarkovChain(path)
This gives us a nice user facing syntax without needing separate flags for every source.
3) Make text generation configurable (max_words)
Previously, generators hard coded 100 words. Now:
generate_nonsense(max_words)generate_weighted_nonsense(max_words)markov.generate(max_words)
Everything respects --max-words making the tool much more flexible.
4) Optional text wrapping (--reflow, --width)
When reflow is enabled we:
- split the generated text into words
- pack words into lines up to
width - join lines with
\n
This is intentionally simple but it works well and makes practice text look consistently formatted across terminal sizes.
Usage Examples
Here are a few ways to run TypeGym now:
# Default settings (Markov chain from data/markov.txt)
# Classic random word nonsense
# Weighted nonsense (more natural distribution)
# Read a random slice from a file
# Markov chain from custom text, wrapped to 60 columns
Wrapping Up the Series
At this point TypeGym is a complete little terminal app:
- solid main loop + clean terminal lifecycle
- a real Ratatui typing UI
- working typing logic with timing + stats
- multiple text sources (including Markov)
- and now… a proper CLI interface
Project Code
You will find the complete source code here: typegym