diff --git a/Cargo.lock b/Cargo.lock index 8207c42..8c0c2ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -240,7 +240,7 @@ dependencies = [ "rust-ini", "serde", "serde_json", - "toml 0.8.19", + "toml", "winnow", "yaml-rust2", ] @@ -493,17 +493,6 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" -[[package]] -name = "gray_matter" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31ee6a6070bad7c953b0c8be9367e9372181fed69f3e026c4eb5160d8b3c0222" -dependencies = [ - "serde", - "serde_json", - "toml 0.5.11", -] - [[package]] name = "h2" version = "0.4.7" @@ -820,25 +809,6 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" -[[package]] -name = "is-docker" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3" -dependencies = [ - "once_cell", -] - -[[package]] -name = "is-wsl" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5" -dependencies = [ - "is-docker", - "once_cell", -] - [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -858,14 +828,12 @@ dependencies = [ "clap", "config", "directories", - "gray_matter", - "open", "reqwest", "serde", "serde_json", "tempfile", "tokio", - "toml 0.8.19", + "toml", ] [[package]] @@ -997,17 +965,6 @@ version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" -[[package]] -name = "open" -version = "5.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2483562e62ea94312f3576a7aca397306df7990b8d89033e18766744377ef95" -dependencies = [ - "is-wsl", - "libc", - "pathdiff", -] - [[package]] name = "openssl" version = "0.10.68" @@ -1689,15 +1646,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "toml" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" -dependencies = [ - "serde", -] - [[package]] name = "toml" version = "0.8.19" diff --git a/Cargo.toml b/Cargo.toml index e4712c7..b5268c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,4 @@ serde_json = "1.0" toml = "0.8" config = "0.15" directories = "6.0" -tempfile = "3.8" -gray_matter = { version = "0.2", default-features = false, features = ["toml"] } -open = "5.2" \ No newline at end of file +tempfile = "3.8" \ No newline at end of file diff --git a/readme.md b/readme.md index d09efd0..962adc9 100644 --- a/readme.md +++ b/readme.md @@ -37,7 +37,7 @@ Options: ### Creating a ticket ``` -Usage: jirac create [OPTIONS] [MARKDOWN_FILE] +Usage: jirac create --project [MARKDOWN_FILE] Arguments: [MARKDOWN_FILE] @@ -50,19 +50,13 @@ Options: *Using your favourite editor* ```sh -jirac create +jirac create --project KEY ``` *From a markdown file* ```sh -jirac create ticket.md -``` - -*Specify a project* - -```sh -jirac create --project KEY +jirac create --project KEY ticket.md ``` ## Listing tickets diff --git a/src/cli.rs b/src/cli.rs index 30e0376..aef9bfa 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -12,10 +12,7 @@ pub struct Cli { pub enum Commands { Create { #[arg(long)] - project: Option, - - #[arg(long)] - open: bool, + project: String, #[arg(value_name = "MARKDOWN_FILE")] markdown_file: Option, diff --git a/src/cmd/create.rs b/src/cmd/create.rs index 8996c30..10a50ac 100644 --- a/src/cmd/create.rs +++ b/src/cmd/create.rs @@ -1,73 +1,30 @@ use crate::jira_config::JiraConfig; -use gray_matter::engine::TOML; -use gray_matter::Matter; use reqwest::header::{HeaderMap, HeaderValue, CONTENT_TYPE}; use serde::{Deserialize, Serialize}; -use std::collections::HashMap; use std::fs; use std::io::Write; use std::path::PathBuf; use tempfile::NamedTempFile; -#[derive(Debug, Deserialize, Serialize)] -struct TicketMetadata { - #[serde(skip_serializing_if = "Option::is_none")] - status: Option, - #[serde(skip_serializing_if = "Option::is_none")] - project: Option, - #[serde(skip_serializing_if = "Option::is_none")] - assignee: Option, - #[serde(skip_serializing_if = "Option::is_none")] - parent: Option, - #[serde(flatten)] - extra: HashMap, -} - #[derive(Debug, Serialize)] struct JiraIssueRequest { fields: JiraIssueFields, - update: Option, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize)] struct JiraIssueFields { project: Project, summary: String, description: String, issuetype: IssueType, - #[serde(skip_serializing_if = "Option::is_none")] - assignee: Option, - #[serde(skip_serializing_if = "Option::is_none")] - status: Option, } #[derive(Debug, Serialize)] -struct JiraIssueUpdate { - #[serde(skip_serializing_if = "Option::is_none")] - parent: Option>, -} - -#[derive(Debug, Serialize)] -struct ParentUpdate { - add: Parent, -} - -#[derive(Debug, Serialize)] -struct Parent { - key: String, -} - -#[derive(Debug, Serialize, Deserialize)] -struct Assignee { - name: String, -} - -#[derive(Debug, Serialize, Deserialize)] struct Project { key: String, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize)] struct IssueType { name: String, } @@ -96,72 +53,18 @@ struct JiraIssueResponseFields { created: String, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Deserialize)] struct Status { name: String, } -fn get_ticket_template( - ticket_metadata: &TicketMetadata, -) -> Result> { - let matter = toml::to_string(ticket_metadata)?; - let has_project = ticket_metadata.project.is_some(); - - let template = format!( - r#"--- -# The ticket can contain the following properties: -# - status -# - project (required) -# - assignee -# - parent -# - labels -# - priority - -{} -{} ---- -# Title - -[description] -"#, - if has_project { "" } else { r#"project = """# }, - matter, - ); - - Ok(template) -} - fn get_editor() -> String { std::env::var("EDITOR") .or_else(|_| std::env::var("VISUAL")) .unwrap_or_else(|_| "vi".to_string()) } -fn parse_markdown( - content: &str, -) -> Result<(String, String, TicketMetadata), Box> { - let matter = Matter::::new(); - let result = matter.parse_with_struct::(content); - - let (metadata, content) = match result { - Some(x) => (x.data, x.content), - None => { - eprintln!("unexpected markdown content found"); - std::process::exit(1); - } - }; - - // let metadata = result.data - // .and_then(|d| toml::from_str(d).ok()) - // .unwrap_or_else(|| TicketMetadata { - // status: None, - // project: None, - // assignee: None, - // parent: None, - // extra: HashMap::new(), - // }); - // - // let content = result.content; +fn parse_markdown(content: &str) -> (String, String) { let mut lines = content.lines(); // Get first non-empty line as title @@ -176,21 +79,13 @@ fn parse_markdown( // Rest is description let description = lines.collect::>().join("\n"); - Ok((title, description, metadata)) + (title, description) } -fn create_temp_markdown(project_key: Option) -> Result> { +fn create_temp_markdown() -> Result> { let mut temp_file = NamedTempFile::new()?; - let metadata = TicketMetadata { - status: None, - project: project_key, - assignee: None, - parent: None, - extra: Default::default(), - }; - - let template = get_ticket_template(&metadata)?; + let template = r#"# "#; temp_file.write_all(template.as_bytes())?; temp_file.flush()?; @@ -210,56 +105,35 @@ fn create_temp_markdown(project_key: Option) -> Result Result> { let _client = reqwest::Client::new(); let mut headers = HeaderMap::new(); headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json")); - let mut fields = JiraIssueFields { - project: Project { - key: project_key.to_string(), + let issue = JiraIssueRequest { + fields: JiraIssueFields { + project: Project { + key: project_key.to_string(), + }, + summary: title.to_string(), + description: description.to_string(), + issuetype: IssueType { + name: "Task".to_string(), + }, }, - summary: title.to_string(), - description: description.to_string(), - issuetype: IssueType { - name: "Task".to_string(), - }, - assignee: None, - status: None, }; - // Add assignee if specified - if let Some(assignee) = &metadata.assignee { - fields.assignee = Some(Assignee { - name: assignee.clone(), - }); - } - - let mut update = None; - - // Add parent if specified - if let Some(parent_key) = &metadata.parent { - update = Some(JiraIssueUpdate { - parent: Some(vec![ParentUpdate { - add: Parent { - key: parent_key.clone(), - }, - }]), - }); - } - - let issue = JiraIssueRequest { fields, update }; - println!("{:#?}", issue); + Ok(JiraResponse { - key: format!("{}-1234", project_key), + key: "123".to_string(), }) // let response = client @@ -275,72 +149,11 @@ async fn create_jira_issue( // return Err(format!("Failed to create issue: {}", error_text).into()); // } // - // let issue_response = response.json::().await?; - // - // // Update status if specified (requires a separate API call) - // if let Some(status) = &metadata.status { - // update_issue_status(config, &issue_response.key, status).await?; - // } - // - // Ok(issue_response) -} - -async fn update_issue_status( - config: &JiraConfig, - issue_key: &str, - status: &str, -) -> Result<(), Box> { - let client = reqwest::Client::new(); - - let transitions = client - .get(format!( - "{}/rest/api/2/issue/{}/transitions", - config.url, issue_key - )) - .basic_auth(&config.email, Some(&config.api_token)) - .send() - .await? - .json::() - .await?; - - // Find the transition ID for the desired status - let transition_id = transitions["transitions"] - .as_array() - .and_then(|t| { - t.iter().find(|t| { - t["to"]["name"] - .as_str() - .map_or(false, |s| s.eq_ignore_ascii_case(status)) - }) - }) - .and_then(|t| t["id"].as_str()) - .ok_or_else(|| format!("No transition found for status: {}", status))?; - - // Perform the transition - let transition_payload = serde_json::json!({ - "transition": { "id": transition_id } - }); - - let response = client - .post(format!( - "{}/rest/api/2/issue/{}/transitions", - config.url, issue_key - )) - .basic_auth(&config.email, Some(&config.api_token)) - .json(&transition_payload) - .send() - .await?; - - if !response.status().is_success() { - return Err(format!("Failed to update status: {}", response.text().await?).into()); - } - - Ok(()) + // Ok(response.json::().await?) } pub async fn create( - project: Option, - open: bool, + project: String, markdown_file: Option, ) -> Result<(), Box> { let config = JiraConfig::load().map_err(|e| format!("Configuration error: {}", e))?; @@ -350,8 +163,8 @@ pub async fn create( fs::read_to_string(&path).map_err(|e| format!("Failed to read file: {}", e))? } None => { - eprintln!("No markdown file specified. Opening editor..."); - create_temp_markdown(project)? + println!("No markdown file specified. Opening editor..."); + create_temp_markdown()? } }; @@ -359,42 +172,21 @@ pub async fn create( return Err("Empty content. Aborting ticket creation.".into()); } - let (title, description, metadata) = parse_markdown(&content)?; - - let selected_project = match metadata.project.clone() { - Some(x) => x.to_uppercase(), - None => { - eprintln!("No project set..."); - std::process::exit(1); - } - }; - - if selected_project.is_empty() { - eprintln!("No project set..."); - std::process::exit(1); - } + let (title, description) = parse_markdown(&content); // Confirm creation println!("\nAbout to create ticket:"); println!("Title: {}", title); - println!("{}", description); - // println!("{}...", &description.chars().take(100).collect::()); + println!("{}...", &description.chars().take(100).collect::()); println!("\nPress Enter to continue or Ctrl+C to abort"); let mut input = String::new(); std::io::stdin().read_line(&mut input)?; - let response = - create_jira_issue(&config, &selected_project, &title, &description, &metadata).await?; - - let url = format!("{}/browse/{}", config.url, response.key); + let response = create_jira_issue(&config, &project, &title, &description).await?; println!("Successfully created ticket: {}", response.key); - println!("URL: {}", url); - - if open { - open::that(url)?; - } + println!("URL: {}/browse/{}", config.url, response.key); Ok(()) } diff --git a/src/main.rs b/src/main.rs index cc526c8..0e8d145 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,9 +13,8 @@ async fn main() -> Result<(), Box> { match cli.command { Commands::Create { project, - open, markdown_file, - } => cmd::create::create(project, open, markdown_file).await?, + } => cmd::create::create(project, markdown_file).await?, Commands::List { json } => cmd::list::list(json).await?, Commands::Init { url, email, token } => { JiraConfig::init(url, email, token).await?;