wekan_cli/commands/
profile.rs1mod add;
2mod list;
3mod remove;
4mod show;
5mod update;
6mod use_profile;
7
8#[cfg(test)]
9mod tests;
10
11use clap::{Args, Subcommand};
12
13pub use self::{
14 add::AddArgs, list::ListArgs, remove::RemoveArgs, show::ShowArgs, update::UpdateArgs,
15 use_profile::UseArgs,
16};
17use crate::{
18 command_result::CommandSuccess,
19 config::profiles::ProfileStore,
20 credentials::{CredentialError, CredentialStore, CredentialTarget},
21 error::{AppError, ErrorCode, ErrorDetails},
22 exit_code::StableExitCode,
23 input::ConfirmationProvider,
24};
25
26#[derive(Debug, Args)]
27pub struct ProfileArgs {
28 #[command(subcommand)]
29 pub command: ProfileCommand,
30}
31
32#[derive(Debug, Subcommand)]
33pub enum ProfileCommand {
34 Add(AddArgs),
36 List(ListArgs),
38 Show(ShowArgs),
40 Use(UseArgs),
42 Update(UpdateArgs),
44 Remove(RemoveArgs),
46}
47
48pub(crate) fn dispatch(
49 command: ProfileCommand,
50 profile_store: &dyn ProfileStore,
51 credential_store: &dyn CredentialStore,
52 confirmation: &dyn ConfirmationProvider,
53) -> Result<CommandSuccess, AppError> {
54 match command {
55 ProfileCommand::Add(args) => {
56 let name = args.name.clone();
57 with_profile_context(name, add::execute(args, profile_store))
58 }
59 ProfileCommand::List(args) => list::execute(args, profile_store),
60 ProfileCommand::Show(args) => {
61 let name = args.name.clone();
62 with_profile_context(name, show::execute(args, profile_store))
63 }
64 ProfileCommand::Use(args) => {
65 let name = args.name.clone();
66 with_profile_context(name, use_profile::execute(args, profile_store))
67 }
68 ProfileCommand::Update(args) => {
69 let name = args.name.clone();
70 with_profile_context(name, update::execute(args, profile_store, credential_store))
71 }
72 ProfileCommand::Remove(args) => {
73 let name = args.name.clone();
74 with_profile_context(
75 name,
76 remove::execute(args, profile_store, credential_store, confirmation),
77 )
78 }
79 }
80}
81
82fn with_profile_context(
83 name: String,
84 result: Result<CommandSuccess, AppError>,
85) -> Result<CommandSuccess, AppError> {
86 result.map_err(|error| error.with_profile_context(name))
87}
88
89fn ensure_logged_out(
90 credential_store: &dyn CredentialStore,
91 name: &str,
92 server: &str,
93 credential_namespace: &str,
94) -> Result<(), AppError> {
95 let target = CredentialTarget::profile_in_store(name, credential_namespace, server.to_owned());
96 match credential_store.exists(&target) {
97 Ok(false) => Ok(()),
98 Ok(true) => Err(profile_error(
99 ErrorCode::ProfileHasCredential,
100 name,
101 format!(
102 "profile `{name}` has a stored credential; run `wekan --profile {name} auth logout` or `wekan --profile {name} auth logout --local-only` first"
103 ),
104 )),
105 Err(CredentialError::Unavailable(message)) => Err(AppError::new(
106 ErrorCode::CredentialStoreUnavailable,
107 format!("the operating-system credential store is unavailable: {message}"),
108 StableExitCode::Credential,
109 )
110 .with_profile_context(name.to_owned())),
111 Err(error) => Err(AppError::new(
112 ErrorCode::CredentialStoreFailed,
113 format!("the stored credential could not be checked: {error}"),
114 StableExitCode::Credential,
115 )
116 .with_profile_context(name.to_owned())),
117 }
118}
119
120fn profile_not_found(name: &str) -> AppError {
121 profile_error(
122 ErrorCode::ProfileNotFound,
123 name,
124 format!("profile `{name}` does not exist"),
125 )
126}
127
128fn profile_error(code: ErrorCode, name: &str, message: String) -> AppError {
129 AppError::new(code, message, StableExitCode::Configuration).with_details(ErrorDetails {
130 profile: Some(name.to_owned()),
131 ..ErrorDetails::default()
132 })
133}