Skip to content

Commit 86bb7bd

Browse files
author
Rami Chowdhury
committed
Add changes subcommand
For some tooling (e.g. editors, prompts) it's helpful to know what modifications Shadowenv has made, in a short summary. This PR adds a `changes` subcommand that does just that: prints out a summary of the form `+added -removed ^updated`.
1 parent 0e97318 commit 86bb7bd

File tree

3 files changed

+124
-0
lines changed

3 files changed

+124
-0
lines changed

src/changes.rs

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
use std::fmt;
2+
3+
use failure::{Error, Fail};
4+
5+
use crate::undo;
6+
7+
#[derive(Fail, Debug)]
8+
#[fail(display = "Could not output changes")]
9+
pub struct ChangesError;
10+
11+
#[derive(PartialEq, Eq, Debug)]
12+
pub struct Changes {
13+
added: usize,
14+
removed: usize,
15+
updated: usize,
16+
}
17+
18+
impl fmt::Display for Changes {
19+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
20+
write!(
21+
f,
22+
"+{added} -{removed} ^{updated}",
23+
added = self.added,
24+
removed = self.removed,
25+
updated = self.updated
26+
)
27+
}
28+
}
29+
30+
#[derive(Debug)]
31+
enum ChangeType {
32+
Added,
33+
Removed,
34+
Updated,
35+
Unchanged,
36+
}
37+
38+
/// How has this environment variable changed?
39+
fn change_type(v: &undo::Scalar) -> ChangeType {
40+
match (v.original.as_ref(), v.current.as_ref()) {
41+
(None, None) => ChangeType::Unchanged,
42+
(None, Some(_)) => ChangeType::Added,
43+
(Some(_), None) => ChangeType::Removed,
44+
(Some(a), Some(b)) if a == b => ChangeType::Unchanged,
45+
(Some(a), Some(b)) if a == "" && b != "" => ChangeType::Added,
46+
(Some(a), Some(b)) if a != "" && b == "" => ChangeType::Removed,
47+
(Some(a), Some(b)) if a != b => ChangeType::Updated,
48+
(Some(_), Some(_)) => ChangeType::Updated,
49+
}
50+
}
51+
52+
/// Output a simple summary of the changes
53+
pub fn run(shadowenv_data: String) -> Result<Changes, Error> {
54+
let mut parts = shadowenv_data.splitn(2, ':');
55+
let _prev_hash = parts.next();
56+
let json_data = parts.next().unwrap_or("{}");
57+
let shadowenv_data = undo::Data::from_str(json_data).unwrap();
58+
let mut changes = Changes {
59+
added: 0,
60+
removed: 0,
61+
updated: 0,
62+
};
63+
for s in shadowenv_data.scalars {
64+
match change_type(&s) {
65+
ChangeType::Added => changes.added += 1,
66+
ChangeType::Updated => changes.updated += 1,
67+
ChangeType::Removed => changes.removed += 1,
68+
ChangeType::Unchanged => {}
69+
}
70+
}
71+
for l in shadowenv_data.lists {
72+
if l.additions.len() != 0 || l.deletions.len() != 0 {
73+
changes.updated += 1;
74+
}
75+
}
76+
Ok(changes)
77+
}
78+
79+
#[cfg(test)]
80+
mod tests {
81+
use super::*;
82+
83+
#[test]
84+
fn display_test() {
85+
let changes = Changes {
86+
added: 1,
87+
updated: 2,
88+
removed: 3,
89+
};
90+
assert_eq!(changes.to_string(), "+1 -3 ^2",);
91+
}
92+
93+
#[test]
94+
fn nominal_test() {
95+
let data = r#"d6508595be775af2:{"scalars":[{"name":"CLICOLOR","original":"1","current":"0"},{"name":"DEBUG","original":null,"current":"1"},{"name":"GOPATH","original":"/Users/rami/Go","current":""},{"name":"INFOPATH","original":"/opt/homebrew/share/info:","current":""},{"name":"NODE_PATH","original":"/Users/rami/.npm/packages/lib/node_modules:","current":""}],"lists":[{"name":"PATH","additions":["/usr/libexec/bin"],"deletions":[]}]}"#;
96+
let result = run(data.to_string());
97+
98+
let expected: Changes = Changes {
99+
added: 1,
100+
updated: 2,
101+
removed: 3,
102+
};
103+
assert_eq!(result.unwrap(), expected);
104+
}
105+
}

src/cli.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,4 +148,9 @@ pub fn app() -> App<'static, 'static> {
148148
.about("Prints a script which can be eval'd by fish to set up shadowenv."),
149149
)
150150
)
151+
.subcommand(
152+
SubCommand::with_name("change-count")
153+
.about("Show the number of variables set / updated. Useful for tooling e.g. IDEs, prompts.")
154+
.setting(AppSettings::DisableHelpSubcommand)
155+
)
151156
}

src/main.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
mod changes;
12
mod cli;
23
mod diff;
34
mod execcmd;
@@ -83,6 +84,19 @@ fn main() {
8384
let shellname = matches.subcommand_name().unwrap();
8485
process::exit(init::run(shellname));
8586
}
87+
("changes", Some(matches)) => {
88+
let legacy_fallback_data = matches.value_of("$__shadowenv_data").map(|d| d.to_string());
89+
let data = Shadowenv::load_shadowenv_data_or_legacy_fallback(legacy_fallback_data);
90+
match changes::run(data) {
91+
Err(err) => {
92+
eprintln!("{}", err);
93+
process::exit(1);
94+
}
95+
Ok(changes) => {
96+
print!("{}", changes);
97+
}
98+
}
99+
}
86100
_ => {
87101
panic!("subcommand was required by config but somehow none was provided");
88102
}

0 commit comments

Comments
 (0)