So much premature optimization

This commit is contained in:
Daniel Flanagan 2024-04-18 12:14:21 -05:00
parent 77cabd9e3e
commit ab9b3647f9
8 changed files with 91 additions and 79 deletions

View file

@ -33,6 +33,26 @@ pub struct Cli {
pub config_file_path: Option<String>,
}
impl Cli {
pub async fn exec(self) -> Result<()> {
let tasks = Arc::new(crate::tasks::Tasks::try_new(
self.config_file_path,
self.data_directory,
)?);
match self.command {
Commands::Ui(args) => args.run(tasks).await,
Commands::Purge(args) => args.run(tasks).await,
Commands::Cleanup(args) => args.run(tasks).await,
Commands::Sync(args) => args.sync(tasks).await,
Commands::List(args) => args.list(tasks).await,
Commands::Jira(jira) => jira.exec(tasks).await,
Commands::Gitlab(gitlab) => gitlab.exec(tasks).await,
Commands::Config(config) => config.exec(tasks).await,
}
}
}
#[derive(Subcommand)]
pub enum Commands {
/// List local tasks with options for syncing

View file

@ -8,19 +8,12 @@ pub enum Command {
impl Command {
pub async fn exec(&self, tasks: SharedTasks) -> Result<()> {
match self {
Command::Show => self.show().await,
Command::Show => self.show(tasks).await,
}
}
pub async fn show(&self) -> Result<()> {}
}
#[derive(Parser)]
pub struct MeArgs {}
impl MeArgs {
pub async fn me(&self, tasks: SharedTasks) -> Result<()> {
println!("{:#?}", tasks.gitlab()?.me().await?);
pub async fn show(&self, tasks: SharedTasks) -> Result<()> {
println!("{:#?}", tasks.config());
Ok(())
}
}

View file

@ -1,7 +1,8 @@
use crate::prelude::*;
use std::{collections::HashMap, fmt::Debug, path::Path};
use std::{fmt::Debug, path::Path};
use color_eyre::{eyre::Context, Section};
use config::{builder::DefaultState, Config as CConfig, ConfigBuilder};
use serde::{Deserialize, Serialize};
@ -25,38 +26,22 @@ pub struct Config {
}
impl Config {
pub fn load<P>(config_file_path: Option<P>) -> Result<Self>
where
P: AsRef<Path>,
{
pub fn load(config_file_path: &Path) -> Result<Self> {
let c = Self::builder(config_file_path)?.build()?;
Ok(c.try_deserialize()?)
Ok(c.try_deserialize()
.suggestion("run `taskr config setup` to ensure valid configuration")
.with_context(|| "failed to deserialize configuration")?)
}
pub fn builder<P>(config_file_path: Option<P>) -> Result<ConfigBuilder<DefaultState>>
where
P: AsRef<Path>,
{
let config_file_path: Box<dyn AsRef<Path>> = config_file_path
.as_ref()
.map(|s| Ok::<Box<dyn AsRef<Path>>, anyhow::Error>(Box::new(s.as_ref())))
.unwrap_or_else(|| {
Ok(Box::new(
xdg::BaseDirectories::new()?.get_config_file("taskr/config.toml"),
))
})?;
Ok(Self::default_builder()
.map_err(anyhow::Error::from)?
pub fn builder(config_file_path: &Path) -> Result<ConfigBuilder<DefaultState>>
where {
Ok(Self::default_builder()?
.add_source(config::File::from((*config_file_path).as_ref()).required(false))
.add_source(config::Environment::with_prefix("taskr").separator("__")))
}
pub fn default_builder() -> Result<ConfigBuilder<DefaultState>> {
Ok(CConfig::builder()
.set_default("version", CURRENT_VERSION)?
.set_default("jira", Option::<HashMap<String, config::Value>>::None)?
.set_default("gitlab", Option::<HashMap<String, config::Value>>::None)?)
Ok(CConfig::builder().set_default("version", CURRENT_VERSION)?)
}
#[allow(dead_code)]

View file

@ -12,31 +12,19 @@ mod task;
mod tasks;
mod tui;
use config::Config;
use crate::cli::{Cli, Commands};
use crate::prelude::*;
#[tokio::main]
async fn main() -> Result<()> {
observe::setup_error_handling()?;
let cli = Cli::new();
let conf = Config::load(cli.config_file_path)?;
// this guard causes logs to be flushed when dropped (which is at the end of
// this function which would be the end of our program)
// https://docs.rs/tracing-appender/latest/tracing_appender/non_blocking/struct.WorkerGuard.html
let _log_guard = observe::setup_logging(cli.logs_directory, cli.tracing_env_filter)?;
let _log_guard = observe::setup_logging(&cli.logs_directory, &cli.tracing_env_filter)?;
let tasks = Arc::new(crate::tasks::Tasks::try_new(conf, cli.data_directory)?);
let result = match cli.command {
Commands::Ui(args) => args.run(tasks).await,
Commands::Purge(args) => args.run(tasks).await,
Commands::Cleanup(args) => args.run(tasks).await,
Commands::Sync(args) => args.sync(tasks).await,
Commands::List(args) => args.list(tasks).await,
Commands::Jira(jira) => jira.exec(tasks).await,
Commands::Gitlab(gitlab) => gitlab.exec(tasks).await,
};
result
cli.exec().await
}

View file

@ -4,9 +4,14 @@ use std::path::Path;
use tracing_appender::non_blocking::WorkerGuard;
use tracing_subscriber::{filter::LevelFilter, EnvFilter};
pub fn setup_error_handling() -> Result<()> {
color_eyre::install()?;
Ok(())
}
pub fn setup_logging<T, S>(
logs_directory: Option<T>,
env_filter_directive: Option<S>,
logs_directory: &Option<T>,
env_filter_directive: &Option<S>,
) -> Result<WorkerGuard>
where
T: AsRef<Path>,
@ -38,7 +43,6 @@ where
.map(S::as_ref)
.unwrap_or("taskr=trace,info");
color_eyre::install().expect("Failed to install color_eyre");
let filter = EnvFilter::builder()
.with_default_directive(LevelFilter::TRACE.into())
.parse_lossy(env_filter);

View file

@ -1,3 +1 @@
use anyhow::Error;
pub type Result<T> = std::result::Result<T, Error>;
pub type Result<T> = color_eyre::Result<T>;

View file

@ -85,15 +85,15 @@ impl Display for Task {
}
impl TryFrom<IVec> for Task {
type Error = anyhow::Error;
type Error = serde_json::Error;
fn try_from(value: IVec) -> std::prelude::v1::Result<Self, Self::Error> {
serde_json::from_slice(&value).map_err(|e| e.into())
serde_json::from_slice(&value)
}
}
impl TryInto<IVec> for &Task {
type Error = anyhow::Error;
type Error = serde_json::Error;
fn try_into(self) -> std::prelude::v1::Result<IVec, Self::Error> {
Ok(IVec::from(serde_json::to_vec(self)?))
@ -101,7 +101,7 @@ impl TryInto<IVec> for &Task {
}
impl TryFrom<&Issue> for Task {
type Error = anyhow::Error;
type Error = std::num::ParseIntError;
fn try_from(value: &Issue) -> std::prelude::v1::Result<Self, Self::Error> {
let mut tags = HashSet::from_iter(value.fields.labels.iter().map(|s| s.to_owned()));

View file

@ -19,9 +19,10 @@ use crate::{gitlab::GitLab, jira::Jira, result::Result};
pub type Db = sled::Db;
pub struct Tasks {
config: Config,
data_dir: PathBuf,
config_file_path: PathBuf,
config: OnceLock<Config>,
gitlab: OnceLock<GitLab>,
jira: OnceLock<Jira>,
@ -31,30 +32,42 @@ pub struct Tasks {
const MY_DONE_STATUS_CATEGORY_KEY: &str = "done";
impl Tasks {
pub fn try_new<D>(config: Config, data_dir: Option<D>) -> Result<Self>
pub fn try_new<D>(config_file_path: Option<D>, data_dir: Option<D>) -> Result<Self>
where
D: AsRef<Path>,
{
let config = OnceLock::new();
let gitlab = OnceLock::new();
let jira = OnceLock::new();
let mut default_data_dir = Default::default();
let data_dir = data_dir
.as_ref()
.map(D::as_ref)
.map(Result::Ok)
.unwrap_or_else(|| {
// really want to avoid calling this so it's put in here to force it to be lazy
// also have to shove it in a function-scoped variable so the
// reference we create in this closure has the same lifetime as the function
default_data_dir =
xdg::BaseDirectories::new()?.create_data_directory("taskr/data")?;
Ok(default_data_dir.as_ref())
})?;
let xdgonce = OnceLock::new();
let xdg = || -> Result<&xdg::BaseDirectories> {
match xdgonce.get() {
Some(x) => Ok(x),
None => {
let result = xdg::BaseDirectories::new()?;
let _ = xdgonce.set(result);
Ok(xdgonce.get().unwrap())
}
}
};
let config_file_path: PathBuf = if let Some(p) = config_file_path {
p.as_ref().to_owned()
} else {
xdg()?.get_config_file("taskr/config.toml")
};
let data_dir: PathBuf = if let Some(p) = data_dir {
p.as_ref().to_owned()
} else {
xdg()?.create_data_directory("taskr/data")?
};
let db = OnceLock::new();
let data_dir: PathBuf = data_dir.to_owned();
Ok(Self {
config_file_path,
config,
gitlab,
jira,
@ -63,6 +76,17 @@ impl Tasks {
})
}
pub fn config(&self) -> Result<&Config> {
match self.config.get() {
Some(c) => Ok(c),
None => {
let result = Config::load(self.config_file_path.as_path())?;
let _ = self.config.set(result);
Ok(self.config.get().unwrap())
}
}
}
pub fn db(&self) -> Result<&Db> {
match self.db.get() {
Some(d) => Ok(d),
@ -109,7 +133,7 @@ impl Tasks {
}
};
let result =
GitLab::try_new(&format!("{}/api/v4", self.config.gitlab.url), &gl_token)?;
GitLab::try_new(&format!("{}/api/v4", self.config()?.gitlab.url), &gl_token)?;
let _ = self.gitlab.set(result);
Ok(self.gitlab.get().unwrap())
// TODO: ensure the token works?
@ -152,7 +176,7 @@ impl Tasks {
}
};
let result = Jira::try_new(&format!("{}", self.config.jira.url), &jira_token)?;
let result = Jira::try_new(&format!("{}", self.config()?.jira.url), &jira_token)?;
let _ = self.jira.set(result);
Ok(self.jira.get().unwrap())
// TODO: ensure the token works?