mirror of
https://codeberg.org/tyy/aspm
synced 2024-12-22 20:39:29 -07:00
Add basic profile create command, with debugging for now (doesn't actually create)
This commit is contained in:
parent
b545ffe364
commit
1eff198141
6 changed files with 118 additions and 5 deletions
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
@ -16,5 +16,6 @@
|
||||||
"subkeys",
|
"subkeys",
|
||||||
"userid",
|
"userid",
|
||||||
"writedoc"
|
"writedoc"
|
||||||
]
|
],
|
||||||
|
"rust-analyzer.check.command": "clippy"
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
use hex_color::HexColor;
|
pub use hex_color::HexColor;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_email::Email;
|
pub use serde_email::Email;
|
||||||
use url::Url;
|
pub use url::Url;
|
||||||
|
|
||||||
use crate::utils::jwt::{AspJwsType, JwtSerializable};
|
use crate::utils::jwt::{AspJwsType, JwtSerializable};
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
pub mod keys;
|
pub mod keys;
|
||||||
|
pub mod profiles;
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use sea_orm::{ColumnTrait, Condition, DatabaseConnection, EntityTrait, QueryFilter, QueryOrder};
|
use sea_orm::{ColumnTrait, Condition, DatabaseConnection, EntityTrait, QueryFilter, QueryOrder};
|
||||||
|
|
92
src/commands/profiles/create.rs
Normal file
92
src/commands/profiles/create.rs
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use anyhow::Context;
|
||||||
|
use asp::{profiles::*, utils::jwt::AspJwsType};
|
||||||
|
use clap::Parser;
|
||||||
|
use dialoguer::{theme::ColorfulTheme, Input};
|
||||||
|
|
||||||
|
use crate::commands::AspmSubcommand;
|
||||||
|
|
||||||
|
/// Creates a new profile containing claims and other metadata.
|
||||||
|
/// A key is needed to attach this profile to, but multiple profiles can be created for the same key.
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
pub struct ProfilesCreateCommand {
|
||||||
|
/// The alias of the profile to create. This can be anything, and it can also be omitted to prompt interactively.
|
||||||
|
/// This has no purpose other than providing a way to nicely distinguish profiles.
|
||||||
|
#[arg(short = 'n', long)]
|
||||||
|
profile_alias: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl AspmSubcommand for ProfilesCreateCommand {
|
||||||
|
async fn execute(&self, state: crate::AspmState) -> Result<(), anyhow::Error> {
|
||||||
|
let theme = ColorfulTheme::default();
|
||||||
|
|
||||||
|
let alias = if let Some(alias) = &self.profile_alias {
|
||||||
|
alias.clone()
|
||||||
|
} else {
|
||||||
|
Input::with_theme(&theme)
|
||||||
|
.with_prompt("Please enter an alias to give to this profile")
|
||||||
|
.allow_empty(false)
|
||||||
|
.interact()
|
||||||
|
.context("Unable to prompt on stderr")?
|
||||||
|
};
|
||||||
|
|
||||||
|
let profile = AriadneSignatureProfile {
|
||||||
|
version: 0,
|
||||||
|
r#type: AspJwsType::Profile,
|
||||||
|
name: Input::with_theme(&theme)
|
||||||
|
.with_prompt("Please enter a public name to show on this profile (usually a username or real name, though anything works)")
|
||||||
|
.allow_empty(false)
|
||||||
|
.interact()
|
||||||
|
.context("Unable to prompt on stderr")?,
|
||||||
|
claims: {
|
||||||
|
let mut claims = Vec::<String>::new();
|
||||||
|
loop {
|
||||||
|
let claim: String = Input::with_theme(&theme)
|
||||||
|
.with_prompt("Please enter a claim and then press enter. To finish, enter an empty string")
|
||||||
|
.allow_empty(true)
|
||||||
|
.interact()
|
||||||
|
.context("Unable to prompt on stderr")?;
|
||||||
|
|
||||||
|
if claim.is_empty() {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
claims.push(claim);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
claims
|
||||||
|
},
|
||||||
|
description: Some(Input::with_theme(&theme)
|
||||||
|
.with_prompt("If you want to add a description to this profile, enter it or an empty string")
|
||||||
|
.allow_empty(true)
|
||||||
|
.interact()
|
||||||
|
.context("Unable to prompt on stderr")?).filter(|x: &String| !x.is_empty()),
|
||||||
|
avatar_url: Some(Input::with_theme(&theme)
|
||||||
|
.with_prompt("If you want to add an avatar to this profile, enter the URL or an empty string")
|
||||||
|
.allow_empty(true)
|
||||||
|
.interact()
|
||||||
|
.context("Unable to prompt on stderr")?)
|
||||||
|
.filter(|x: &String| !x.is_empty())
|
||||||
|
.map_or(Ok(None), |string: String| Url::parse(&string).context("Unable to parse avatar URL").map(|url| Some(url)))?,
|
||||||
|
email: Some(Input::with_theme(&theme)
|
||||||
|
.with_prompt("If you want to add an email to this profile, enter it or an empty string")
|
||||||
|
.allow_empty(true)
|
||||||
|
.interact()
|
||||||
|
.context("Unable to prompt on stderr")?)
|
||||||
|
.filter(|x: &String| !x.is_empty())
|
||||||
|
.map_or(Ok(None), |string: String| Email::from_string(string).context("Unable to parse email").map(|email| Some(email)))?,
|
||||||
|
color: Some(Input::with_theme(&theme)
|
||||||
|
.with_prompt("If you want to add a color to this profile, enter it in hex format (#AABBCC) or an empty string")
|
||||||
|
.allow_empty(true)
|
||||||
|
.interact()
|
||||||
|
.context("Unable to prompt on stderr")?)
|
||||||
|
.filter(|x: &String| !x.is_empty())
|
||||||
|
.map_or(Ok(None), |hex: String| HexColor::from_str(&hex).context("Unable to parse color code").map(|color| Some(color)))?,
|
||||||
|
};
|
||||||
|
|
||||||
|
dbg!(profile);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
15
src/commands/profiles/mod.rs
Normal file
15
src/commands/profiles/mod.rs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
use clap::{Parser, Subcommand};
|
||||||
|
|
||||||
|
pub mod create;
|
||||||
|
|
||||||
|
/// A subcommand to allow the management of keys, which can then be used to create, modify, or delete profiles.
|
||||||
|
#[derive(Parser)]
|
||||||
|
pub struct ProfilesSubcommand {
|
||||||
|
#[command(subcommand)]
|
||||||
|
pub subcommand: ProfilesSubcommands,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Subcommand)]
|
||||||
|
pub enum ProfilesSubcommands {
|
||||||
|
Create(create::ProfilesCreateCommand),
|
||||||
|
}
|
|
@ -6,7 +6,7 @@ use anstyle::{AnsiColor, Color as AnstyleColor, Style as Anstyle};
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use app_dirs2::{AppDataType, AppInfo};
|
use app_dirs2::{AppDataType, AppInfo};
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use commands::{keys::KeysSubcommands, AspmSubcommand};
|
use commands::{keys::KeysSubcommands, profiles::ProfilesSubcommands, AspmSubcommand};
|
||||||
use migrations::{Migrator, MigratorTrait, SchemaManager};
|
use migrations::{Migrator, MigratorTrait, SchemaManager};
|
||||||
use sea_orm::{Database, DatabaseConnection};
|
use sea_orm::{Database, DatabaseConnection};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
@ -77,6 +77,7 @@ impl AspmCommand {
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
pub enum AspmSubcommands {
|
pub enum AspmSubcommands {
|
||||||
Keys(commands::keys::KeysSubcommand),
|
Keys(commands::keys::KeysSubcommand),
|
||||||
|
Profiles(commands::profiles::ProfilesSubcommand),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -151,6 +152,9 @@ fn cli(parsed: AspmCommand) -> Result<(), anyhow::Error> {
|
||||||
KeysSubcommands::Delete(subcommand) => subcommand.execute_sync(state, runtime),
|
KeysSubcommands::Delete(subcommand) => subcommand.execute_sync(state, runtime),
|
||||||
KeysSubcommands::Import(subcommand) => subcommand.execute_sync(state, runtime),
|
KeysSubcommands::Import(subcommand) => subcommand.execute_sync(state, runtime),
|
||||||
},
|
},
|
||||||
|
AspmSubcommands::Profiles(subcommand) => match &subcommand.subcommand {
|
||||||
|
ProfilesSubcommands::Create(subcommand) => subcommand.execute_sync(state, runtime),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue