refactor
This commit is contained in:
parent
a11fe29048
commit
09f6dd0625
6 changed files with 53 additions and 70 deletions
|
@ -1,5 +1,3 @@
|
||||||
use std::collections::HashSet;
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
mod project;
|
mod project;
|
||||||
|
@ -47,24 +45,11 @@ where
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unique_tasks(tasks: Vec<Task>) -> Vec<Task> {
|
|
||||||
let mut seen_ids = HashSet::new();
|
|
||||||
let mut unique_tasks = Vec::with_capacity(tasks.len());
|
|
||||||
|
|
||||||
for task in tasks {
|
|
||||||
if seen_ids.insert(task.id) {
|
|
||||||
unique_tasks.push(task);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unique_tasks
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ProjectID(pub isize);
|
pub struct ProjectID(pub isize);
|
||||||
|
|
||||||
impl ProjectID {
|
impl ProjectID {
|
||||||
pub fn parse(api: &VikunjaAPI, project: &str) -> Option<Self> {
|
pub fn parse(api: &VikunjaAPI, project: &str) -> Option<Self> {
|
||||||
let project = project.trim_start_matches("#");
|
let project = project.trim_start_matches('#');
|
||||||
|
|
||||||
if let Ok(num) = project.parse() {
|
if let Ok(num) = project.parse() {
|
||||||
Some(Self(num))
|
Some(Self(num))
|
||||||
|
@ -95,29 +80,30 @@ impl VikunjaAPI {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_request(&self, path: &str) -> String {
|
fn get_request(&self, path: &str) -> String {
|
||||||
if let Some(cached) = self.cache.get(path) {
|
self.cache.get(path).map_or_else(
|
||||||
cached
|
|| {
|
||||||
} else {
|
let client = reqwest::blocking::Client::new();
|
||||||
let client = reqwest::blocking::Client::new();
|
|
||||||
|
|
||||||
let ret = client
|
let ret = client
|
||||||
.get(&format!("{}/api/v1{}", self.host, path))
|
.get(format!("{}/api/v1{}", self.host, path))
|
||||||
.header("Authorization", format!("Bearer {}", self.token))
|
.header("Authorization", format!("Bearer {}", self.token))
|
||||||
.send()
|
.send()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.text()
|
.text()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
self.cache.insert(path.to_string(), ret.clone());
|
self.cache.insert(path.to_string(), ret.clone());
|
||||||
ret
|
ret
|
||||||
}
|
},
|
||||||
|
|cached| cached,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn put_request(&self, path: &str, data: serde_json::Value) -> String {
|
fn put_request(&self, path: &str, data: &serde_json::Value) -> String {
|
||||||
let client = reqwest::blocking::Client::new();
|
let client = reqwest::blocking::Client::new();
|
||||||
|
|
||||||
client
|
client
|
||||||
.put(&format!("{}/api/v1{}", self.host, path))
|
.put(format!("{}/api/v1{}", self.host, path))
|
||||||
.header("Authorization", format!("Bearer {}", self.token))
|
.header("Authorization", format!("Bearer {}", self.token))
|
||||||
.json(&data)
|
.json(&data)
|
||||||
.send()
|
.send()
|
||||||
|
@ -126,11 +112,11 @@ impl VikunjaAPI {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn post_request(&self, path: &str, data: serde_json::Value) -> String {
|
fn post_request(&self, path: &str, data: &serde_json::Value) -> String {
|
||||||
let client = reqwest::blocking::Client::new();
|
let client = reqwest::blocking::Client::new();
|
||||||
|
|
||||||
client
|
client
|
||||||
.post(&format!("{}/api/v1{}", self.host, path))
|
.post(format!("{}/api/v1{}", self.host, path))
|
||||||
.header("Authorization", format!("Bearer {}", self.token))
|
.header("Authorization", format!("Bearer {}", self.token))
|
||||||
.json(&data)
|
.json(&data)
|
||||||
.send()
|
.send()
|
||||||
|
@ -180,7 +166,7 @@ impl VikunjaAPI {
|
||||||
serde_json::from_str(&resp).unwrap()
|
serde_json::from_str(&resp).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_task(&self, title: &str, project: ProjectID) -> Task {
|
pub fn new_task(&self, title: &str, project: &ProjectID) -> Task {
|
||||||
let id = project.0;
|
let id = project.0;
|
||||||
|
|
||||||
let data = serde_json::json!({
|
let data = serde_json::json!({
|
||||||
|
@ -194,14 +180,14 @@ impl VikunjaAPI {
|
||||||
// labels
|
// labels
|
||||||
// priority
|
// priority
|
||||||
|
|
||||||
let resp = self.put_request(&format!("/projects/{id}/tasks"), data);
|
let resp = self.put_request(&format!("/projects/{id}/tasks"), &data);
|
||||||
serde_json::from_str(&resp).unwrap()
|
serde_json::from_str(&resp).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn done_task(&self, task_id: isize) -> Task {
|
pub fn done_task(&self, task_id: isize) -> Task {
|
||||||
let resp = self.post_request(
|
let resp = self.post_request(
|
||||||
&format!("/tasks/{task_id}"),
|
&format!("/tasks/{task_id}"),
|
||||||
serde_json::json!({
|
&serde_json::json!({
|
||||||
"done": true
|
"done": true
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
|
@ -18,6 +18,8 @@ pub fn get_args() -> clap::ArgMatches {
|
||||||
.name("prj")
|
.name("prj")
|
||||||
.about("Commands about projects")
|
.about("Commands about projects")
|
||||||
.subcommand(command!().name("ls").about("List projects")),
|
.subcommand(command!().name("ls").about("List projects")),
|
||||||
|
// todo : prj add <project>
|
||||||
|
// todo : prj rm <project>
|
||||||
)
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
command!()
|
command!()
|
||||||
|
@ -35,6 +37,9 @@ pub fn get_args() -> clap::ArgMatches {
|
||||||
.name("label")
|
.name("label")
|
||||||
.about("Manage labels")
|
.about("Manage labels")
|
||||||
.subcommand(command!().name("ls").about("List all labels")),
|
.subcommand(command!().name("ls").about("List all labels")),
|
||||||
|
// todo : label new
|
||||||
|
// todo : label rm
|
||||||
|
// todo : label <label> <task>
|
||||||
)
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
command!()
|
command!()
|
||||||
|
|
|
@ -20,6 +20,7 @@ fn main() {
|
||||||
Some(("ls", _)) => {
|
Some(("ls", _)) => {
|
||||||
ui::project::list_projects(&api);
|
ui::project::list_projects(&api);
|
||||||
}
|
}
|
||||||
|
Some(("add", add_prj_arg)) => {}
|
||||||
_ => {
|
_ => {
|
||||||
ui::project::list_projects(&api);
|
ui::project::list_projects(&api);
|
||||||
}
|
}
|
||||||
|
@ -28,15 +29,16 @@ fn main() {
|
||||||
Some(("ls", _)) => {
|
Some(("ls", _)) => {
|
||||||
ui::print_all_labels(&api);
|
ui::print_all_labels(&api);
|
||||||
}
|
}
|
||||||
|
Some(("new", new_label_arg)) => {}
|
||||||
_ => {
|
_ => {
|
||||||
ui::print_all_labels(&api);
|
// todo : label tasks
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Some(("new", new_task_arg)) => {
|
Some(("new", new_task_arg)) => {
|
||||||
let title: &String = new_task_arg.get_one("title").unwrap();
|
let title: &String = new_task_arg.get_one("title").unwrap();
|
||||||
let project: &String = new_task_arg.get_one("project").unwrap();
|
let project: &String = new_task_arg.get_one("project").unwrap();
|
||||||
let project = ProjectID::parse(&api, project).unwrap();
|
let project = ProjectID::parse(&api, project).unwrap();
|
||||||
let task = api.new_task(title.as_str(), project);
|
let task = api.new_task(title.as_str(), &project);
|
||||||
ui::task::print_task_info(task.id, &api);
|
ui::task::print_task_info(task.id, &api);
|
||||||
}
|
}
|
||||||
Some(("done", done_args)) => {
|
Some(("done", done_args)) => {
|
||||||
|
|
|
@ -46,10 +46,7 @@ fn parse_datetime(datetime_str: &str) -> Option<DateTime<Utc>> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
match DateTime::parse_from_rfc3339(datetime_str) {
|
DateTime::parse_from_rfc3339(datetime_str).map_or(None, |dt| Some(dt.with_timezone(&Utc)))
|
||||||
Ok(dt) => Some(dt.with_timezone(&Utc)),
|
|
||||||
Err(_) => None, // Return None if parsing fails
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a formatted time duration
|
/// Return a formatted time duration
|
||||||
|
@ -58,19 +55,19 @@ pub fn time_since(event: DateTime<Utc>) -> String {
|
||||||
let duration = now.signed_duration_since(event);
|
let duration = now.signed_duration_since(event);
|
||||||
|
|
||||||
if duration.num_days() > 0 {
|
if duration.num_days() > 0 {
|
||||||
return format!("{}d ago", duration.num_days());
|
format!("{}d ago", duration.num_days())
|
||||||
} else if duration.num_hours() > 0 {
|
} else if duration.num_hours() > 0 {
|
||||||
return format!("{}h ago", duration.num_hours());
|
format!("{}h ago", duration.num_hours())
|
||||||
} else if duration.num_minutes() > 0 {
|
} else if duration.num_minutes() > 0 {
|
||||||
return format!("{}m ago", duration.num_minutes());
|
format!("{}m ago", duration.num_minutes())
|
||||||
} else {
|
} else {
|
||||||
return "Just now".to_string();
|
"Just now".to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_label(label: &Label) {
|
fn print_label(label: &Label) {
|
||||||
let color = hex_to_color(&label.hex_color).unwrap();
|
let color = hex_to_color(&label.hex_color).unwrap();
|
||||||
print_color_bg(color, &label.title.trim());
|
print_color_bg(color, label.title.trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_all_labels(api: &VikunjaAPI) {
|
pub fn print_all_labels(api: &VikunjaAPI) {
|
||||||
|
|
|
@ -15,7 +15,7 @@ pub fn list_projects(api: &VikunjaAPI) {
|
||||||
for prj in projects {
|
for prj in projects {
|
||||||
project_map
|
project_map
|
||||||
.entry(prj.parent_project_id)
|
.entry(prj.parent_project_id)
|
||||||
.or_insert_with(Vec::new)
|
.or_default()
|
||||||
.push(prj);
|
.push(prj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ pub fn list_projects(api: &VikunjaAPI) {
|
||||||
hex_to_color(&prj.hex_color).unwrap()
|
hex_to_color(&prj.hex_color).unwrap()
|
||||||
};
|
};
|
||||||
print_color(color, &prj.title);
|
print_color(color, &prj.title);
|
||||||
print!(" [{}]\n", prj.id);
|
println!(" [{}]", prj.id);
|
||||||
|
|
||||||
if let Some(sub_projects) = project_map.get(&(prj.id as usize)) {
|
if let Some(sub_projects) = project_map.get(&(prj.id as usize)) {
|
||||||
for sub_prj in sub_projects {
|
for sub_prj in sub_projects {
|
||||||
|
@ -36,7 +36,7 @@ pub fn list_projects(api: &VikunjaAPI) {
|
||||||
hex_to_color(&sub_prj.hex_color).unwrap()
|
hex_to_color(&sub_prj.hex_color).unwrap()
|
||||||
};
|
};
|
||||||
print_color(color, &format!(" - {}", sub_prj.title));
|
print_color(color, &format!(" - {}", sub_prj.title));
|
||||||
print!(" [{}]\n", sub_prj.id);
|
println!(" [{}]", sub_prj.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
api::{Label, ProjectID, Task, VikunjaAPI},
|
api::{ProjectID, Task, VikunjaAPI},
|
||||||
ui::{hex_to_color, parse_datetime, print_color, print_color_bg, print_label, time_since},
|
ui::{parse_datetime, print_color, print_label, time_since},
|
||||||
};
|
};
|
||||||
|
|
||||||
fn print_task_oneline(task: &Task, api: &VikunjaAPI) {
|
fn print_task_oneline(task: &Task, api: &VikunjaAPI) {
|
||||||
|
@ -30,7 +30,7 @@ fn print_task_oneline(task: &Task, api: &VikunjaAPI) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
print!("\n");
|
println!();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_current_tasks(
|
pub fn print_current_tasks(
|
||||||
|
@ -57,27 +57,20 @@ pub fn print_current_tasks(
|
||||||
|
|
||||||
if let Some(project) = project {
|
if let Some(project) = project {
|
||||||
let p_id = ProjectID::parse(api, project).unwrap();
|
let p_id = ProjectID::parse(api, project).unwrap();
|
||||||
|
selection.retain(|x| x.project_id == p_id.0);
|
||||||
selection = selection
|
|
||||||
.into_iter()
|
|
||||||
.filter(|x| x.project_id == p_id.0)
|
|
||||||
.collect();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(label_match) = label {
|
if let Some(label_match) = label {
|
||||||
selection = selection
|
selection.retain(|x| {
|
||||||
.into_iter()
|
if let Some(labels) = &x.labels {
|
||||||
.filter(|x| {
|
for label in labels {
|
||||||
if let Some(labels) = &x.labels {
|
if label.title.trim() == *label_match {
|
||||||
for label in labels {
|
return true;
|
||||||
if label.title.trim() == *label_match {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
false
|
}
|
||||||
})
|
false
|
||||||
.collect();
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
for task in selection {
|
for task in selection {
|
||||||
|
|
Loading…
Reference in a new issue