Config stuff working

This commit is contained in:
Daniel Flanagan 2024-04-19 15:27:16 -05:00
parent 75816058cf
commit 9d61b04731
3 changed files with 93 additions and 58 deletions

View file

@ -47,6 +47,7 @@
# debugger
lldb
gdbgui
# libs
libopus

View file

@ -1,12 +1,16 @@
use std::{fs::File, io::Write};
use cliclack::input;
use cliclack::{input, password};
use futures::StreamExt;
use signal_hook::consts::*;
use signal_hook_tokio::Signals;
use crate::cli::prelude::*;
#[derive(Subcommand)]
pub enum Command {
/// Write the current config (without secrets) in TOML format
Show,
/// Interactively setup a Taskr configuration
Setup,
}
@ -14,66 +18,78 @@ impl Command {
pub async fn exec(&self, tasks: SharedTasks) -> Result<()> {
match self {
Command::Show => self.show(tasks).await,
Command::Setup => self.setup(tasks).await,
Command::Setup => Self::setup(tasks).await,
}
}
pub async fn show(&self, tasks: SharedTasks) -> Result<()> {
println!("{:#?}", tasks.config());
println!("{}", toml::to_string_pretty(tasks.config()?)?);
Ok(())
}
pub async fn setup(&self, tasks: SharedTasks) -> Result<()> {
pub async fn setup(stasks: SharedTasks) -> Result<()> {
use cliclack::{intro, outro};
// TODO: maybe use the normal builder
let tasks = stasks.clone();
let cliclack_handle: tokio::task::JoinHandle<Result<()>> = tokio::spawn(async move {
// TODO: maybe use the normal builder
// TODO: handle sigint and reset the terminal gracefully
intro("Configure taskr")?;
intro("Configure taskr")?;
// let gitlab_url: String = input("What is your GitLab URL?")
// .placeholder("https://gitlab.com")
// .validate_interactively(|input: &String| match url::Url::parse(input) {
// Ok(_) => Ok(()),
// Err(_) => Err("Must be a URL"),
// })
// .interact()?;
let gitlab_url: String = input("What is your GitLab URL?")
.placeholder("https://gitlab.com")
.validate_interactively(|input: &String| match url::Url::parse(input) {
Ok(_) => Ok(()),
Err(_) => Err("Must be a URL"),
})
.interact()?;
// let jira_url: String = input("What is your Jira URL?")
// .placeholder("https://company.atlassian.net")
// .validate_interactively(|input: &String| match url::Url::parse(input) {
// Ok(_) => Ok(()),
// Err(_) => Err("Must be a URL"),
// })
// .interact()?;
let jira_url: String = input("What is your Jira URL?")
.placeholder("https://company.atlassian.net")
.validate_interactively(|input: &String| match url::Url::parse(input) {
Ok(_) => Ok(()),
Err(_) => Err("Must be a URL"),
})
.interact()?;
let gitlab_url = "https://gitlab.com";
let jira_url = "https://jira.com";
let mut config = Config::build(
Config::builder(&tasks.config_file_path)?
.set_override("gitlab.url", gitlab_url.as_str())?
.set_override("jira.url", jira_url.as_str())?,
)?;
let config_builder = Config::builder(&tasks.config_file_path)?
.set_override("gitlab.url", gitlab_url)?
.set_override("jira.url", jira_url)?
.set_override("secrets.gitlab_token", gitlab_url)?
.set_override("secrets.jira_token", jira_url)?;
// TODO: link user to page to setup tokens
config.secrets.gitlab_token =
password("What is your GitLab Personal Access Token? (scopes: read_api)")
// .placeholder("glpat-XXXX...")
.interact()?;
dbg!(&config_builder);
let built = config_builder.build()?;
dbg!(&built);
config.secrets.jira_token = password("What is your Jira API Token?").interact()?;
let config_result = built.try_deserialize();
dbg!(&config_result);
if let Err(err) = &config_result {
match err {
config::ConfigError::Type { origin, unexpected, expected, key } => println!("Type Error at key {key:#?} expected {expected:#?} from origin {origin:#?} (unexpected: {unexpected:#?})"),
config::ConfigError::Message(str) => println!("Message {err:#?} {str}"),
_ => println!("I dunno. {err:#?}"),
}
}
let config = config_result?;
let mut file = File::create(&tasks.config_file_path)?;
file.write_all(toml::to_string(&config)?.as_bytes())?;
config.save(&tasks.config_file_path)?;
// TODO: initialize known keyring values/entries
outro("All set! Try `taskr list --sync` now!")?;
Ok(())
});
let mut signals = Signals::new(&[SIGHUP, SIGTERM, SIGINT, SIGQUIT])?;
let signal_handle = signals.handle();
// TODO: either we will die, get a signal, or we setup
tokio::select! {
_ = signals.next() => {
// outro_cancel("Canceled taskr setup")?;
}
_ = cliclack_handle => {
signal_handle.close();
}
};
// reset our terminal?
// TODO: initialize known keyring values/entries
outro("All set! Try `taskr list --sync` now!")?;
Ok(())
}
}

View file

@ -1,6 +1,6 @@
use crate::prelude::*;
use std::{collections::HashMap, fmt::Debug, path::Path};
use std::{collections::HashMap, fmt::Debug, fs::File, io::Write, path::Path};
use color_eyre::{eyre::Context, Section};
use config::{builder::DefaultState, Config as CConfig, ConfigBuilder, Value};
@ -20,8 +20,8 @@ pub struct Jira {
#[derive(Deserialize, Debug, Clone, Default)]
pub struct Secrets {
jira_token: String,
gitlab_token: String,
pub jira_token: String,
pub gitlab_token: String,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
@ -36,15 +36,10 @@ pub struct Config {
impl Config {
pub fn load(config_file_path: &Path) -> Result<Self> {
let builder = Self::builder(config_file_path)?.build()?;
let mut conf: Self = builder
.try_deserialize()
.suggestion("run `taskr config setup` to ensure valid configuration")
.with_context(|| "failed to deserialize configuration")?;
let mut conf = Self::build(Self::builder(config_file_path)?)?;
if conf.secrets.jira_token == "" {
conf.secrets.jira_token =
keyring::Entry::new("taskr", "gitlab_token")?.get_password()?;
conf.secrets.jira_token = keyring::Entry::new("taskr", "jira_token")?.get_password()?;
}
if conf.secrets.gitlab_token == "" {
@ -55,6 +50,30 @@ impl Config {
Ok(conf)
}
/// This should inject the files/values/whatever into their appropriate
/// places such that the next time Self::load() is called (with the same
/// `config_file_path`) you end up with the same Self.
pub fn save(&self, config_file_path: &Path) -> Result<()> {
if let Some(p) = config_file_path.parent() {
std::fs::create_dir_all(p)?;
}
let mut file = File::create(config_file_path)?;
file.write_all(toml::to_string(self)?.as_bytes())?;
keyring::Entry::new("taskr", "gitlab_token")?.set_password(&self.secrets.gitlab_token)?;
keyring::Entry::new("taskr", "jira_token")?.set_password(&self.secrets.jira_token)?;
Ok(())
}
pub fn build(builder: ConfigBuilder<DefaultState>) -> Result<Self> {
builder
.build()?
.try_deserialize()
.suggestion("run `taskr config setup` to ensure valid configuration")
.with_context(|| "failed to deserialize configuration")
}
pub fn builder(config_file_path: &Path) -> Result<ConfigBuilder<DefaultState>>
where {
Ok(Self::default_builder()?
@ -76,8 +95,7 @@ where {
#[allow(dead_code)]
pub fn defaults() -> Result<Self> {
let c = Self::default_builder()?.build()?;
Ok(c.try_deserialize()?)
Self::build(Self::default_builder()?)
}
}