Skip to content

Commit f730887

Browse files
committed
feat: add add command
1 parent 47c7e02 commit f730887

File tree

7 files changed

+163
-11
lines changed

7 files changed

+163
-11
lines changed

Cargo.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "envfetch"
3-
version = "1.2.1"
3+
version = "1.3.0"
44
edition = "2021"
55
authors = ["ANKDDEV"]
66
description = "Lightweight cross-platform CLI tool for working with environment variables"

README.md

+23-4
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
- [x] Set variable (temporary and permanent)
2020
- [x] Delete variable (temporary and permanent)
2121
- [x] Load variables from dotenv-style file (temporary and permanent)
22-
- [ ] Add string to the end of variable
22+
- [x] Add string to the end of variable
2323
- [ ] Set and delete multiple variables at once
2424
# Get started
2525
## Installing
@@ -64,7 +64,7 @@ You can run `envfetch help` to see help message or `envfetch --version` to see p
6464

6565
### Command list
6666
#### Set
67-
Set environment variable and run process.
67+
Set environment variable and optionally run process.
6868

6969
Usage:
7070
`envfetch set <KEY> <VALUE> [PROCESS]`, where:
@@ -83,7 +83,26 @@ For example:
8383
$ envfetch set MY_VAR "Hello" "npm run" # temporary for process
8484
$ envfetch set MY_VAR "Hello" --global # permanent system-wide
8585
```
86+
#### Add
87+
Add value to the end of environment variable and optionally run the process
8688

89+
Usage:
90+
`envfetch add <KEY> <VALUE> [PROCESS]`, where:
91+
- `KEY` - name of environment variable
92+
- `VALUE` - value of environment variable to add
93+
- `PROCESS` - name of process which you want to run (optional if --global is used)
94+
95+
Options:
96+
- `--help`/`-h` - show help message
97+
- `--global`/`-g` - update variable permanently in system environment
98+
- On Windows: stores in registry
99+
- On Unix: stores in shell config (.bashrc, .zshrc, or config.fish)
100+
101+
For example:
102+
```shell
103+
$ envfetch add PATH "../hello.exe" "crago run" # temporary for process
104+
$ envfetch add MY_VAR "Hello" --global # permanent system-wide
105+
```
87106
#### Print
88107
Print all environment variables
89108

@@ -121,7 +140,7 @@ $ envfetch get MY_VAR
121140
```
122141
It will print value of specified variable.
123142
#### Delete
124-
Delete variable and start process.
143+
Delete variable and optionally start process.
125144

