Files
AstralRinth/theseus_cli/src/subcommands/user.rs
Geometrically f0b8a708a3 Updating + Profile Repairs + Performance Improvements (#97)
* repairing

* Main framework for updating

* add jsconfig

* more work

* Improve performance

* Finish updating

* run lint
2023-04-26 10:28:08 -07:00

178 lines
4.3 KiB
Rust

//! User management subcommand
use crate::util::{confirm_async, table};
use eyre::Result;
use paris::*;
use tabled::Tabled;
use theseus::prelude::*;
use tokio::sync::oneshot;
#[derive(argh::FromArgs, Debug)]
#[argh(subcommand, name = "user")]
/// manage Minecraft accounts
pub struct UserCommand {
#[argh(subcommand)]
action: UserSubcommand,
}
#[derive(argh::FromArgs, Debug)]
#[argh(subcommand)]
pub enum UserSubcommand {
Add(UserAdd),
List(UserList),
Remove(UserRemove),
SetDefault(UserDefault),
}
#[derive(argh::FromArgs, Debug)]
/// add a new user to Theseus
#[argh(subcommand, name = "add")]
pub struct UserAdd {
#[argh(option)]
/// the browser to authenticate using
browser: Option<webbrowser::Browser>,
}
impl UserAdd {
pub async fn run(
&self,
_args: &crate::Args,
_largs: &UserCommand,
) -> Result<()> {
info!("Adding new user account to Theseus");
info!("A browser window will now open, follow the login flow there.");
let (tx, rx) = oneshot::channel::<url::Url>();
let flow = tokio::spawn(auth::authenticate(tx));
let url = rx.await?;
match self.browser {
Some(browser) => webbrowser::open_browser(browser, url.as_str()),
None => webbrowser::open(url.as_str()),
}?;
let credentials = flow.await??;
State::sync().await?;
success!("Logged in user {}.", credentials.username);
Ok(())
}
}
#[derive(argh::FromArgs, Debug)]
/// list all known users
#[argh(subcommand, name = "list")]
pub struct UserList {}
#[derive(Tabled)]
struct UserRow<'a> {
username: &'a str,
id: uuid::Uuid,
default: bool,
}
impl<'a> UserRow<'a> {
pub fn from(
credentials: &'a Credentials,
default: Option<uuid::Uuid>,
) -> Self {
Self {
username: &credentials.username,
id: credentials.id,
default: Some(credentials.id) == default,
}
}
}
impl UserList {
pub async fn run(
&self,
_args: &crate::Args,
_largs: &UserCommand,
) -> Result<()> {
let state = State::get().await?;
let default = state.settings.read().await.default_user;
let users = auth::users().await?;
let rows = users.iter().map(|user| UserRow::from(user, default));
let table = table(rows);
println!("{table}");
Ok(())
}
}
#[derive(argh::FromArgs, Debug)]
/// remove a user
#[argh(subcommand, name = "remove")]
pub struct UserRemove {
/// the user to remove
#[argh(positional)]
user: uuid::Uuid,
}
impl UserRemove {
pub async fn run(
&self,
_args: &crate::Args,
_largs: &UserCommand,
) -> Result<()> {
info!("Removing user {}", self.user.as_hyphenated());
if confirm_async(String::from("Do you wish to continue"), true).await? {
if !auth::has_user(self.user).await? {
warn!("Profile was not managed by Theseus!");
} else {
auth::remove_user(self.user).await?;
State::sync().await?;
success!("User removed!");
}
} else {
error!("Aborted!");
}
Ok(())
}
}
#[derive(argh::FromArgs, Debug)]
/// set the default user
#[argh(subcommand, name = "set-default")]
pub struct UserDefault {
/// the user to set as default
#[argh(positional)]
user: uuid::Uuid,
}
impl UserDefault {
pub async fn run(
&self,
_args: &crate::Args,
_largs: &UserCommand,
) -> Result<()> {
info!("Setting user {} as default", self.user.as_hyphenated());
let state: std::sync::Arc<State> = State::get().await?;
let mut settings = state.settings.write().await;
if settings.default_user == Some(self.user) {
warn!("User is already the default!");
} else {
settings.default_user = Some(self.user);
success!("User set as default!");
}
Ok(())
}
}
impl UserCommand {
pub async fn run(&self, args: &crate::Args) -> Result<()> {
dispatch!(&self.action, (args, self) => {
UserSubcommand::Add,
UserSubcommand::List,
UserSubcommand::Remove,
UserSubcommand::SetDefault
})
}
}