Improve config options
This commit is contained in:
parent
1b70e7c9bd
commit
c08929c765
8 changed files with 109 additions and 29 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1681,6 +1681,7 @@ dependencies = [
|
|||
"clap_derive",
|
||||
"color-eyre",
|
||||
"crossterm",
|
||||
"futures",
|
||||
"ratatui",
|
||||
"redact",
|
||||
"regex",
|
||||
|
|
|
@ -11,6 +11,7 @@ clap = { version = "4.5.4", features = ["derive"] }
|
|||
clap_derive = "4.5.4"
|
||||
color-eyre = "0.6.3"
|
||||
crossterm = "0.27.0"
|
||||
futures = "0.3.30"
|
||||
ratatui = "0.26.2"
|
||||
redact = "0.1.9"
|
||||
regex = "1.10.3"
|
||||
|
|
36
src/cli.rs
36
src/cli.rs
|
@ -10,9 +10,15 @@ pub struct Cli {
|
|||
#[command(subcommand)]
|
||||
pub command: Commands,
|
||||
|
||||
/// Tell taskr which directory to write log files to
|
||||
/// The directory to write log files
|
||||
#[arg(long, default_value = None)]
|
||||
pub logs_directory: Option<String>,
|
||||
|
||||
/// Define which tracing values are logged
|
||||
/// See https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html
|
||||
/// for details
|
||||
#[arg(long, default_value = None)]
|
||||
pub tracing_env_filter: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
|
@ -22,6 +28,12 @@ pub enum Commands {
|
|||
|
||||
/// Lists tasks with options for syncing
|
||||
List(ListArgs),
|
||||
|
||||
/// Sync local tasks with GitLab and Jira
|
||||
Sync,
|
||||
|
||||
/// Interact with the associated tasks' Jira issues
|
||||
Jira(JiraArgs),
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
|
@ -33,14 +45,27 @@ pub struct ListArgs {
|
|||
pub sync: bool,
|
||||
}
|
||||
|
||||
#[derive(Args, Debug)]
|
||||
pub struct JiraArgs {
|
||||
#[arg(short, long)]
|
||||
pub key: String,
|
||||
}
|
||||
|
||||
impl Cli {
|
||||
pub fn new() -> Self {
|
||||
Self::parse()
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn sync() -> Result<()> {
|
||||
eprintln!("Syncing...");
|
||||
Arc::new(crate::tasks::Tasks::try_new()?).sync().await?;
|
||||
eprintln!("Done!");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn list_tasks(args: &ListArgs) -> Result<()> {
|
||||
let tasks = crate::tasks::Tasks::try_new()?;
|
||||
let tasks = Arc::new(crate::tasks::Tasks::try_new()?);
|
||||
if args.sync {
|
||||
eprintln!("Syncing...");
|
||||
tasks.sync().await?;
|
||||
|
@ -58,6 +83,13 @@ pub async fn list_tasks(args: &ListArgs) -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn jira(args: &JiraArgs) -> Result<()> {
|
||||
let tasks = crate::tasks::Tasks::try_new()?;
|
||||
let issue = tasks.jira.issue(&args.key).await?;
|
||||
println!("{issue:?}");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_cli() {
|
||||
use clap::CommandFactory;
|
||||
|
|
15
src/main.rs
15
src/main.rs
|
@ -18,10 +18,19 @@ use cli::{Cli, Commands};
|
|||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
let cli = Cli::new();
|
||||
observe::setup_logging(cli.logs_directory)?;
|
||||
|
||||
// this guard causes logs to be flushed when dropped
|
||||
// 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)?;
|
||||
|
||||
println!("Starting...");
|
||||
trace!("Starting...");
|
||||
match cli.command {
|
||||
info!("Starting...");
|
||||
let result = match cli.command {
|
||||
Commands::Sync => cli::sync().await,
|
||||
Commands::Ui(_args) => tui::run().await,
|
||||
Commands::List(args) => cli::list_tasks(&args).await,
|
||||
}
|
||||
Commands::Jira(args) => cli::jira(&args).await,
|
||||
};
|
||||
result
|
||||
}
|
||||
|
|
|
@ -1,32 +1,54 @@
|
|||
use crate::prelude::*;
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::path::Path;
|
||||
use tracing_appender::non_blocking::WorkerGuard;
|
||||
use tracing_subscriber::{filter::LevelFilter, EnvFilter};
|
||||
|
||||
pub fn setup_logging<T: Into<PathBuf>>(logs_directory: Option<T>) -> Result<()> {
|
||||
let default_logs_directory = || -> Result<PathBuf> {
|
||||
Ok(xdg::BaseDirectories::new()?.create_cache_directory("taskr/logs")?)
|
||||
};
|
||||
let logs_directory: PathBuf = match logs_directory {
|
||||
Some(p) => p.into(),
|
||||
None => default_logs_directory()?,
|
||||
};
|
||||
if !logs_directory.exists() {
|
||||
std::fs::create_dir_all(&logs_directory)?;
|
||||
pub fn setup_logging<T, S>(
|
||||
logs_directory: Option<T>,
|
||||
env_filter_directive: Option<S>,
|
||||
) -> Result<WorkerGuard>
|
||||
where
|
||||
T: AsRef<Path>,
|
||||
S: AsRef<str>,
|
||||
{
|
||||
let mut default_logs_dir = Default::default();
|
||||
let logs_dir = logs_directory
|
||||
.as_ref()
|
||||
.map(T::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_logs_dir = xdg::BaseDirectories::new()?.create_cache_directory("taskr/logs")?;
|
||||
Ok(default_logs_dir.as_ref())
|
||||
})?;
|
||||
|
||||
eprintln!("Logs Directory: {}", logs_dir.display());
|
||||
if !logs_dir.exists() {
|
||||
std::fs::create_dir_all(&logs_dir)?;
|
||||
}
|
||||
|
||||
let file_appender = tracing_appender::rolling::hourly(logs_directory, "log");
|
||||
let (non_blocking, _guard) = tracing_appender::non_blocking(file_appender);
|
||||
let file_appender = tracing_appender::rolling::hourly(logs_dir, "log");
|
||||
let (non_blocking, guard) = tracing_appender::non_blocking(file_appender);
|
||||
|
||||
let env_filter = env_filter_directive
|
||||
.as_ref()
|
||||
.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("info,taskr=trace");
|
||||
.parse_lossy(env_filter);
|
||||
|
||||
eprintln!("EnvFilter Directive: {}", filter);
|
||||
|
||||
tracing_subscriber::fmt()
|
||||
.with_writer(non_blocking)
|
||||
.with_env_filter(filter)
|
||||
.init();
|
||||
|
||||
Ok(())
|
||||
Ok(guard)
|
||||
}
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
#![allow(unused_imports)]
|
||||
|
||||
pub use crate::result::Result;
|
||||
pub use regex::Regex;
|
||||
pub use std::sync::Arc;
|
||||
pub use tokio::sync::Mutex;
|
||||
pub use tracing::{debug, error, info, trace, warn};
|
||||
|
|
|
@ -5,13 +5,13 @@ use sled::IVec;
|
|||
|
||||
use crate::jira::Issue;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
||||
pub struct MergeRequestRef {
|
||||
pub url: String,
|
||||
pub state: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
||||
pub struct Task {
|
||||
pub jira_key: String,
|
||||
pub description: String,
|
||||
|
|
26
src/tasks.rs
26
src/tasks.rs
|
@ -1,4 +1,5 @@
|
|||
use regex::Regex;
|
||||
use crate::prelude::*;
|
||||
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
env,
|
||||
|
@ -21,11 +22,7 @@ pub struct Tasks {
|
|||
db: sled::Db,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Desyncs {
|
||||
issues: Vec<crate::jira::Issue>,
|
||||
tasks: Vec<Task>,
|
||||
}
|
||||
const MY_DONE_STATUS_CATEGORY_KEY: &str = "done";
|
||||
|
||||
impl Tasks {
|
||||
pub fn try_new() -> Result<Self> {
|
||||
|
@ -135,6 +132,14 @@ impl Tasks {
|
|||
async fn fix_dangling_task(&self, task: &Task) -> Result<()> {
|
||||
// check if closed, if it is, delete the task
|
||||
let issue = self.jira.issue(&task.jira_key).await?;
|
||||
if issue.fields.status.status_category.key == MY_DONE_STATUS_CATEGORY_KEY {
|
||||
info!(
|
||||
"Deleting task {} due to being marked {} in Jira",
|
||||
task.jira_key, MY_DONE_STATUS_CATEGORY_KEY
|
||||
);
|
||||
// remote issue is done, so delete the task
|
||||
self.delete(task)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -149,7 +154,7 @@ impl Tasks {
|
|||
HashSet::from_iter(issues.keys().map(|s| s.to_owned()));
|
||||
|
||||
// keys which have a task but not an issue
|
||||
let _dangling_keys: HashSet<String, RandomState> = HashSet::from_iter(
|
||||
let dangling_keys: HashSet<String, RandomState> = HashSet::from_iter(
|
||||
task_keys
|
||||
.difference(&issue_keys)
|
||||
.into_iter()
|
||||
|
@ -166,6 +171,13 @@ impl Tasks {
|
|||
tasks.insert(task.jira_key.clone(), task);
|
||||
}
|
||||
|
||||
futures::future::try_join_all(
|
||||
dangling_keys
|
||||
.iter()
|
||||
.map(|t| self.fix_dangling_task(tasks.get(t).unwrap())),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// TODO: blocking? maybe should be async?
|
||||
// while let Ok(_res) = rx.recv() {
|
||||
// awaiting all the tasks in the joinset
|
||||
|
|
Loading…
Reference in a new issue