Skip to content

Commit b5951f5

Browse files
committed
improv: workspaces are more user friendly
1 parent a503bd4 commit b5951f5

11 files changed

Lines changed: 151 additions & 124 deletions

File tree

src/cli/src/cli.rs

Lines changed: 77 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use clap::{Parser, Subcommand};
22
use devmode::action::Action;
3-
use devmode::{DevmodeStatus, Error};
3+
use devmode::config::Config;
4+
use devmode::workspace::Workspace;
5+
use devmode::{DevmodeError, Error};
46
use fs_extra::{dir, move_items};
57
use libset::routes::home;
68
use regex::bytes::Regex;
@@ -111,8 +113,10 @@ pub enum Commands {
111113
arg_required_else_help = true
112114
)]
113115
Workspace {
114-
#[clap(help = "Name for the workspace.")]
116+
#[clap(help = "The name of the workspace")]
115117
name: Option<String>,
118+
#[clap(help = "Add a workspace", short = 'a', long = "add")]
119+
add: bool,
116120
#[clap(help = "Delete a workspace", short = 'd', long = "delete")]
117121
delete: bool,
118122
#[clap(
@@ -123,12 +127,12 @@ pub enum Commands {
123127
)]
124128
rename: Option<String>,
125129
#[clap(
126-
help = "Add a repo to a workspace",
127-
short = 'a',
128-
long = "add",
130+
help = "Include a repo in a workspace",
131+
short = 'i',
132+
long = "include",
129133
takes_value = true
130134
)]
131-
add: Option<String>,
135+
include: Option<String>,
132136
#[clap(
133137
help = "Remove a repo from a workspace",
134138
short = 'm',
@@ -156,30 +160,32 @@ impl Cli {
156160
editor,
157161
owner,
158162
host,
159-
} => Cli::config(
160-
*map,
161-
*show,
162-
*all,
163-
*editor,
164-
*owner,
165-
*host,
166-
!map && !show && !*all && !*editor && !*owner && !*host,
167-
),
163+
} => Cli::config(Config {
164+
map: *map,
165+
show: *show,
166+
all: *all,
167+
editor: *editor,
168+
owner: *owner,
169+
host: *host,
170+
none: !map && !show && !all && !editor && !owner && !host,
171+
}),
168172
Commands::Workspace {
169173
name,
174+
add,
170175
delete,
171176
rename,
172-
add,
177+
include,
173178
remove,
174179
list,
175-
} => Cli::workspace(
176-
name.to_owned(),
177-
*delete,
178-
rename.clone(),
179-
add.clone(),
180-
remove.clone(),
181-
*list,
182-
),
180+
} => Cli::workspace(Workspace {
181+
name: name.to_owned(),
182+
add: *add,
183+
delete: *delete,
184+
rename: rename.to_owned(),
185+
include: include.to_owned(),
186+
remove: remove.to_owned(),
187+
list: *list,
188+
}),
183189
}
184190
}
185191

