127 lines
3.5 KiB
Rust
127 lines
3.5 KiB
Rust
use clap::{Args, ArgGroup};
|
|
use restson::RestClient;
|
|
use anyhow::{Result, Error};
|
|
use std::str::{FromStr};
|
|
|
|
#[derive(Args, Debug)]
|
|
#[command(group(
|
|
ArgGroup::new("authentication")
|
|
.args(["access", "refresh", "email", "phone"])
|
|
))]
|
|
pub struct Authentication {
|
|
#[arg(long)]
|
|
access: Option<Access>,
|
|
|
|
#[arg(long)]
|
|
refresh: Option<Refresh>,
|
|
|
|
#[arg(long)]
|
|
email: Option<Email>,
|
|
|
|
#[arg(long)]
|
|
phone: Option<Phone>,
|
|
}
|
|
|
|
fn bearer(mut client: RestClient, token: &str) -> Result<RestClient> {
|
|
client.set_header("Authorization", &format!("Bearer {}", token))?;
|
|
Ok(client)
|
|
}
|
|
|
|
impl Authentication {
|
|
pub async fn apply(&self, client: RestClient) -> Result<RestClient> {
|
|
let client = match (self.access.as_ref(), self.refresh.as_ref(), self.email.as_ref(), self.phone.as_ref()) {
|
|
(Some(ref v), None, None, None) => v.apply(client)?,
|
|
(None, Some(ref v), None, None) => v.apply(client).await?,
|
|
(None, None, Some(ref v), None) => v.apply(client).await?,
|
|
(None, None, None, Some(ref v)) => v.apply(client).await?,
|
|
(None, None, None, None) => client,
|
|
(a, b, c, d) => anyhow::bail!("invalid authentication: {} + {} + {} + {}", a.is_some(), b.is_some(), c.is_some(), d.is_some()),
|
|
};
|
|
Ok(client)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
struct WithPassword {
|
|
value: String,
|
|
password: String,
|
|
}
|
|
impl FromStr for WithPassword {
|
|
type Err = Error;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
let password = match std::env::var("SPOND_PASSWORD") {
|
|
Ok(password) => password,
|
|
Err(_) => rpassword::prompt_password("Password: ")?,
|
|
};
|
|
let value = String::from_str(s)?;
|
|
Ok(Self { value, password })
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
struct Email(WithPassword);
|
|
impl Email {
|
|
async fn apply(&self, client: RestClient) -> Result<RestClient> {
|
|
let tokens = spond_api::authentication::email(&client, &self.0.value, &self.0.password).await?;
|
|
bearer(client, tokens.access.token.as_ref())
|
|
}
|
|
}
|
|
impl FromStr for Email {
|
|
type Err= <WithPassword as FromStr>::Err;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
Ok(Self(WithPassword::from_str(s)?))
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
struct Phone(WithPassword);
|
|
impl Phone {
|
|
async fn apply(&self, client: RestClient) -> Result<RestClient> {
|
|
let tokens = spond_api::authentication::phone(&client, &self.0.value, &self.0.password).await?;
|
|
bearer(client, tokens.access.token.as_ref())
|
|
}
|
|
}
|
|
impl FromStr for Phone {
|
|
type Err= <WithPassword as FromStr>::Err;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
Ok(Self(WithPassword::from_str(s)?))
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
struct Access(String);
|
|
|
|
impl Access {
|
|
fn apply(&self, client: RestClient) -> Result<RestClient> {
|
|
bearer(client, &self.0)
|
|
}
|
|
}
|
|
|
|
impl FromStr for Access {
|
|
type Err = std::convert::Infallible; // parsing a String never fails
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
Ok(Access(s.to_string()))
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
struct Refresh(String);
|
|
|
|
impl Refresh {
|
|
async fn apply(&self, client: RestClient) -> Result<RestClient> {
|
|
let tokens = spond_api::authentication::token(&client, &self.0).await?;
|
|
bearer(client, tokens.access.token.as_ref())
|
|
}
|
|
}
|
|
|
|
impl FromStr for Refresh {
|
|
type Err = std::convert::Infallible; // parsing a String never fails
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
Ok(Refresh(s.to_string()))
|
|
}
|
|
}
|