126145
Usage:
127146
`envfetch delete <KEY> [PROCESS]`, where:
@@ -139,7 +158,7 @@ $ envfetch delete MY_VAR --global # permanent system-wide
139158
```
140159

141160
#### Load
142-
Load environment variables from dotenv-style file and run process.
161+
Load environment variables from dotenv-style file and optionally run process.
143162

144163
Usage:
145164
`envfetch load [PROCESS]`, where:

src/commands.rs

+19
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,25 @@ pub fn set(args: &SetArgs) {
8383
}
8484
}
8585

86+
/// Add value to environment variable
87+
pub fn add(args: &AddArgs) {
88+
if let Err(err) = validate_var_name(&args.key) {
89+
error(&err);
90+
process::exit(1);
91+
}
92+
93+
let current_value = if let Ok(value) = env::var(&args.key) {
94+
value
95+
} else {
96+
"".to_string()
97+
};
98+
99+
if let Err(err) = variables::set_variable(&args.key, &format!("{}{}", current_value, args.value), args.global, args.process.clone()) {
100+
error(&err);
101+
process::exit(1);
102+
}
103+
}
104+
86105
/// Delete environment variable
87106
pub fn delete(args: &DeleteArgs, exit_on_warning: bool) {
88107
if let Err(err) = validate_var_name(&args.key) {

src/main.rs

+4
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ fn main() {
3434
Commands::Set(ref opt) => {
3535
set(opt);
3636
}
37+
// Add command handler
38+
Commands::Add(ref opt) => {
39+
add(opt);
40+
}
3741
// Delete command handler
3842
Commands::Delete(ref opt) => {
3943
delete(opt, cli.exit_on_warning);

src/models.rs

+24-5
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,17 @@ pub struct Cli {
2626
/// All tool's commands
2727
#[derive(Subcommand)]
2828
pub enum Commands {
29-
/// Prints value of environment variable
29+
/// Print value of environment variable.
3030
Get(GetArgs),
31-
/// Set environment variable and run given process.
31+
/// Set environment variable and optionally run given process.
3232
Set(SetArgs),
33-
/// Delete environment variable and run given process.
33+
/// Add value to the end of environment variable and optionally run given process.
34+
Add(AddArgs),
35+
/// Delete environment variable and optionally run given process.
3436
Delete(DeleteArgs),
35-
/// Load environment variables from dotenv file
37+
/// Load environment variables from dotenv file and optionally run given process.
3638
Load(LoadArgs),
37-
/// Prints all environment variables
39+
/// Print all environment variables.
3840
Print,
3941
}
4042

@@ -81,6 +83,23 @@ pub struct SetArgs {
8183
pub process: Option<String>,
8284
}
8385

86+
/// Args for add command
87+
#[derive(Args, Debug)]
88+
pub struct AddArgs {
89+
/// Environment variable name
90+
#[arg(required = true)]
91+
pub key: String,
92+
/// Value for add to the end of environment variable
93+
#[arg(required = true)]
94+
pub value: String,
95+
/// Globally set variable
96+
#[arg(required = false, long, short)]
97+
pub global: bool,
98+
/// Process to start, not required if --global flag is set
99+
#[arg(required_unless_present = "global")]
100+
pub process: Option<String>,
101+
}
102+
84103
/// Args for delete command
85104
#[derive(Args, Debug)]
86105
pub struct DeleteArgs {

tests/cli.rs

+91
Original file line numberDiff line numberDiff line change
@@ -194,4 +194,95 @@ fn load_custom_file_doesnt_exists() -> Result<(), Box<dyn std::error::Error>> {
194194
Ok(())
195195
}
196196

197+
#[test]
198+
fn test_add_local_variable() -> Result<(), Box<dyn std::error::Error>> {
199+
let envfetch = Command::cargo_bin("envfetch")?.get_program().to_string_lossy().to_string();
200+
let mut cmd = Command::cargo_bin("envfetch")?;
201+
202+
cmd.args([
203+
"add",
204+
"TEST_VAR",
205+
"test_value",
206+
&format!("{} get TEST_VAR", envfetch)
207+
])
208+
.assert()
209+
.success()
210+
.stdout(predicate::str::contains("test_value"));
211+
212+
Ok(())
213+
}
214+
215+
#[test]
216+
fn test_add_variable_with_special_characters() -> Result<(), Box<dyn std::error::Error>> {
217+
let envfetch = Command::cargo_bin("envfetch")?.get_program().to_string_lossy().to_string();
218+
let mut cmd = Command::cargo_bin("envfetch")?;
219+
220+
cmd.args([
221+
"add",
222+
"SPECIAL_VAR",
223+
"test@#$%^&*",
224+
&format!("{} get SPECIAL_VAR", envfetch)
225+
])
226+
.assert()
227+
.success()
228+
.stdout(predicate::str::contains("test@#$%^&*"));
229+
230+
Ok(())
231+
}
232+
233+
#[test]
234+
fn test_add_empty_value() -> Result<(), Box<dyn std::error::Error>> {
235+
let envfetch = Command::cargo_bin("envfetch")?.get_program().to_string_lossy().to_string();
236+
let mut cmd = Command::cargo_bin("envfetch")?;
237+
238+
cmd.args([
239+
"add",
240+
"EMPTY_VAR",
241+
"",
242+
&format!("{} get EMPTY_VAR", envfetch)
243+
])
244+
.assert()
245+
.success()
246+
.stdout(predicate::str::contains("\"\"\n")); // Expect empty string in quotes with newline
247+
248+
Ok(())
249+
}
250+
251+
#[test]
252+
fn test_add_invalid_variable_name() -> Result<(), Box<dyn std::error::Error>> {
253+
let mut cmd = Command::cargo_bin("envfetch")?;
254+
cmd.args(["add", "INVALID NAME", "test_value", "echo test"])
255+
.assert()
256+
.failure()
257+
.stderr(predicates::str::contains("Variable name cannot contain spaces"));
258+
Ok(())
259+
}
260+
261+
#[test]
262+
fn test_add_missing_value() -> Result<(), Box<dyn std::error::Error>> {
263+
let mut cmd = Command::cargo_bin("envfetch")?;
264+
cmd.args(["add", "TEST_VAR"])
265+
.assert()
266+
.failure();
267+
Ok(())
268+
}
269+
270+
#[test]
271+
fn test_add_with_process() -> Result<(), Box<dyn std::error::Error>> {
272+
let envfetch = Command::cargo_bin("envfetch")?.get_program().to_string_lossy().to_string();
273+
let mut cmd = Command::cargo_bin("envfetch")?;
274+
275+
cmd.args([
276+
"add",
277+
"PROCESS_VAR",
278+
"test_value",
279+
&format!("{} get PROCESS_VAR", envfetch)
280+
])
281+
.assert()
282+
.success()
283+
.stdout(predicate::str::contains("test_value"));
284+
285+
Ok(())
286+
}
287+
197288
// TODO: add tests for commands with --global flag

0 commit comments

Comments
 (0)