Skip to content

Commit f962cc7

Browse files
authored
General CLI overhaul (#44)
* rewrite clap opts to use subcommands * rewrite using match * handle default destination from clap argument * cleanup braces * remove cmd option, force stat default * handle cd -f together with the regular cd * subcommand shorthands * add short status option * added plain status option * improve the help * fix ci test * refactor location.go_to and add game.visit * preserve destination side-effects on cd -f * set quiet/plain as (still unused) globals * global quiet/plain logging with once_cell * use quiet/plain in battle * quiet/plain should be global commands * more output cleanup * improve tombstone output * another tombstone improvement * no battles at home * true up readme examples * update changelog * use cd -f for ci to prevent error exit code * add --force to cargo install example * remove redundant help * clap help improvements * format
1 parent fd79c84 commit f962cc7

11 files changed

Lines changed: 249 additions & 187 deletions

File tree

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ script:
2323
- cargo +nightly clippy
2424
- cargo fmt -- --check
2525
- cargo run
26-
- cargo run -- .
26+
- cargo run -- cd -f .
2727

2828
before_deploy:
2929
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then export TARGET=x86_64-unknown-linux-musl ; fi

CHANGELOG.md

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,18 @@
22

33
## Unreleased
44
### Added
5-
* --battle initiates a battle (with a probability) at the hero's current location b349362
6-
* --mv sets the hero location without initiating battles, intended for custom shell integrations 7ab2401
7-
* --stat,-s prints the hero status 8da1090
8-
* This Changelog
5+
* This Changelog
6+
* `rpg cd -f` sets the hero location without initiating battles, intended for custom shell integrations
7+
* `rpg battle` initiates a battle (with a probability) at the hero's current location.
8+
* --quiet,-q option to reduce output while changing directories and printing the hero status.
9+
* --plain to facilitate scripting around the hero stats.
10+
* Documentation for shell integrations.
911

1012
### Changed
11-
* `rpg` without args moves the hero to home and `rpg -` moves it to `$OLDPWD` (when present) to match the `cd` behavior 4ba4c59
12-
* --shop,-s renamed to --buy,-b and --inventory,-i renamed to --use,-u f737a81
13-
* always print hero location on cd command 05b661e
13+
* General command overhaul, now all actions are done via a subcommand: `rpg cd`, `rpg stat`, etc., with status printing being the default.
14+
* `rpg cd` without args moves the hero to home and `rpg cd -` moves it to `$OLDPWD` (when present) to match the `cd` behavior 4ba4c59
15+
* --shop,-s renamed to buy,b and --inventory,-i renamed to use,u f737a81
16+
* Removed most empty lines from output.
1417

1518
## [0.3.0](https://github.com/facundoolano/rpg-cli/releases/tag/0.2.0) - 2021-05-28
1619
### Added

Cargo.lock

Lines changed: 9 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ colored = "2"
1515
clap = "3.0.0-beta.2"
1616
typetag = "0.1"
1717
dunce = "1.0.1"
18+
once_cell = "1.7.2"

README.md

Lines changed: 15 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ Just download the binary for your platform (linux/macOS/windows) from the [GitHu
2222
### Installing with Cargo
2323
Assuming you have [Rust and Cargo installed](https://doc.rust-lang.org/cargo/getting-started/installation.html#install-rust-and-cargo):
2424

25-
$ cargo install --git https://github.com/facundoolano/rpg-cli --branch main
25+
$ cargo install --git https://github.com/facundoolano/rpg-cli --force --branch main
2626

2727
The binary should be available as `rpg-cli` (assuming you have `~/.cargo/bin` in your `$PATH`).
2828

@@ -39,16 +39,16 @@ The most basic type of integration consists in wrapping rpg-cli in a shell funct
3939
```sh
4040
rpg () {
4141
rpg-cli "$@"
42-
cd "$(rpg-cli --pwd)"
42+
cd "$(rpg-cli pwd)"
4343
}
4444
```
4545

4646
Or, if you want to go all the way and *really* use it in place of `cd`:
4747

4848
```sh
4949
cd () {
50-
rpg-cli "$@"
51-
builtin cd "$(rpg-cli --pwd)"
50+
rpg-cli cd "$@"
51+
builtin cd "$(rpg-cli pwd)"
5252
}
5353
```
5454

@@ -65,7 +65,6 @@ This example session assumes a basic `rpg` function as described in the previous
6565
The first time you run the program, a new hero is created at the user's home directory.
6666

6767
~ $ rpg
68-
6968
hero[1]@home
7069
hp:[xxxxxxxxxx] 25/25
7170
xp:[----------] 0/30
@@ -76,9 +75,8 @@ The first time you run the program, a new hero is created at the user's home dir
7675

7776
When running without parameters, as above, the hero status is printed (health points, accumulated experience, etc.). If you pass in a path as parameter, that will instruct the hero to move:
7877

79-
~ $ rpg dev/
78+
~ $ rpg cd dev/
8079
~/dev $ rpg
81-
8280
hero[1]@~/dev
8381
hp:[xxxxxxxxxx] 25/25
8482
xp:[----------] 0/30
@@ -90,38 +88,34 @@ When running without parameters, as above, the hero status is printed (health po
9088
In this case, the hero moved to `~/dev/facundoolano`. Sometimes enemies will appear as you move through the directories,
9189
and both characters will engage in battle:
9290

93-
~/dev $ rpg facundoolano/
94-
91+
~/dev $ rpg cd facundoolano/
9592
snake[1][xxxx]@~/dev/facundoolano
96-
9793
snake[1][x---] -12hp
9894
hero[1][xxxx] dodged!
9995
snake[1][----] -12hp
100-
101-
hero[1][xxxx][xxxx]@~/dev/facundoolano +24xp +75g
96+
hero[1][xxxx] +24xp +75g
97+
hero[1][xxxx][xxxx]@~/dev/facundoolano
10298

10399
Each character attacks in turn (the frequency being determined by their `speed` stat).
104100
Whenever you win a fight, your hero gains experience points and eventually raises its level, along with their other stats.
105101

106102
When you return to the home directory, the hero's health points are restored:
107103

108-
~/dev/facundoolano/rpg-cli $ rpg ~
104+
~/dev/facundoolano/rpg-cli $ rpg cd ~
109105
hero[1][xxxx][xxxx]@home +20hp
110106

111107
Also at the home directory, you can buy items and equipment:
112108

113-
~ $ rpg --shop
114-
109+
~ $ rpg buy
115110
sword[1] 500g
116111
shield[1] 500g
117112
potion[1] 200g
118113
escape 1000g
119114

120115
funds: 275g
121116

122-
~ $ rpg --shop potion
117+
~ $ rpg buy potion
123118
~ $ rpg
124-
125119
hero[3]@home
126120
hp:[xxxxxxxxxx] 37/37
127121
xp:[xx--------] 19/155
@@ -130,29 +124,22 @@ Also at the home directory, you can buy items and equipment:
130124
item:{potion[1]x1}
131125
75g
132126

133-
The shortcut `rpg -s p` would also work above.
127+
The shortcut `rpg b p` would also work above.
134128

135129
The further from home you move the hero, the tougher the enemies will get. If you go to far or too long without restoring your health your hero is likely to die in battle, causing the game to restart.
136130

137-
~ $ rpg ~/dev/facundoolano/rpg-cli/target/debug/examples/
138-
131+
~ $ rpg cd ~/dev/facundoolano/rpg-cli/target/debug/examples/
139132
orc[1][xxxx]@~/dev/facundoolano/rpg-cli
140-
141133
hero[1][x---] -20hp critical!
142134
orc[1][xxx-] -9hp
143135
hero[1][----] -16hp
144-
145-
hero[1][----][----]@~/dev/facundoolano/rpg-cli 💀
136+
hero[1][----] 💀
146137

147138
Death is permanent: you can't save your progress and reload after dying, but if you take your new hero to the location of the previous one's death,
148139
you can recover gold, items and equipment:
149140

150141
~ $ rpg ~/dev/facundoolano/rpg-cli/
151-
152-
🪦 @~/dev/facundoolano/rpg-cli/
153-
154-
+potionx1
155-
+75g
142+
hero[🪦]@~/dev/facundoolano/rpg-cli/ +potionx1 +75g
156143

157144

158145
Try `rpg --help` for more options.

shell/README.md

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ you need to write a function so the working directory is changed to match that o
1313
```sh
1414
rpg () {
1515
rpg-cli "$@"
16-
cd "$(rpg-cli --pwd)"
16+
cd "$(rpg-cli pwd)"
1717
}
1818
```
1919

@@ -24,7 +24,7 @@ If you use fish shell, update `~/.config/fish/config.fish` instead:
2424
```fish
2525
function rpg
2626
rpg-cli $argv
27-
cd (rpg-cli --pwd)
27+
cd (rpg-cli pwd)
2828
end
2929
```
3030

@@ -34,18 +34,20 @@ If you like having enemies popping up while using `cd`, you can override that in
3434

3535
```sh
3636
cd () {
37-
rpg-cli "$@"
38-
builtin cd "$(rpg-cli --pwd)"
37+
rpg-cli cd "$@"
38+
builtin cd "$(rpg-cli pwd)"
3939
}
4040
```
4141

4242
## Custom integration commands
4343

4444
To better adapt for different usage patterns, finer-grained commands are provided:
4545

46-
* `rpg-cli --mv <path>` will set the hero's location to `<path>` without initiating battles.
47-
* `rpg-cli --pwd` will print the hero's current location.
48-
* `rpg-cli --battle` will initiate a battle with a probability that changes based on the distance from home. If the battle is lost the exit code of the program will be non-negative.
46+
* `rpg-cli cd --force <path>` will set the hero's location to `<path>` without initiating battles.
47+
* `rpg-cli pwd` will print the hero's current location.
48+
* `rpg-cli battle` will initiate a battle with a probability that changes based on the distance from home. If the battle is lost the exit code of the program will be non-negative.
49+
* `rpg-cli stat --quiet` will return hero stats in a succinct format.
50+
* `rpg-cli stat --plain` will return hero stats as tab separated fields, to facilitate parsing (e.g. to integrate to the prompt).
4951

5052
## Prevent intermediate battles
5153

@@ -60,8 +62,8 @@ A better alternative for this usage pattern is enabled by the other integration
6062
```sh
6163
cd () {
6264
builtin cd "$@"
63-
rpg-cli --mv .
64-
rpg-cli --battle
65+
rpg-cli cd -f .
66+
rpg-cli battle
6567
}
6668
```
6769

@@ -70,7 +72,7 @@ cd () {
7072
Another way to use rpg-cli is to initiate battles when attempting to execute file-modifying operations. Only when the battle is won the operation is allowed:
7173

7274
```sh
73-
alias rpg-battle="rpg-cli --mv . && rpg-cli --battle"
75+
alias rpg-battle="rpg-cli cd -f . && rpg-cli battle"
7476

7577
alias rm="rpg-battle && rm"
7678
alias rmdir="rpg-battle && rmdir"

src/game/mod.rs

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -72,23 +72,30 @@ impl Game {
7272
/// at a time, with some chance of enemies appearing on each one.
7373
pub fn go_to(&mut self, dest: &Location, run: bool, bribe: bool) -> Result<(), Error> {
7474
while self.location != *dest {
75-
self.location.go_to(&dest);
76-
if self.location.is_home() {
77-
self.visit_home();
78-
} else if self.pick_up_tombstone() {
79-
return Ok(());
80-
} else if let Some(mut enemy) = self.maybe_spawn_enemy() {
81-
return self.maybe_battle(&mut enemy, run, bribe);
75+
self.visit(self.location.go_to(&dest));
76+
77+
if !self.location.is_home() {
78+
if let Some(mut enemy) = self.maybe_spawn_enemy() {
79+
return self.maybe_battle(&mut enemy, run, bribe);
80+
}
8281
}
8382
}
8483
Ok(())
8584
}
8685

86+
/// Set the hero's location to the one given, and apply related side effects.
87+
pub fn visit(&mut self, location: Location) {
88+
self.location = location;
89+
if self.location.is_home() {
90+
let recovered = self.player.heal_full();
91+
log::heal(&self.player, &self.location, recovered);
92+
}
93+
self.pick_up_tombstone();
94+
}
95+
8796
/// Set the current location to home, and apply related side-effects
8897
pub fn visit_home(&mut self) {
89-
self.location = Location::home();
90-
let recovered = self.player.heal_full();
91-
log::heal(&self.player, &self.location, recovered);
98+
self.visit(Location::home());
9299
}
93100

94101
pub fn add_item(&mut self, name: &str, item: Box<dyn Item>) {
@@ -128,7 +135,6 @@ impl Game {
128135
/// If there's a tombstone laying in the current location, pick up its items
129136
fn pick_up_tombstone(&mut self) -> bool {
130137
if let Some(mut tombstone) = self.tombstones.remove(&self.location) {
131-
log::tombstone_found(&self.location);
132138
tombstone.pick_up(self);
133139
true
134140
} else {
@@ -194,7 +200,7 @@ impl Game {
194200
self.gold += gold;
195201
let level_up = self.player.add_experience(xp);
196202

197-
log::battle_won(&self.player, xp, level_up, gold);
203+
log::battle_won(&self, xp, level_up, gold);
198204
Ok(())
199205
} else {
200206
// leave hero items in the location

src/game/tombstone.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ impl Tombstone {
6363

6464
game.gold += self.gold;
6565

66-
log::tombstone_items(&to_log, self.gold);
66+
log::tombstone(&game.location, &to_log, self.gold);
6767
}
6868
}
6969

src/location.rs

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -39,20 +39,15 @@ impl Location {
3939
self.path == dirs::home_dir().unwrap()
4040
}
4141

42-
/// Move this location one step towards the given destination
43-
pub fn go_to(&mut self, dest: &Self) {
44-
if !dest.path.starts_with(&self.path) {
45-
self.path = self.path.parent().unwrap().to_path_buf();
46-
} else if dest != self {
47-
let next = dest
48-
.path
49-
.strip_prefix(&self.path)
50-
.unwrap()
51-
.components()
52-
.next()
53-
.unwrap();
54-
self.path = self.path.join(next);
55-
}
42+
/// Return a new location that it's one dir closer to the given destination.
43+
pub fn go_to(&self, dest: &Self) -> Self {
44+
let next = if !dest.path.starts_with(&self.path) {
45+
self.path.parent().unwrap().to_path_buf()
46+
} else {
47+
let self_len = self.path.components().count();
48+
dest.path.components().take(self_len + 1).collect()
49+
};
50+
Self { path: next }
5651
}
5752

5853
fn distance_from(&self, other: &Self) -> Distance {
@@ -159,28 +154,28 @@ mod tests {
159154

160155
#[test]
161156
fn test_walk_towards() {
162-
let mut source = location_from("/Users/facundo/dev/");
157+
let source = location_from("/Users/facundo/dev/");
163158
let dest = location_from("/");
164159

165-
source.go_to(&dest);
160+
let source = source.go_to(&dest);
166161
assert_eq!(location_from("/Users/facundo/"), source);
167-
source.go_to(&dest);
162+
let source = source.go_to(&dest);
168163
assert_eq!(location_from("/Users/"), source);
169-
source.go_to(&dest);
164+
let source = source.go_to(&dest);
170165
assert_eq!(location_from("/"), source);
171-
source.go_to(&dest);
166+
let source = source.go_to(&dest);
172167
assert_eq!(location_from("/"), source);
173168

174-
let mut source = location_from("/Users/facundo/rust/rpg");
169+
let source = location_from("/Users/facundo/rust/rpg");
175170
let dest = location_from("/Users/facundo/erlang/app");
176171

177-
source.go_to(&dest);
172+
let source = source.go_to(&dest);
178173
assert_eq!(location_from("/Users/facundo/rust/"), source);
179-
source.go_to(&dest);
174+
let source = source.go_to(&dest);
180175
assert_eq!(location_from("/Users/facundo/"), source);
181-
source.go_to(&dest);
176+
let source = source.go_to(&dest);
182177
assert_eq!(location_from("/Users/facundo/erlang"), source);
183-
source.go_to(&dest);
178+
let source = source.go_to(&dest);
184179
assert_eq!(location_from("/Users/facundo/erlang/app"), source);
185180
}
186181

0 commit comments

Comments
 (0)