Compare commits
No commits in common. "0c459e2770ae299ab34ad2302f3ab5b1bbd88515" and "86fe96f0cde83ee98fb5ecaad492cfe319ff2925" have entirely different histories.
0c459e2770
...
86fe96f0cd
11 changed files with 143 additions and 325 deletions
87
Cargo.lock
generated
87
Cargo.lock
generated
|
@ -256,6 +256,16 @@ version = "1.0.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||
|
||||
[[package]]
|
||||
name = "colored"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "config"
|
||||
version = "0.15.6"
|
||||
|
@ -329,31 +339,6 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm"
|
||||
version = "0.28.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"crossterm_winapi",
|
||||
"mio",
|
||||
"parking_lot",
|
||||
"rustix",
|
||||
"signal-hook",
|
||||
"signal-hook-mio",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm_winapi"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crunchy"
|
||||
version = "0.2.2"
|
||||
|
@ -935,8 +920,8 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"chrono",
|
||||
"clap",
|
||||
"colored",
|
||||
"config",
|
||||
"crossterm",
|
||||
"directories",
|
||||
"gray_matter",
|
||||
"open",
|
||||
|
@ -970,6 +955,12 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.169"
|
||||
|
@ -1042,7 +1033,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
@ -1555,27 +1545,6 @@ version = "1.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"signal-hook-registry",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-mio"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"mio",
|
||||
"signal-hook",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.2"
|
||||
|
@ -2084,28 +2053,6 @@ dependencies = [
|
|||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.52.0"
|
||||
|
|
|
@ -9,6 +9,7 @@ reqwest = { version = "0.12", features = ["json"] }
|
|||
tokio = { version = "1.0", features = ["full"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
colored = "2.0"
|
||||
toml = "0.8"
|
||||
config = "0.15"
|
||||
directories = "6.0"
|
||||
|
@ -17,4 +18,3 @@ gray_matter = { version = "0.2", default-features = false, features = ["toml"] }
|
|||
open = "5.2"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
tabwriter = "1.4"
|
||||
crossterm = "0.28"
|
34
readme.md
34
readme.md
|
@ -24,10 +24,9 @@ cargo install jirac
|
|||
Usage: jirac <COMMAND>
|
||||
|
||||
Commands:
|
||||
create Create an issue
|
||||
list Find issues currently assigned to you
|
||||
search Search for issues
|
||||
init Set up the configuration
|
||||
create Create a ticket
|
||||
list Find tickets currently assigned to you
|
||||
init Setup the configuration
|
||||
help Print this message or the help of the given subcommand(s)
|
||||
|
||||
Options:
|
||||
|
@ -69,7 +68,7 @@ jirac create ticket.md
|
|||
jirac create --project KEY
|
||||
```
|
||||
|
||||
### Listing tickets
|
||||
## Listing tickets
|
||||
|
||||
```
|
||||
Find tickets currently assigned to you
|
||||
|
@ -81,29 +80,6 @@ Options:
|
|||
-h, --help Print help
|
||||
```
|
||||
|
||||
### Search for tickets
|
||||
|
||||
```
|
||||
Search for issues
|
||||
|
||||
Usage: jirac search [OPTIONS] <JQL>
|
||||
|
||||
Arguments:
|
||||
<JQL> A JQL string
|
||||
|
||||
Options:
|
||||
--json Print JSON rather than pretty print
|
||||
-h, --help Print help
|
||||
```
|
||||
|
||||
Use [JQL](https://support.atlassian.com/jira-software-cloud/docs/use-advanced-search-with-jira-query-language-jql/) to search for Issues.
|
||||
|
||||
*Find all in-progress tickets in a project*
|
||||
|
||||
```
|
||||
jirac search 'project = KEY AND status = "In Progress" ORDER BY created DESC'
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Get the following information:
|
||||
|
@ -112,7 +88,7 @@ Get the following information:
|
|||
* You email
|
||||
* [An API key](https://id.atlassian.com/manage-profile/security/api-tokens)
|
||||
|
||||
Then run the `jirac init` command
|
||||
Then run the the `jirac init` command
|
||||
|
||||
```
|
||||
Setup the configuration
|
||||
|
|
24
src/cli.rs
24
src/cli.rs
|
@ -10,37 +10,27 @@ pub struct Cli {
|
|||
|
||||
#[derive(Subcommand)]
|
||||
pub enum Commands {
|
||||
/// Create an issue
|
||||
/// Create a ticket
|
||||
Create {
|
||||
/// The project key in which to create the issue
|
||||
/// The project key in which to create the ticket
|
||||
#[arg(long)]
|
||||
project: Option<String>,
|
||||
|
||||
/// Open the new issue in a browser
|
||||
/// Open the new ticket in a browser
|
||||
#[arg(long)]
|
||||
open: bool,
|
||||
|
||||
/// A Markdown file
|
||||
/// A markdown file
|
||||
#[arg(value_name = "MARKDOWN_FILE")]
|
||||
markdown_file: Option<PathBuf>,
|
||||
},
|
||||
/// Find issues currently assigned to you
|
||||
/// Find tickets currently assigned to you
|
||||
List {
|
||||
/// Print JSON rather than pretty print
|
||||
/// Print json rather than pretty print
|
||||
#[arg(long)]
|
||||
json: bool,
|
||||
},
|
||||
/// Search for issues
|
||||
Search {
|
||||
/// Print JSON rather than pretty print
|
||||
#[arg(long)]
|
||||
json: bool,
|
||||
|
||||
/// A JQL string
|
||||
#[arg(value_name = "JQL")]
|
||||
jql: String,
|
||||
},
|
||||
/// Set up the configuration
|
||||
/// Setup the configuration
|
||||
Init {
|
||||
/// Jira instance URL
|
||||
#[arg(long)]
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
pub mod create;
|
||||
pub mod list;
|
||||
pub mod search;
|
||||
|
|
116
src/cmd/list.rs
116
src/cmd/list.rs
|
@ -1,5 +1,115 @@
|
|||
use crate::jira_config::JiraConfig;
|
||||
use crate::types::issue::{display_issues_json, display_issues_pretty};
|
||||
use colored::Colorize;
|
||||
use reqwest::header::{HeaderMap, HeaderValue, CONTENT_TYPE};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::io::Write;
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
struct JiraIssue {
|
||||
key: String,
|
||||
#[serde(rename = "self")]
|
||||
href: String,
|
||||
fields: JiraIssueResponseFields,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
struct JiraIssueResponseFields {
|
||||
summary: String,
|
||||
status: Status,
|
||||
created: chrono::DateTime<chrono::Utc>,
|
||||
priority: Priority,
|
||||
assignee: Person,
|
||||
reporter: Person,
|
||||
creator: Person,
|
||||
#[serde(rename = "duedate")]
|
||||
due_date: Option<chrono::NaiveDate>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct JiraSearchResponse {
|
||||
issues: Vec<JiraIssue>,
|
||||
total: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
struct Status {
|
||||
name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
struct Priority {
|
||||
name: String,
|
||||
id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
struct Person {
|
||||
#[serde(rename = "self")]
|
||||
href: String,
|
||||
#[serde(rename = "displayName")]
|
||||
display_name: String,
|
||||
#[serde(rename = "accountId")]
|
||||
account_id: String,
|
||||
#[serde(rename = "emailAddress")]
|
||||
email_address: String,
|
||||
}
|
||||
|
||||
async fn list_jira_issues(
|
||||
config: &JiraConfig,
|
||||
) -> Result<JiraSearchResponse, Box<dyn std::error::Error>> {
|
||||
let client = reqwest::Client::new();
|
||||
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
|
||||
|
||||
// JQL query to find issues assigned to the specified user
|
||||
let jql =
|
||||
"assignee = currentUser() AND resolution = Unresolved order by updated DESC".to_string();
|
||||
let query = [("jql", jql.as_str())];
|
||||
|
||||
let response = client
|
||||
.get(format!("{}/rest/api/2/search", config.url))
|
||||
.basic_auth(&config.email, Some(&config.api_token))
|
||||
.headers(headers)
|
||||
.query(&query)
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
let error_text = response.text().await?;
|
||||
return Err(format!("Failed to fetch issues: {}", error_text).into());
|
||||
}
|
||||
|
||||
Ok(response.json::<JiraSearchResponse>().await?)
|
||||
}
|
||||
|
||||
fn display_issues_pretty(issues: &[JiraIssue]) -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("Found {} issues:", issues.len());
|
||||
println!("{:-<80}", "");
|
||||
|
||||
for issue in issues {
|
||||
let mut tw = tabwriter::TabWriter::new(vec![]);
|
||||
writeln!(tw, "{}:\t{}", "Key".blue(), issue.key)?;
|
||||
writeln!(tw, "{}:\t{}", "Summary".blue(), issue.fields.summary)?;
|
||||
writeln!(tw, "{}:\t{}", "Status".blue(), issue.fields.status.name)?;
|
||||
writeln!(tw, "{}:\t{}", "Created".blue(), issue.fields.created)?;
|
||||
writeln!(tw, "{}:\t{:?}", "Due Date".blue(), issue.fields.due_date)?;
|
||||
writeln!(tw, "{}:\t{}", "URL".blue(), issue.href.underline())?;
|
||||
tw.flush().unwrap();
|
||||
|
||||
let written = String::from_utf8(tw.into_inner().unwrap()).unwrap();
|
||||
print!("{}", written);
|
||||
println!("{:-<80}", "");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_issues_json(issues: &[JiraIssue]) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let j = serde_json::to_string_pretty(issues)?;
|
||||
println!("{}", j);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn list(json: bool) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let config = JiraConfig::load().map_err(|e| format!("Configuration error: {}", e))?;
|
||||
|
@ -7,9 +117,7 @@ pub async fn list(json: bool) -> Result<(), Box<dyn std::error::Error>> {
|
|||
println!("Fetching issues assigned...");
|
||||
}
|
||||
|
||||
let jql = "assignee = currentUser() AND resolution = Unresolved order by updated DESC";
|
||||
|
||||
match crate::jql::run(&config, jql).await {
|
||||
match list_jira_issues(&config).await {
|
||||
Ok(response) => {
|
||||
if json {
|
||||
if response.issues.is_empty() {
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
use crate::jira_config::JiraConfig;
|
||||
use crate::types::issue::{display_issues_json, display_issues_pretty};
|
||||
|
||||
pub async fn exec(json: bool, jql: String) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let config = JiraConfig::load().map_err(|e| format!("Configuration error: {}", e))?;
|
||||
if !json {
|
||||
println!("Searching for issues...");
|
||||
}
|
||||
|
||||
match crate::jql::run(&config, &jql).await {
|
||||
Ok(response) => {
|
||||
if json {
|
||||
if response.issues.is_empty() {
|
||||
println!("[]");
|
||||
} else {
|
||||
display_issues_json(&response.issues)?;
|
||||
}
|
||||
} else if response.issues.is_empty() {
|
||||
println!("No results found for query.");
|
||||
} else {
|
||||
display_issues_pretty(&response.issues)?;
|
||||
println!("Total issues: {}", response.total);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Error fetching issues: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
37
src/jql.rs
37
src/jql.rs
|
@ -1,37 +0,0 @@
|
|||
use crate::jira_config::JiraConfig;
|
||||
use crate::types::issue::JiraIssue;
|
||||
use reqwest::header::{HeaderMap, HeaderValue, CONTENT_TYPE};
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct JiraSearchResponse {
|
||||
pub issues: Vec<JiraIssue>,
|
||||
pub total: u32,
|
||||
}
|
||||
|
||||
pub async fn run(
|
||||
config: &JiraConfig,
|
||||
jql: &str,
|
||||
) -> Result<JiraSearchResponse, Box<dyn std::error::Error>> {
|
||||
let client = reqwest::Client::new();
|
||||
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
|
||||
|
||||
let query = [("jql", jql)];
|
||||
|
||||
let response = client
|
||||
.get(format!("{}/rest/api/2/search", config.url))
|
||||
.basic_auth(&config.email, Some(&config.api_token))
|
||||
.headers(headers)
|
||||
.query(&query)
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
let error_text = response.text().await?;
|
||||
return Err(format!("Failed to fetch issues: {}", error_text).into());
|
||||
}
|
||||
|
||||
Ok(response.json::<JiraSearchResponse>().await?)
|
||||
}
|
|
@ -1,8 +1,6 @@
|
|||
mod cli;
|
||||
mod cmd;
|
||||
mod jira_config;
|
||||
mod jql;
|
||||
mod types;
|
||||
|
||||
use clap::Parser;
|
||||
use cli::{Cli, Commands};
|
||||
|
@ -19,7 +17,6 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
markdown_file,
|
||||
} => cmd::create::create(project, open, markdown_file).await?,
|
||||
Commands::List { json } => cmd::list::list(json).await?,
|
||||
Commands::Search { json, jql } => cmd::search::exec(json, jql).await?,
|
||||
Commands::Init { url, email, token } => {
|
||||
JiraConfig::init(url, email, token).await?;
|
||||
println!("Configuration initialized successfully!");
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
pub mod issue;
|
|
@ -1,129 +0,0 @@
|
|||
use crossterm::style::Stylize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::io::Write;
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct JiraIssue {
|
||||
pub key: String,
|
||||
#[serde(rename = "self")]
|
||||
pub href: String,
|
||||
pub fields: JiraIssueResponseFields,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct JiraIssueResponseFields {
|
||||
pub summary: String,
|
||||
pub status: Status,
|
||||
pub created: chrono::DateTime<chrono::Utc>,
|
||||
pub priority: Priority,
|
||||
pub assignee: Person,
|
||||
pub reporter: Person,
|
||||
pub creator: Person,
|
||||
#[serde(rename = "duedate")]
|
||||
pub due_date: Option<chrono::NaiveDate>,
|
||||
pub comment: Option<Comments>,
|
||||
pub votes: Votes,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct Status {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct Priority {
|
||||
pub name: String,
|
||||
pub id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct Person {
|
||||
#[serde(rename = "self")]
|
||||
pub href: String,
|
||||
#[serde(rename = "displayName")]
|
||||
pub display_name: String,
|
||||
#[serde(rename = "accountId")]
|
||||
pub account_id: String,
|
||||
#[serde(rename = "emailAddress")]
|
||||
pub email_address: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct Comments {
|
||||
#[serde(rename = "self")]
|
||||
pub href: String,
|
||||
pub total: u32,
|
||||
#[serde(rename = "maxResults")]
|
||||
pub max_results: u32,
|
||||
#[serde(rename = "startAt")]
|
||||
pub start_at: u32,
|
||||
pub comments: Vec<Comment>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct Comment {
|
||||
#[serde(rename = "self")]
|
||||
pub href: String,
|
||||
pub id: String,
|
||||
pub author: Person,
|
||||
pub body: String,
|
||||
#[serde(rename = "updateAuthor")]
|
||||
pub update_author: Person,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct Votes {
|
||||
#[serde(rename = "self")]
|
||||
pub href: String,
|
||||
#[serde(rename = "votes")]
|
||||
pub count: i32,
|
||||
#[serde(rename = "hasVoted")]
|
||||
pub has_voted: bool,
|
||||
}
|
||||
|
||||
pub fn display_issues_pretty(issues: &[JiraIssue]) -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("Found {} issues:", issues.len());
|
||||
println!("{:-<80}", "");
|
||||
|
||||
for issue in issues {
|
||||
let mut tw = tabwriter::TabWriter::new(vec![]);
|
||||
writeln!(tw, "{}:\t{}", "Key".blue(), issue.key)?;
|
||||
writeln!(tw, "{}:\t{}", "Summary".blue(), issue.fields.summary)?;
|
||||
writeln!(tw, "{}:\t{}", "Status".blue(), issue.fields.status.name)?;
|
||||
writeln!(
|
||||
tw,
|
||||
"{}:\t{}",
|
||||
"Created".blue(),
|
||||
issue.fields.created.with_timezone(&chrono::Local)
|
||||
)?;
|
||||
writeln!(
|
||||
tw,
|
||||
"{}:\t{}",
|
||||
"Due Date".blue(),
|
||||
match issue.fields.due_date {
|
||||
None => "None".to_string(),
|
||||
Some(x) => x.to_string(),
|
||||
}
|
||||
)?;
|
||||
writeln!(
|
||||
tw,
|
||||
"\u{1b}]8;;{}\u{7}{}\u{1b}]8;;\u{7}",
|
||||
issue.href,
|
||||
"Open Issue".green()
|
||||
)?;
|
||||
|
||||
tw.flush().unwrap();
|
||||
|
||||
let written = String::from_utf8(tw.into_inner().unwrap()).unwrap();
|
||||
print!("{}", written);
|
||||
println!("{:-<80}", "");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn display_issues_json(issues: &[JiraIssue]) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let j = serde_json::to_string_pretty(issues)?;
|
||||
println!("{}", j);
|
||||
Ok(())
|
||||
}
|
Loading…
Reference in a new issue