@@ -190,7 +196,7 @@ impl Cli {
190196
clone_setup()?
191197
} else if Settings::current().is_some() && args.len() == 1 {
192198
let Some(options) = Settings::current() else {
193-
return devmode::error("");
199+
return Err(Error::Devmode(DevmodeError::AppSettingsNotFound));
194200
};
195201

196202
url.set_host(Host::from(&options.host).url())
@@ -204,7 +210,7 @@ impl Cli {
204210
if let Some(url) = args.get(0) {
205211
CloneAction::new(url)
206212
} else {
207-
return devmode::error("No URL provided");
213+
return Err(Error::Devmode(DevmodeError::NoUrlProvided));
208214
}
209215
} else if args.len() == 3 {
210216
if let Some(host) = args.get(0) {
@@ -218,7 +224,7 @@ impl Cli {
218224
}
219225
CloneAction::new(&url.build())
220226
} else {
221-
return devmode::error("The command was invalid");
227+
return Err(Error::Devmode(DevmodeError::InvalidCommand));
222228
};
223229
if let Some(workspace) = workspace {
224230
clone.set_workspace(workspace);
@@ -245,7 +251,7 @@ impl Cli {
245251
let reader = create_paths_reader()?;
246252
let paths = find_paths(reader, project)?;
247253
if paths.is_empty() {
248-
return Err(Error::String(DevmodeStatus::NoProjectFound.to_string()));
254+
return Err(Error::Devmode(DevmodeError::NoProjectFound));
249255
} else if paths.len() > 1 {
250256
let paths: Vec<&str> = paths.iter().map(|s| s as &str).collect();
251257
let path = select_repo(paths)?.to_string();
@@ -258,7 +264,7 @@ impl Cli {
258264
let reader = create_paths_reader()?;
259265
let paths = find_paths(reader, project)?;
260266
if paths.is_empty() {
261-
return Err(Error::String(DevmodeStatus::NoProjectFound.to_string()));
267+
return Err(Error::Devmode(DevmodeError::NoProjectFound));
262268
} else if paths.len() > 1 {
263269
let paths: Vec<&str> = paths.iter().map(|s| s as &str).collect();
264270
let path = select_repo(paths)?;
@@ -274,9 +280,8 @@ impl Cli {
274280
} else if rx.is_match(args.get(0).unwrap().as_bytes()) {
275281
ForkAction::parse_url(args.get(0).unwrap(), rx, upstream.to_string())?
276282
} else if args.len() == 1 {
277-
let options = Settings::current().ok_or(Error::String(
278-
DevmodeStatus::AppSettingsNotFound.to_string(),
279-
))?;
283+
let options =
284+
Settings::current().ok_or(Error::Devmode(DevmodeError::AppSettingsNotFound))?;
280285
let host = Host::from(&options.host);
281286
let repo = args
282287
.get(0)
@@ -297,63 +302,49 @@ impl Cli {
297302
};
298303
action.run()
299304
}
300-
fn config(
301-
map: bool,
302-
show: bool,
303-
all: bool,
304-
editor: bool,
305-
owner: bool,
306-
host: bool,
307-
none: bool,
308-
) -> Result<(), Error> {
309-
if all || none {
305+
fn config(config: Config) -> Result<(), Error> {
306+
if config.all || config.none {
310307
if get_settings().is_err() {
311308
println!("First time setup! 🥳\n");
312309
Settings::init()?;
313310
}
314311
let settings = config_all()?;
315312
settings.write(false)?;
316313
}
317-
if map {
314+
if config.map {
318315
OpenAction::make_dev_paths()?
319316
}
320-
if editor {
317+
if config.editor {
321318
let settings = config_editor()?;
322319
settings.write(false)?
323320
}
324-
if owner {
321+
if config.owner {
325322
let settings = config_owner()?;
326323
settings.write(false)?
327324
}
328-
if host {
325+
if config.host {
329326
let settings = config_host()?;
330327
settings.write(false)?
331328
}
332-
if show {
329+
if config.show {
333330
let settings = get_settings()?;
334331
settings.show();
335332
}
336333
Ok(())
337334
}
338-
fn workspace(
339-
name: Option<String>,
340-
delete: bool,
341-
rename: Option<String>,
342-
add: Option<String>,
343-
remove: Option<String>,
344-
list: bool,
345-
) -> Result<(), Error> {
335+
336+
fn workspace(workspace: Workspace) -> Result<(), Error> {
346337
let mut settings =
347-
Settings::current().ok_or(Error::Generic("Failed to get configuration"))?;
348-
if let Some(name) = name {
338+
Settings::current().ok_or(Error::Devmode(DevmodeError::AppSettingsNotFound))?;
339+
if let Some(name) = workspace.name {
349340
if settings.workspaces.names.contains(&name) {
350341
let index = settings
351342
.workspaces
352343
.names
353344
.iter()
354345
.position(|ws| *ws == name)
355346
.unwrap();
356-
if delete {
347+
if workspace.delete {
357348
let dev = home().join("Developer");
358349
for provider in fs::read_dir(dev)? {
359350
for user in fs::read_dir(provider?.path())? {
@@ -381,7 +372,7 @@ impl Cli {
381372
settings.workspaces.names.remove(index);
382373
settings.write(true)?;
383374
println!("Workspace {name} was successfully deleted.")
384-
} else if rename.is_some() {
375+
} else if workspace.rename.is_some() {
385376
let dev = home().join("Developer");
386377
for provider in fs::read_dir(dev)? {
387378
for user in fs::read_dir(provider?.path())? {
@@ -397,18 +388,22 @@ impl Cli {
397388
.path()
398389
.parent()
399390
.unwrap()
400-
.join(rename.clone().unwrap()),
391+
.join(workspace.rename.clone().unwrap()),
401392
)?;
402393
}
403394
}
404395
}
405396
}
406-
*settings.workspaces.names.get_mut(index).unwrap() = rename.clone().unwrap();
397+
*settings.workspaces.names.get_mut(index).unwrap() =
398+
workspace.rename.clone().unwrap();
407399
settings.write(true)?;
408-
println!("Workspace renamed from {name} to {}.", rename.unwrap());
409-
} else if let Some(add) = add {
400+
println!(
401+
"Workspace renamed from {name} to {}.",
402+
workspace.rename.unwrap()
403+
);
404+
} else if let Some(include) = workspace.include {
410405
let reader = create_paths_reader()?;
411-
let paths: Vec<String> = find_paths(reader, &add)?
406+
let paths: Vec<String> = find_paths(reader, &include)?
412407
.iter()
413408
.map(|path| path.to_owned())
414409
.filter(|path| !path.contains(name.as_str()))
@@ -423,7 +418,7 @@ impl Cli {
423418
};
424419
let mut options = dir::CopyOptions::new();
425420
let to = PathBuf::from(&path).parent().unwrap().join(&name);
426-
if to.join(add.as_str()).exists() {
421+
if to.join(include.as_str()).exists() {
427422
let question = requestty::Question::confirm("overwrite")
428423
.message("We found an existing repository with the same name, do you want to overwrite the existing repository?")
429424
.build();
@@ -437,7 +432,7 @@ impl Cli {
437432
} else {
438433
move_items(&[path], to, &options)?;
439434
}
440-
} else if let Some(remove) = remove {
435+
} else if let Some(remove) = workspace.remove {
441436
let reader = create_paths_reader()?;
442437
let paths: Vec<String> = find_paths(reader, &remove)?
443438
.iter()
@@ -474,14 +469,26 @@ impl Cli {
474469
} else {
475470
println!("Workspace `{name}` found.");
476471
}
477-
} else if delete || rename.is_some() {
472+
} else if workspace.delete || workspace.rename.is_some() {
478473
return devmode::error("Couldn't find a workspace that matches {name}.");
479-
} else {
474+
} else if workspace.add {
480475
settings.workspaces.names.push(name.clone());
481476
settings.write(true)?;
482477
println!("Workspace {name} was added.")
478+
} else {
479+
let question = requestty::Question::confirm("workspace")
480+
.message("Would you like to create this workspace?")
481+
.build();
482+
let answer = requestty::prompt_one(question)?;
483+
if let Answer::Bool(create) = answer {
484+
if create {
485+
settings.workspaces.names.push(name.clone());
486+
settings.write(true)?;
487+
println!("Workspace {name} was added.")
488+
}
489+
}
483490
}
484-
} else if list {
491+
} else if workspace.list {
485492
let workspaces = settings.workspaces.names;
486493
println!("Currently available workspaces: {workspaces:?}",);
487494
}
@@ -490,7 +497,5 @@ impl Cli {
490497
}
491498

492499
fn get_settings() -> Result<Settings, Error> {
493-
Settings::current().ok_or(Error::String(
494-
DevmodeStatus::AppSettingsNotFound.to_string(),
495-
))
500+
Settings::current().ok_or(Error::Devmode(DevmodeError::AppSettingsNotFound))
496501
}

src/cli/src/input.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use devmode::editor::Editor;
55
use devmode::fork::ForkAction;
66
use devmode::host::Host;
77
use devmode::settings::Settings;
8+
use devmode::DevmodeError;
89
use devmode::{application::Application, Error};
910
use requestty::{Answer, Question};
1011
use url_builder::URLBuilder;
@@ -43,7 +44,7 @@ pub fn clone_setup() -> Result<CloneAction, Error> {
4344

4445
let mut clone = CloneAction::new(&url.build());
4546

46-
let settings = Settings::current().ok_or(Error::Generic("Failed to get configuration"))?;
47+
let settings = Settings::current().ok_or(Error::Devmode(DevmodeError::AppSettingsNotFound))?;
4748
let mut options: Vec<&str> = settings
4849
.workspaces
4950
.names

src/cli/src/main.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ fn main() -> Result<(), Error> {
1010
start_logger();
1111
let cli = Cli::parse();
1212
if let Err(e) = cli.run() {
13-
log::error!("{}", e)
13+
match e {
14+
Error::Devmode(error) => println!("{error}"),
15+
_ => log::error!("{}", e),
16+
}
1417
}
1518
Ok(())
1619
}

src/config.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#[derive(Debug)]
2+
pub struct Config {
3+
pub map: bool,
4+
pub show: bool,
5+
pub all: bool,
6+
pub editor: bool,
7+
pub owner: bool,
8+
pub host: bool,
9+
pub none: bool,
10+
}

src/error.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ use thiserror::Error;
44

55
#[derive(Error, Debug)]
66
pub enum Error {
7+
#[error("Devmode error: {0}")]
8+
Devmode(#[from] DevmodeError),
79
#[error("Argument parsing error: {0}")]
810
Parse(#[from] clap::Error),
911
#[error("IO error: {0}")]
@@ -29,3 +31,25 @@ pub enum Error {
2931
pub fn error<T>(msg: &'static str) -> Result<T, Error> {
3032
Err(Error::Generic(msg))
3133
}
34+
35+
#[derive(Error, Debug)]
36+
pub enum DevmodeError {
37+
#[error("No project found.")]
38+
NoProjectFound,
39+
#[error("No settings were changed.")]
40+
NoUrlProvided,
41+
#[error("Invalid command.")]
42+
InvalidCommand,
43+
#[error("The current app options could not be found.\nRun `dm cf --all` to reconfigure them")]
44+
AppSettingsNotFound,
45+
#[error("Failed to write settings")]
46+
FailedToWriteSettings,
47+
#[error("Failed to parse settings")]
48+
FailedToParseSettings,
49+
#[error("Failed to clone repository")]
50+
FailedToCloneRepository,
51+
#[error("Failed to set remote repository")]
52+
FailedToSetRemote,
53+
#[error("Failed to get branch")]
54+
FailedToGetBranch,
55+
}

0 commit comments

Comments
 (0)