Added clap for CLI
This commit is contained in:
parent
3fe83e83f1
commit
1b70e7c9bd
10 changed files with 314 additions and 98 deletions
118
Cargo.lock
generated
118
Cargo.lock
generated
|
@ -59,6 +59,54 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstream"
|
||||||
|
version = "0.6.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"anstyle-parse",
|
||||||
|
"anstyle-query",
|
||||||
|
"anstyle-wincon",
|
||||||
|
"colorchoice",
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle"
|
||||||
|
version = "1.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-parse"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
|
||||||
|
dependencies = [
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-query"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-wincon"
|
||||||
|
version = "3.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.81"
|
version = "1.0.81"
|
||||||
|
@ -184,6 +232,46 @@ dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "4.5.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
|
||||||
|
dependencies = [
|
||||||
|
"clap_builder",
|
||||||
|
"clap_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_builder"
|
||||||
|
version = "4.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
|
||||||
|
dependencies = [
|
||||||
|
"anstream",
|
||||||
|
"anstyle",
|
||||||
|
"clap_lex",
|
||||||
|
"strsim",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_derive"
|
||||||
|
version = "4.5.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64"
|
||||||
|
dependencies = [
|
||||||
|
"heck 0.5.0",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_lex"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "color-eyre"
|
name = "color-eyre"
|
||||||
version = "0.6.3"
|
version = "0.6.3"
|
||||||
|
@ -211,6 +299,12 @@ dependencies = [
|
||||||
"tracing-error",
|
"tracing-error",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colorchoice"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "compact_str"
|
name = "compact_str"
|
||||||
version = "0.7.1"
|
version = "0.7.1"
|
||||||
|
@ -546,6 +640,12 @@ version = "0.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.3.4"
|
version = "0.3.4"
|
||||||
|
@ -1495,6 +1595,12 @@ version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strum"
|
name = "strum"
|
||||||
version = "0.26.2"
|
version = "0.26.2"
|
||||||
|
@ -1510,7 +1616,7 @@ version = "0.26.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946"
|
checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck 0.4.1",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"rustversion",
|
"rustversion",
|
||||||
|
@ -1565,12 +1671,14 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tasks"
|
name = "taskr"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
"chrono-humanize",
|
"chrono-humanize",
|
||||||
|
"clap",
|
||||||
|
"clap_derive",
|
||||||
"color-eyre",
|
"color-eyre",
|
||||||
"crossterm",
|
"crossterm",
|
||||||
"ratatui",
|
"ratatui",
|
||||||
|
@ -1892,6 +2000,12 @@ dependencies = [
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf8parse"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "valuable"
|
name = "valuable"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
[package]
|
[package]
|
||||||
name = "tasks"
|
name = "taskr"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.81"
|
anyhow = "1.0.81"
|
||||||
chrono = { version = "0.4.35", features = ["serde"] }
|
chrono = { version = "0.4.35", features = ["serde"] }
|
||||||
chrono-humanize = "0.2.3"
|
chrono-humanize = "0.2.3"
|
||||||
|
clap = { version = "4.5.4", features = ["derive"] }
|
||||||
|
clap_derive = "4.5.4"
|
||||||
color-eyre = "0.6.3"
|
color-eyre = "0.6.3"
|
||||||
crossterm = "0.27.0"
|
crossterm = "0.27.0"
|
||||||
ratatui = "0.26.2"
|
ratatui = "0.26.2"
|
||||||
|
|
65
src/cli.rs
Normal file
65
src/cli.rs
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
use crate::prelude::*;
|
||||||
|
use clap::{Args, Parser, Subcommand};
|
||||||
|
|
||||||
|
// TODO: clap for CLI
|
||||||
|
|
||||||
|
#[derive(Parser)]
|
||||||
|
#[command(version, about, long_about = None)]
|
||||||
|
#[command(propagate_version = true)]
|
||||||
|
pub struct Cli {
|
||||||
|
#[command(subcommand)]
|
||||||
|
pub command: Commands,
|
||||||
|
|
||||||
|
/// Tell taskr which directory to write log files to
|
||||||
|
#[arg(long, default_value = None)]
|
||||||
|
pub logs_directory: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Subcommand)]
|
||||||
|
pub enum Commands {
|
||||||
|
/// Run the interactive terminal user interface (TUI)
|
||||||
|
Ui(UiArgs),
|
||||||
|
|
||||||
|
/// Lists tasks with options for syncing
|
||||||
|
List(ListArgs),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Args)]
|
||||||
|
pub struct UiArgs {}
|
||||||
|
|
||||||
|
#[derive(Args, Debug)]
|
||||||
|
pub struct ListArgs {
|
||||||
|
#[arg(long, default_value_t = false)]
|
||||||
|
pub sync: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cli {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::parse()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn list_tasks(args: &ListArgs) -> Result<()> {
|
||||||
|
let tasks = crate::tasks::Tasks::try_new()?;
|
||||||
|
if args.sync {
|
||||||
|
eprintln!("Syncing...");
|
||||||
|
tasks.sync().await?;
|
||||||
|
}
|
||||||
|
// TODO: if we _don't_ sync, check last sync time and let user know
|
||||||
|
// that things may have changed
|
||||||
|
let tasks = tasks.all()?;
|
||||||
|
eprintln!("{} Tasks", tasks.len());
|
||||||
|
// TODO: make this generical? take a vec of vecs or something, scan each
|
||||||
|
// entry and their lengths and we can spit out a nice table with each
|
||||||
|
// "column" having equal length
|
||||||
|
for (_, t) in tasks.iter() {
|
||||||
|
println!("{t}");
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn verify_cli() {
|
||||||
|
use clap::CommandFactory;
|
||||||
|
Cli::command().debug_assert()
|
||||||
|
}
|
11
src/jira.rs
11
src/jira.rs
|
@ -139,9 +139,16 @@ impl Jira {
|
||||||
Ok(issues)
|
Ok(issues)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn issue(&self, key: &str) -> Result<Issue> {
|
||||||
|
self.client
|
||||||
|
.build(Method::GET, &format!("/rest/api/3/issue/{key}"))?
|
||||||
|
.res::<Issue>()
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: move this somewhere nicer?
|
// TODO: move this somewhere nicer?
|
||||||
pub fn by_key(issues: Vec<Issue>) -> Result<HashMap<String, Issue>> {
|
pub fn by_key(issues: Vec<Issue>) -> HashMap<String, Issue> {
|
||||||
Ok(issues.into_iter().map(|i| (i.key.to_owned(), i)).collect())
|
issues.into_iter().map(|i| (i.key.to_owned(), i)).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn assigned_open_issues(&self) -> Result<Vec<Issue>> {
|
pub async fn assigned_open_issues(&self) -> Result<Vec<Issue>> {
|
||||||
|
|
101
src/main.rs
101
src/main.rs
|
@ -1,104 +1,27 @@
|
||||||
#![warn(clippy::all)]
|
#![warn(clippy::all)]
|
||||||
|
|
||||||
|
mod cli;
|
||||||
mod client;
|
mod client;
|
||||||
mod config;
|
mod config;
|
||||||
mod gitlab;
|
mod gitlab;
|
||||||
mod jira;
|
mod jira;
|
||||||
|
mod observe;
|
||||||
|
mod prelude;
|
||||||
mod result;
|
mod result;
|
||||||
mod task;
|
mod task;
|
||||||
mod tasks;
|
mod tasks;
|
||||||
|
mod tui;
|
||||||
|
|
||||||
use crate::result::Result;
|
use crate::prelude::*;
|
||||||
use tasks::Tasks;
|
use cli::{Cli, Commands};
|
||||||
use tracing::{error, info};
|
|
||||||
use tracing_subscriber::{filter::LevelFilter, EnvFilter};
|
|
||||||
|
|
||||||
use crossterm::{
|
|
||||||
event::{self, KeyCode, KeyEventKind},
|
|
||||||
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
|
||||||
ExecutableCommand,
|
|
||||||
};
|
|
||||||
use ratatui::{
|
|
||||||
prelude::{CrosstermBackend, Stylize, Terminal},
|
|
||||||
widgets::Paragraph,
|
|
||||||
};
|
|
||||||
use std::io::stdout;
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
const ANSI_CLEAR: &'static str = "\x1b[2J\x1b[1;1H";
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
let logs_dir = xdg::BaseDirectories::new()?.create_cache_directory("taskr/logs")?;
|
let cli = Cli::new();
|
||||||
let file_appender = tracing_appender::rolling::hourly(logs_dir, "log");
|
observe::setup_logging(cli.logs_directory)?;
|
||||||
let (non_blocking, _guard) = tracing_appender::non_blocking(file_appender);
|
trace!("Starting...");
|
||||||
|
match cli.command {
|
||||||
color_eyre::install().expect("Failed to install color_eyre");
|
Commands::Ui(_args) => tui::run().await,
|
||||||
let filter = EnvFilter::builder()
|
Commands::List(args) => cli::list_tasks(&args).await,
|
||||||
.with_default_directive(LevelFilter::TRACE.into())
|
|
||||||
.parse_lossy("info,tasks=trace");
|
|
||||||
|
|
||||||
tracing_subscriber::fmt()
|
|
||||||
.with_writer(non_blocking)
|
|
||||||
.with_env_filter(filter)
|
|
||||||
.init();
|
|
||||||
|
|
||||||
match run().await {
|
|
||||||
Ok(()) => Ok(()),
|
|
||||||
Err(err) => {
|
|
||||||
error!("{err}");
|
|
||||||
Err(err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
async fn run() -> Result<()> {
|
|
||||||
let t = Tasks::try_new()?;
|
|
||||||
|
|
||||||
// print!("{ANSI_CLEAR}");
|
|
||||||
// let gitlab_user = tasks.gitlab.me().await?;
|
|
||||||
// info!("{gitlab_user:#?}");
|
|
||||||
// let jira_user = tasks.jira.me().await?;
|
|
||||||
// tasks.purge_all()?;
|
|
||||||
let tasks = t.all()?;
|
|
||||||
|
|
||||||
if tasks.len() < 1 {
|
|
||||||
info!("{:?}", t.sync().await?);
|
|
||||||
}
|
|
||||||
let mut vtasks: Vec<&task::Task> = tasks.values().collect();
|
|
||||||
vtasks.sort_unstable();
|
|
||||||
for t in &vtasks {
|
|
||||||
info!("{}", t);
|
|
||||||
}
|
|
||||||
info!("Number of tasks: {}", vtasks.len());
|
|
||||||
tui().await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn tui() -> Result<()> {
|
|
||||||
stdout().execute(EnterAlternateScreen)?;
|
|
||||||
enable_raw_mode()?;
|
|
||||||
let mut terminal = Terminal::new(CrosstermBackend::new(stdout()))?;
|
|
||||||
terminal.clear()?;
|
|
||||||
|
|
||||||
loop {
|
|
||||||
terminal.draw(|frame| {
|
|
||||||
let area = frame.size();
|
|
||||||
frame.render_widget(
|
|
||||||
Paragraph::new("Hello Ratatui! (press 'q' to quit)").white(),
|
|
||||||
area,
|
|
||||||
);
|
|
||||||
})?;
|
|
||||||
if event::poll(std::time::Duration::from_millis(10))? {
|
|
||||||
if let event::Event::Key(key) = event::read()? {
|
|
||||||
if key.kind == KeyEventKind::Press && key.code == KeyCode::Char('q') {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// TODO main loop
|
|
||||||
|
|
||||||
stdout().execute(LeaveAlternateScreen)?;
|
|
||||||
disable_raw_mode()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
32
src/observe.rs
Normal file
32
src/observe.rs
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
use std::path::PathBuf;
|
||||||
|
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)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let file_appender = tracing_appender::rolling::hourly(logs_directory, "log");
|
||||||
|
let (non_blocking, _guard) = tracing_appender::non_blocking(file_appender);
|
||||||
|
|
||||||
|
color_eyre::install().expect("Failed to install color_eyre");
|
||||||
|
let filter = EnvFilter::builder()
|
||||||
|
.with_default_directive(LevelFilter::TRACE.into())
|
||||||
|
.parse_lossy("info,taskr=trace");
|
||||||
|
|
||||||
|
tracing_subscriber::fmt()
|
||||||
|
.with_writer(non_blocking)
|
||||||
|
.with_env_filter(filter)
|
||||||
|
.init();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
4
src/prelude.rs
Normal file
4
src/prelude.rs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
#![allow(unused_imports)]
|
||||||
|
|
||||||
|
pub use crate::result::Result;
|
||||||
|
pub use tracing::{debug, error, info, trace, warn};
|
|
@ -69,7 +69,7 @@ impl Display for Task {
|
||||||
tags,
|
tags,
|
||||||
} = self;
|
} = self;
|
||||||
f.write_fmt(format_args!(
|
f.write_fmt(format_args!(
|
||||||
"{jira_key}: <{status}> {description} [p{jira_priority}]",
|
"{jira_key} {status:>10} {jira_priority} {description}",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,8 @@ pub struct Desyncs {
|
||||||
|
|
||||||
impl Tasks {
|
impl Tasks {
|
||||||
pub fn try_new() -> Result<Self> {
|
pub fn try_new() -> Result<Self> {
|
||||||
|
// TODO: cache, use keyring, talk to a daemon, or otherwise cache this safely
|
||||||
|
// TODO: or find a way to more-lazily load the token?
|
||||||
let gl_token = env::var("GITLAB_TOKEN").or_else(|_| -> Result<String> {
|
let gl_token = env::var("GITLAB_TOKEN").or_else(|_| -> Result<String> {
|
||||||
let output = Command::new("pass")
|
let output = Command::new("pass")
|
||||||
.arg("client/divvy/gitlab-glpat")
|
.arg("client/divvy/gitlab-glpat")
|
||||||
|
@ -38,6 +40,8 @@ impl Tasks {
|
||||||
})?;
|
})?;
|
||||||
let gitlab = GitLab::try_new("https://git.hq.bill.com/api/v4", &gl_token)?;
|
let gitlab = GitLab::try_new("https://git.hq.bill.com/api/v4", &gl_token)?;
|
||||||
|
|
||||||
|
// TODO: cache, use keyring, talk to a daemon, or otherwise cache this safely
|
||||||
|
// TODO: or find a way to more-lazily load the token?
|
||||||
let jira_token = env::var("JIRA_TOKEN").or_else(|_| -> Result<String> {
|
let jira_token = env::var("JIRA_TOKEN").or_else(|_| -> Result<String> {
|
||||||
let output = Command::new("pass")
|
let output = Command::new("pass")
|
||||||
.arg("client/divvy/jira-api-token-with-email")
|
.arg("client/divvy/jira-api-token-with-email")
|
||||||
|
@ -129,6 +133,8 @@ impl Tasks {
|
||||||
|
|
||||||
/// for a task that has no associated open and appropriately assigned jira issue
|
/// for a task that has no associated open and appropriately assigned jira issue
|
||||||
async fn fix_dangling_task(&self, task: &Task) -> Result<()> {
|
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?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,7 +142,7 @@ impl Tasks {
|
||||||
/// for use when sync'ing local tasks to remote state (jira, gitlab)
|
/// for use when sync'ing local tasks to remote state (jira, gitlab)
|
||||||
pub async fn sync(&self) -> Result<()> {
|
pub async fn sync(&self) -> Result<()> {
|
||||||
let mut tasks = self.all()?;
|
let mut tasks = self.all()?;
|
||||||
let issues = crate::jira::Jira::by_key(self.jira.assigned_open_issues().await?)?;
|
let issues = crate::jira::Jira::by_key(self.jira.assigned_open_issues().await?);
|
||||||
let task_keys: HashSet<String, RandomState> =
|
let task_keys: HashSet<String, RandomState> =
|
||||||
HashSet::from_iter(tasks.keys().map(|s| s.to_owned()));
|
HashSet::from_iter(tasks.keys().map(|s| s.to_owned()));
|
||||||
let issue_keys: HashSet<String, RandomState> =
|
let issue_keys: HashSet<String, RandomState> =
|
||||||
|
|
65
src/tui.rs
Normal file
65
src/tui.rs
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
use crate::prelude::*;
|
||||||
|
use crate::task;
|
||||||
|
use crate::tasks::Tasks;
|
||||||
|
|
||||||
|
use crossterm::{
|
||||||
|
event::{self, KeyCode, KeyEventKind},
|
||||||
|
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
||||||
|
ExecutableCommand,
|
||||||
|
};
|
||||||
|
use ratatui::{
|
||||||
|
prelude::{CrosstermBackend, Stylize, Terminal},
|
||||||
|
widgets::Paragraph,
|
||||||
|
};
|
||||||
|
use std::io::stdout;
|
||||||
|
|
||||||
|
pub async fn run() -> Result<()> {
|
||||||
|
let t = Tasks::try_new()?;
|
||||||
|
|
||||||
|
// print!("{ANSI_CLEAR}");
|
||||||
|
// let gitlab_user = tasks.gitlab.me().await?;
|
||||||
|
// info!("{gitlab_user:#?}");
|
||||||
|
// let jira_user = tasks.jira.me().await?;
|
||||||
|
// tasks.purge_all()?;
|
||||||
|
let tasks = t.all()?;
|
||||||
|
|
||||||
|
if tasks.len() < 1 {
|
||||||
|
info!("{:?}", t.sync().await?);
|
||||||
|
}
|
||||||
|
let mut vtasks: Vec<&task::Task> = tasks.values().collect();
|
||||||
|
vtasks.sort_unstable();
|
||||||
|
for t in &vtasks {
|
||||||
|
info!("{}", t);
|
||||||
|
}
|
||||||
|
info!("Number of tasks: {}", vtasks.len());
|
||||||
|
tui().await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn tui() -> Result<()> {
|
||||||
|
stdout().execute(EnterAlternateScreen)?;
|
||||||
|
enable_raw_mode()?;
|
||||||
|
let mut terminal = Terminal::new(CrosstermBackend::new(stdout()))?;
|
||||||
|
terminal.clear()?;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
terminal.draw(|frame| {
|
||||||
|
let area = frame.size();
|
||||||
|
frame.render_widget(
|
||||||
|
Paragraph::new("Hello Ratatui! (press 'q' to quit)").white(),
|
||||||
|
area,
|
||||||
|
);
|
||||||
|
})?;
|
||||||
|
if event::poll(std::time::Duration::from_millis(10))? {
|
||||||
|
if let event::Event::Key(key) = event::read()? {
|
||||||
|
if key.kind == KeyEventKind::Press && key.code == KeyCode::Char('q') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO main loop
|
||||||
|
|
||||||
|
stdout().execute(LeaveAlternateScreen)?;
|
||||||
|
disable_raw_mode()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
Loading…
Reference in a new issue