Compare commits
No commits in common. "fa387c5546120a68437f0ef257baedadd88e9b22" and "07864325d795cb8bc20b5c3fca128d223fe3592a" have entirely different histories.
fa387c5546
...
07864325d7
12 changed files with 14 additions and 229 deletions
|
@ -1,6 +1,6 @@
|
||||||
use crate::cli::FormatMode;
|
use crate::cli::FormatMode;
|
||||||
use crate::jira_config::JiraConfig;
|
use crate::jira_config::JiraConfig;
|
||||||
use crate::renderer::hyperlink;
|
use crate::term::hyperlink;
|
||||||
use crossterm::style::{Color, Stylize};
|
use crossterm::style::{Color, Stylize};
|
||||||
use libjirac::client::commands::SearchCommand;
|
use libjirac::client::commands::SearchCommand;
|
||||||
use libjirac::client::JiraClient;
|
use libjirac::client::JiraClient;
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
use crate::cli::FormatMode;
|
use crate::cli::FormatMode;
|
||||||
use crate::jira_config::JiraConfig;
|
use crate::jira_config::JiraConfig;
|
||||||
use crate::renderer::{hyperlink, render_doc};
|
use crate::term::hyperlink;
|
||||||
use crossterm::style::{Color, Stylize};
|
use crossterm::style::{Color, Stylize};
|
||||||
use libjirac::client::commands::IssueGetCommand;
|
use libjirac::client::commands::IssueGetCommand;
|
||||||
use libjirac::client::JiraClient;
|
use libjirac::client::JiraClient;
|
||||||
use libjirac::entities::issue::{Description, JiraIssue};
|
use libjirac::entities::issue::JiraIssue;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
fn pretty_print(config: &JiraConfig, issue: &JiraIssue) -> Result<(), Box<dyn std::error::Error>> {
|
fn pretty_print(issue: &JiraIssue) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
println!("\n== Title {:=<71}", "");
|
println!("\n== Title {:=<71}", "");
|
||||||
println!(
|
println!(
|
||||||
"{}: {}",
|
"{}: {}",
|
||||||
|
@ -65,9 +65,7 @@ fn pretty_print(config: &JiraConfig, issue: &JiraIssue) -> Result<(), Box<dyn st
|
||||||
print!("{}", written);
|
print!("{}", written);
|
||||||
println!("\n== Description {:=<65}", "");
|
println!("\n== Description {:=<65}", "");
|
||||||
match issue.fields.description.clone() {
|
match issue.fields.description.clone() {
|
||||||
Some(x) => match x {
|
Some(x) => println!("{}", x),
|
||||||
Description::Doc(doc) => println!("{}", render_doc(doc)?),
|
|
||||||
},
|
|
||||||
None => println!("(Issue does not have a description)"),
|
None => println!("(Issue does not have a description)"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,13 +76,13 @@ fn pretty_print(config: &JiraConfig, issue: &JiraIssue) -> Result<(), Box<dyn st
|
||||||
comment.author.display_name.red(),
|
comment.author.display_name.red(),
|
||||||
comment.created.with_timezone(&chrono::Local)
|
comment.created.with_timezone(&chrono::Local)
|
||||||
);
|
);
|
||||||
println!("{}", render_doc(comment.body)?);
|
println!("{}", comment.body);
|
||||||
}
|
}
|
||||||
println!("\n== Actions {:=<69}", "");
|
println!("\n== Actions {:=<69}", "");
|
||||||
println!(
|
println!(
|
||||||
"{}",
|
"{}",
|
||||||
hyperlink(
|
hyperlink(
|
||||||
&format!("{}/browse/{}", config.url, issue.key),
|
&issue.href,
|
||||||
&"Open Issue".green().underline(Color::Green).to_string()
|
&"Open Issue".green().underline(Color::Green).to_string()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@ -110,7 +108,7 @@ pub async fn exec(output: FormatMode, issue_key: &str) -> Result<(), Box<dyn std
|
||||||
let fetched_issue = client.exec(get_cmd).await?;
|
let fetched_issue = client.exec(get_cmd).await?;
|
||||||
|
|
||||||
match output {
|
match output {
|
||||||
FormatMode::Pretty => pretty_print(&config, &fetched_issue)?,
|
FormatMode::Pretty => pretty_print(&fetched_issue)?,
|
||||||
FormatMode::Json => json_print(&fetched_issue)?,
|
FormatMode::Json => json_print(&fetched_issue)?,
|
||||||
FormatMode::Compact => todo!(),
|
FormatMode::Compact => todo!(),
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
mod cli;
|
mod cli;
|
||||||
mod cmd;
|
mod cmd;
|
||||||
mod jira_config;
|
mod jira_config;
|
||||||
mod renderer;
|
mod term;
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use cli::{Cli, Commands};
|
use cli::{Cli, Commands};
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
mod doc;
|
|
||||||
mod hyperlink;
|
|
||||||
|
|
||||||
pub use doc::render_doc;
|
|
||||||
pub use hyperlink::hyperlink;
|
|
|
@ -1,109 +0,0 @@
|
||||||
use crossterm::style::Stylize;
|
|
||||||
use libjirac::entities::doc::{Doc, DocMedia, DocNode, DocTextMarker, DocTextNode};
|
|
||||||
use std::fmt::Write;
|
|
||||||
|
|
||||||
fn render_doc_node(f: &mut String, node: &DocNode) -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
match node {
|
|
||||||
DocNode::Paragraph(x) => {
|
|
||||||
for node in x {
|
|
||||||
render_text_node(f, node)?;
|
|
||||||
}
|
|
||||||
writeln!(f)?;
|
|
||||||
}
|
|
||||||
DocNode::BulletList(x) => {
|
|
||||||
for node in x {
|
|
||||||
write!(f, "* ")?;
|
|
||||||
render_doc_node(f, node)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DocNode::ListItem(x) => {
|
|
||||||
for node in x {
|
|
||||||
render_doc_node(f, node)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DocNode::OrderedList(x) => {
|
|
||||||
let mut i = 1;
|
|
||||||
for node in x {
|
|
||||||
write!(f, "{}. ", i)?;
|
|
||||||
i += 1;
|
|
||||||
render_doc_node(f, node)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DocNode::Rule => {
|
|
||||||
writeln!(f, "---")?;
|
|
||||||
}
|
|
||||||
DocNode::Heading(x) => {
|
|
||||||
for node in x {
|
|
||||||
render_text_node(f, node)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DocNode::MediaSingle(x) => {
|
|
||||||
for node in x {
|
|
||||||
render_text_markets(f, node)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_text_node(f: &mut String, node: &DocTextNode) -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
match node {
|
|
||||||
DocTextNode::Text(x) => {
|
|
||||||
write!(f, "{}", x.text)?;
|
|
||||||
}
|
|
||||||
DocTextNode::HardBreak => {
|
|
||||||
writeln!(f)?;
|
|
||||||
}
|
|
||||||
DocTextNode::InlineCard(x) => {
|
|
||||||
writeln!(f, "{}", x.attrs.url)?;
|
|
||||||
}
|
|
||||||
DocTextNode::Mention(x) => {
|
|
||||||
write!(f, "{}", x.attrs.text.clone().green())?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_text_markets(
|
|
||||||
f: &mut String,
|
|
||||||
node: &DocTextMarker,
|
|
||||||
) -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
match node {
|
|
||||||
DocTextMarker::Link(x) => {
|
|
||||||
write!(f, "{}", x.href)?;
|
|
||||||
}
|
|
||||||
DocTextMarker::Strong => {
|
|
||||||
// no-op
|
|
||||||
}
|
|
||||||
DocTextMarker::Code => {
|
|
||||||
// no-op
|
|
||||||
}
|
|
||||||
DocTextMarker::Media(x) => {
|
|
||||||
render_media(f, x)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_media(f: &mut String, node: &DocMedia) -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
match node {
|
|
||||||
DocMedia::File(x) => {
|
|
||||||
writeln!(f, "[file:{}:{}]", x.id, x.alt)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render_doc(doc: Doc) -> Result<String, Box<dyn std::error::Error>> {
|
|
||||||
let mut f = String::new();
|
|
||||||
|
|
||||||
for node in doc.content {
|
|
||||||
render_doc_node(&mut f, &node)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(f)
|
|
||||||
}
|
|
|
@ -18,7 +18,7 @@ impl JiraCommand for IssueCreateCommand {
|
||||||
const REQUEST_TYPE: JiraRequestType = JiraRequestType::Create;
|
const REQUEST_TYPE: JiraRequestType = JiraRequestType::Create;
|
||||||
|
|
||||||
fn endpoint(&self) -> String {
|
fn endpoint(&self) -> String {
|
||||||
"/rest/api/3/issue".to_string()
|
"/rest/api/2/issue".to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn request_body(&self) -> Option<&Self::TPayload> {
|
fn request_body(&self) -> Option<&Self::TPayload> {
|
||||||
|
|
|
@ -20,7 +20,7 @@ impl JiraCommand for IssueGetCommand {
|
||||||
const REQUEST_TYPE: JiraRequestType = JiraRequestType::Read;
|
const REQUEST_TYPE: JiraRequestType = JiraRequestType::Read;
|
||||||
|
|
||||||
fn endpoint(&self) -> String {
|
fn endpoint(&self) -> String {
|
||||||
format!("/rest/api/3/issue/{}", self.key)
|
format!("/rest/api/2/issue/{}", self.key)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn request_body(&self) -> Option<&Self::TPayload> {
|
fn request_body(&self) -> Option<&Self::TPayload> {
|
||||||
|
|
|
@ -20,7 +20,7 @@ impl JiraCommand for SearchCommand {
|
||||||
const REQUEST_TYPE: JiraRequestType = JiraRequestType::Read;
|
const REQUEST_TYPE: JiraRequestType = JiraRequestType::Read;
|
||||||
|
|
||||||
fn endpoint(&self) -> String {
|
fn endpoint(&self) -> String {
|
||||||
"/rest/api/3/search".to_string()
|
"/rest/api/2/search".to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn request_body(&self) -> Option<&Self::TPayload> {
|
fn request_body(&self) -> Option<&Self::TPayload> {
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
pub mod doc;
|
|
||||||
pub mod issue;
|
pub mod issue;
|
||||||
pub mod issue_request;
|
pub mod issue_request;
|
||||||
pub mod search;
|
pub mod search;
|
||||||
|
|
|
@ -1,90 +0,0 @@
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
|
||||||
pub struct Doc {
|
|
||||||
pub version: i32,
|
|
||||||
pub content: Vec<DocNode>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
#[serde(tag = "type", content = "content")]
|
|
||||||
pub enum DocNode {
|
|
||||||
Paragraph(Vec<DocTextNode>),
|
|
||||||
BulletList(Vec<DocNode>),
|
|
||||||
ListItem(Vec<DocNode>),
|
|
||||||
OrderedList(Vec<DocNode>),
|
|
||||||
Rule,
|
|
||||||
Heading(Vec<DocTextNode>),
|
|
||||||
MediaSingle(Vec<DocTextMarker>),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
#[serde(tag = "type")]
|
|
||||||
pub enum DocTextNode {
|
|
||||||
Text(DocTextNodeContent),
|
|
||||||
HardBreak,
|
|
||||||
InlineCard(DocInlineCard),
|
|
||||||
Mention(DocMention),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
|
||||||
pub struct DocTextNodeContent {
|
|
||||||
pub text: String,
|
|
||||||
pub marks: Option<Vec<DocTextMarker>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
#[serde(tag = "type", content = "attrs")]
|
|
||||||
pub enum DocTextMarker {
|
|
||||||
Link(DocTextMarkerLink),
|
|
||||||
Strong,
|
|
||||||
Code,
|
|
||||||
Media(DocMedia),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
|
||||||
pub struct DocTextMarkerLink {
|
|
||||||
pub href: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
#[serde(tag = "type")]
|
|
||||||
pub enum DocMedia {
|
|
||||||
File(DocMediaFile),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
|
||||||
pub struct DocMediaFile {
|
|
||||||
pub id: String,
|
|
||||||
pub alt: String,
|
|
||||||
pub collection: String,
|
|
||||||
pub height: i32,
|
|
||||||
pub width: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
|
||||||
pub struct DocInlineCard {
|
|
||||||
pub attrs: DocInlineCardAttrs,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
|
||||||
pub struct DocInlineCardAttrs {
|
|
||||||
pub url: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
|
||||||
pub struct DocMention {
|
|
||||||
pub attrs: DocMentionAttrs,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct DocMentionAttrs {
|
|
||||||
pub id: String,
|
|
||||||
pub text: String,
|
|
||||||
pub local_id: String,
|
|
||||||
}
|
|
|
@ -1,4 +1,3 @@
|
||||||
use crate::entities::doc::Doc;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
|
@ -12,7 +11,7 @@ pub struct JiraIssue {
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
pub struct JiraIssueResponseFields {
|
pub struct JiraIssueResponseFields {
|
||||||
pub summary: String,
|
pub summary: String,
|
||||||
pub description: Option<Description>,
|
pub description: Option<String>,
|
||||||
pub status: Status,
|
pub status: Status,
|
||||||
pub created: chrono::DateTime<chrono::Utc>,
|
pub created: chrono::DateTime<chrono::Utc>,
|
||||||
pub priority: Priority,
|
pub priority: Priority,
|
||||||
|
@ -25,13 +24,6 @@ pub struct JiraIssueResponseFields {
|
||||||
pub votes: Votes,
|
pub votes: Votes,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
#[serde(tag = "type")]
|
|
||||||
pub enum Description {
|
|
||||||
Doc(Doc),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
pub struct Status {
|
pub struct Status {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
@ -71,7 +63,7 @@ pub struct Comment {
|
||||||
pub href: String,
|
pub href: String,
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub author: Person,
|
pub author: Person,
|
||||||
pub body: Doc,
|
pub body: String,
|
||||||
#[serde(rename = "updateAuthor")]
|
#[serde(rename = "updateAuthor")]
|
||||||
pub update_author: Person,
|
pub update_author: Person,
|
||||||
pub created: chrono::DateTime<chrono::Utc>,
|
pub created: chrono::DateTime<chrono::Utc>,
|
||||||
|
|
Loading…
Reference in a new issue