aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Gattozzi <mgattozzi@gmail.com>2019-12-13 13:49:33 -0500
committerMichael Gattozzi <mgattozzi@gmail.com>2019-12-13 13:49:33 -0500
commitb32b1478dec75cc8282036bae56097a1e29fb47f (patch)
treeaac3dd036d940224b080c16c5acca3932fca6be6
parenta06e021420e8c374d1e5f76fe69012bec9202e22 (diff)
Add the ability to add comments to tickets
This commit encompasses quite a few changes to add tickets: - The `Ticket` struct is updated to properly order comments using a uuid v1 and to hold the user name, uuid, and their comment - Tickets in the repo are updated to accomodate this change to the ticket. Despite this being a breaking config change, none of these tickets had any comments so it was an easy manual port and the migration tool did not need to be updated. - The TUI was updated to display the tickets a bit better with some coloring and now also showing the comments with them This gets us one step closer to a decent first release for ticket. The only things that are really left to do are adding the ability to comment in the tui, listing tickets on the cli, and adding in issue assignees on both the cli and tui.
-rw-r--r--.dev-suite/ticket/closed/create-a-find_root-function.toml3
-rw-r--r--.dev-suite/ticket/closed/create-a-tui-for-ticket.toml3
-rw-r--r--.dev-suite/ticket/closed/create-git-hooks-tool.toml3
-rw-r--r--.dev-suite/ticket/open/add-ability-to-edit-tickets.toml3
-rw-r--r--Cargo.lock2
-rw-r--r--configamajig/src/lib.rs6
-rw-r--r--ticket/Cargo.toml5
-rw-r--r--ticket/src/actions.rs27
-rw-r--r--ticket/src/main.rs85
-rw-r--r--ticket/src/tui.rs22
10 files changed, 121 insertions, 38 deletions
diff --git a/.dev-suite/ticket/closed/create-a-find_root-function.toml b/.dev-suite/ticket/closed/create-a-find_root-function.toml
index 49a550c..50aa38f 100644
--- a/.dev-suite/ticket/closed/create-a-find_root-function.toml
+++ b/.dev-suite/ticket/closed/create-a-find_root-function.toml
@@ -11,5 +11,6 @@ a more generalized function and be shared amongst future tools as this
will be a common operation. It shouldn't matter where in a repo you are,
the tools should just work.
'''
-comments = []
version = 'V1'
+
+[comments]
diff --git a/.dev-suite/ticket/closed/create-a-tui-for-ticket.toml b/.dev-suite/ticket/closed/create-a-tui-for-ticket.toml
index 999e3c9..2064b8e 100644
--- a/.dev-suite/ticket/closed/create-a-tui-for-ticket.toml
+++ b/.dev-suite/ticket/closed/create-a-tui-for-ticket.toml
@@ -9,5 +9,6 @@ in terminal experience, almost as if they never left GitHub. A basic
tui that allows one to read and comment on issues will suffice to close
this ticket.
'''
-comments = []
version = 'V1'
+
+[comments]
diff --git a/.dev-suite/ticket/closed/create-git-hooks-tool.toml b/.dev-suite/ticket/closed/create-git-hooks-tool.toml
index 4b929d9..53bdefc 100644
--- a/.dev-suite/ticket/closed/create-git-hooks-tool.toml
+++ b/.dev-suite/ticket/closed/create-git-hooks-tool.toml
@@ -10,5 +10,6 @@ repo is a good thing to standardizing checks across a team to avoid
issues that just get caught in CI way later when it would be trivial to
catch with a script.
'''
-comments = []
version = 'V1'
+
+[comments]
diff --git a/.dev-suite/ticket/open/add-ability-to-edit-tickets.toml b/.dev-suite/ticket/open/add-ability-to-edit-tickets.toml
index d668b68..4be73bc 100644
--- a/.dev-suite/ticket/open/add-ability-to-edit-tickets.toml
+++ b/.dev-suite/ticket/open/add-ability-to-edit-tickets.toml
@@ -8,5 +8,6 @@ inside of the git repo itself. While it works it's hardly ideal. The
ability to edit tickets by specifiying the id with an accompanying cli
arg to go with it would be enough to close this.
'''
-comments = []
version = 'V1'
+
+[comments]
diff --git a/Cargo.lock b/Cargo.lock
index 3b3d24a..00aeaac 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -959,9 +959,11 @@ dependencies = [
"anyhow 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)",
"chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
"colored 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "configamajig 0.1.0",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"paw 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pretty_env_logger 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rustyline 5.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
"shared 0.1.0",
diff --git a/configamajig/src/lib.rs b/configamajig/src/lib.rs
index 6585be6..b67d4f2 100644
--- a/configamajig/src/lib.rs
+++ b/configamajig/src/lib.rs
@@ -88,8 +88,10 @@ pub fn set_repo_config(repo_config: RepoConfig) -> Result<()> {
/// User Config struct
#[derive(Serialize, Deserialize, Debug)]
pub struct UserConfig {
- name: String,
- uuid: Uuid,
+ /// The name of the user using dev-suite
+ pub name: String,
+ /// The uuid of the user using dev-suite
+ pub uuid: Uuid,
}
impl UserConfig {
diff --git a/ticket/Cargo.toml b/ticket/Cargo.toml
index d6fb260..574251a 100644
--- a/ticket/Cargo.toml
+++ b/ticket/Cargo.toml
@@ -10,8 +10,13 @@ license = "GPL-3.0"
[dependencies]
anyhow = "1.0"
colored = "1.9"
+configamajig = { path = "../configamajig" }
chrono = "0.4"
paw = "1.0"
+# Pinning a specific version in case they break everything again and mess up semver,
+# though this won't fix if they break the semver on the core crate. Either way I don't
+# trust this crate enough to not pin it.
+rand = "0.7.2"
rustyline = "5.0"
serde = { version = "1.0", features = ["derive"] }
shared = { path = "../shared" }
diff --git a/ticket/src/actions.rs b/ticket/src/actions.rs
index 0d371b3..b6d8cb0 100644
--- a/ticket/src/actions.rs
+++ b/ticket/src/actions.rs
@@ -6,15 +6,31 @@ use anyhow::{
bail,
Result,
};
+use chrono::prelude::*;
use log::*;
+use rand::prelude::*;
use shared::find_root;
use std::{
+ convert::TryInto,
fs,
path::{
Path,
PathBuf,
},
};
+use uuid::{
+ v1::{
+ Context,
+ Timestamp,
+ },
+ Uuid,
+};
+
+pub fn get_all_tickets() -> Result<Vec<Ticket>> {
+ let mut tickets = get_open_tickets()?;
+ tickets.extend(get_closed_tickets()?);
+ Ok(tickets)
+}
pub fn get_open_tickets() -> Result<Vec<Ticket>> {
get_tickets(&open_tickets()?)
@@ -90,3 +106,14 @@ fn get_ticketsv0(path: &Path) -> Result<Vec<TicketV0>> {
out.sort_by(|a, b| a.number.cmp(&b.number));
Ok(out)
}
+
+pub fn uuid_v1() -> Result<Uuid> {
+ Ok(Uuid::new_v1(
+ Timestamp::from_unix(
+ Context::new(random()),
+ Utc::now().timestamp().try_into()?,
+ 0,
+ ),
+ &[random(), random(), random(), random(), random(), random()],
+ )?)
+}
diff --git a/ticket/src/main.rs b/ticket/src/main.rs
index 74a5699..c889da2 100644
--- a/ticket/src/main.rs
+++ b/ticket/src/main.rs
@@ -7,10 +7,11 @@ mod tui;
use actions::*;
use anyhow::{
bail,
+ format_err,
Result,
};
-use chrono::prelude::*;
use colored::*;
+use configamajig::*;
use log::*;
use rustyline::{
error::ReadlineError,
@@ -21,7 +22,7 @@ use serde::{
Serialize,
};
use std::{
- convert::TryInto,
+ collections::BTreeMap,
env,
fs,
process,
@@ -29,13 +30,7 @@ use std::{
thread,
time,
};
-use uuid::{
- v1::{
- Context,
- Timestamp,
- },
- Uuid,
-};
+use uuid::Uuid;
#[derive(structopt::StructOpt)]
struct Args {
@@ -53,8 +48,10 @@ enum Cmd {
New,
/// Show a ticket on the command line
Show { id: Uuid },
- /// Close a ticket on the command line
+ /// Close a ticket from the command line
Close { id: Uuid },
+ /// Comment on a ticket from the command line
+ Comment { id: Uuid, message: String },
}
#[paw::main]
@@ -71,6 +68,7 @@ fn main(args: Args) {
Cmd::Migrate => migrate(),
Cmd::Show { id } => show(id),
Cmd::Close { id } => close(id),
+ Cmd::Comment { id, message } => comment(id, message),
} {
error!("{}", e);
std::process::exit(1);
@@ -134,17 +132,10 @@ fn new() -> Result<()> {
let t = Ticket {
title,
status: Status::Open,
- id: Uuid::new_v1(
- Timestamp::from_unix(
- Context::new(1),
- Utc::now().timestamp().try_into()?,
- 0,
- ),
- &[0, 5, 2, 4, 9, 3],
- )?,
+ id: uuid_v1()?,
assignees: Vec::new(),
description: description_contents,
- comments: Vec::new(),
+ comments: BTreeMap::new(),
version: Version::V1,
};
@@ -195,7 +186,7 @@ fn show(id: Uuid) -> Result<()> {
);
}
- print!(
+ println!(
"{}{}\n\n{}",
"Status: ".bold().purple(),
match ticket.status {
@@ -204,6 +195,9 @@ fn show(id: Uuid) -> Result<()> {
},
ticket.description
);
+ for (_, name, comment) in ticket.comments.values() {
+ println!("{}\n{}\n", name.0.cyan(), comment.0);
+ }
found = true;
break;
}
@@ -256,7 +250,6 @@ fn close(id: Uuid) -> Result<()> {
/// Upgrade from V0 to V1 of the ticket
fn migrate() -> Result<()> {
- let ctx = Context::new(1);
let tickets = get_all_ticketsv0()?;
let open_tickets_path = open_tickets()?;
@@ -266,13 +259,10 @@ fn migrate() -> Result<()> {
let ticket = Ticket {
title: t.title,
status: t.status,
- id: Uuid::new_v1(
- Timestamp::from_unix(&ctx, Utc::now().timestamp().try_into()?, 0),
- &[0, 5, 2, 4, 9, 3],
- )?,
+ id: uuid_v1()?,
assignees: t.assignee.map_or_else(Vec::new, |a| vec![a]),
description: t.description,
- comments: Vec::new(),
+ comments: BTreeMap::new(),
version: Version::V1,
};
@@ -297,6 +287,38 @@ fn migrate() -> Result<()> {
Ok(())
}
+fn comment(id: Uuid, message: String) -> Result<()> {
+ let mut ticket = get_all_tickets()?
+ .into_iter()
+ .find(|t| t.id == id)
+ .ok_or_else(|| {
+ format_err!("The uuid '{}' is not associated with any ticket")
+ })?;
+ let user_config = get_user_config()?;
+ let _ = ticket.comments.insert(
+ uuid_v1()?,
+ (user_config.uuid, Name(user_config.name), Comment(message)),
+ );
+ for (k, v) in &ticket.comments {
+ info!("{:?} = {:?}", k, v);
+ }
+ let open_tickets_path = open_tickets()?;
+ let closed_tickets_path = closed_tickets()?;
+ let path = match ticket.status {
+ Status::Open => &open_tickets_path,
+ Status::Closed => &closed_tickets_path,
+ };
+ let mut name = ticket
+ .title
+ .split_whitespace()
+ .collect::<Vec<&str>>()
+ .join("-");
+ name.push_str(".toml");
+ name = name.to_lowercase();
+ fs::write(path.join(&name), toml::to_string_pretty(&ticket)?)?;
+
+ Ok(())
+}
#[derive(Serialize, Deserialize, Debug)]
/// The fundamental type this tool revolves around. The ticket represents
/// everything about an issue or future plan for the code base.
@@ -306,8 +328,9 @@ pub struct Ticket {
id: Uuid,
assignees: Vec<String>,
description: String,
- comments: Vec<(User, String)>,
version: Version,
+ #[serde(serialize_with = "toml::ser::tables_last")]
+ comments: BTreeMap<Uuid, (Uuid, Name, Comment)>,
}
#[derive(Serialize, Deserialize, Debug)]
@@ -319,8 +342,12 @@ pub enum Version {
}
#[derive(Serialize, Deserialize, Debug)]
-/// Newtype to represent a User or maintainer
-pub struct User(String);
+/// Newtype to represent a users Name
+pub struct Name(String);
+
+#[derive(Serialize, Deserialize, Debug)]
+/// Newtype to represent a Comment
+pub struct Comment(String);
#[derive(Serialize, Deserialize, Debug)]
/// Original version of the tickets on disk. This exists for historical reasons
diff --git a/ticket/src/tui.rs b/ticket/src/tui.rs
index 01f00bf..57c8aca 100644
--- a/ticket/src/tui.rs
+++ b/ticket/src/tui.rs
@@ -233,7 +233,7 @@ pub fn run() -> Result<()> {
app.table("Open").render(&mut f, horizontal[0]);
Paragraph::new(app.description("Open").iter())
- .block(Block::default().title("Description").borders(Borders::ALL))
+ .block(Block::default().borders(Borders::ALL))
.alignment(Alignment::Left)
.wrap(true)
.render(&mut f, horizontal[1]);
@@ -242,7 +242,7 @@ pub fn run() -> Result<()> {
app.table("Closed").render(&mut f, horizontal[0]);
Paragraph::new(app.description("Closed").iter())
- .block(Block::default().title("Description").borders(Borders::ALL))
+ .block(Block::default().borders(Borders::ALL))
.alignment(Alignment::Left)
.wrap(true)
.render(&mut f, horizontal[1]);
@@ -314,7 +314,23 @@ impl<'a> App<'a> {
let mut description = vec![];
for (idx, i) in self.tickets.tickets.get(tab).unwrap().iter().enumerate() {
if idx == self.tickets.index {
- description = vec![Text::raw(i.description.to_owned())];
+ description = {
+ let header = Style::default().fg(Color::Red).modifier(Modifier::BOLD);
+ let mut desc = vec![
+ Text::styled("Description\n-------------\n", header),
+ Text::raw(i.description.to_owned()),
+ ];
+ let name_style =
+ Style::default().fg(Color::Cyan).modifier(Modifier::BOLD);
+ if i.comments.is_empty() {
+ desc.push(Text::styled("\nComments\n--------\n", header));
+ for (_, name, comment) in i.comments.values() {
+ desc.push(Text::styled(format!("\n{}\n", name.0), name_style));
+ desc.push(Text::raw(format!("{}\n", comment.0)));
+ }
+ }
+ desc
+ };
break;
}
}