use clap::Args;
use crate::client::LinearClient;
#[derive(Args, Debug)]
pub struct SearchArgs {
pub query: String,
#[arg(long, default_value = "10")]
pub limit: i32,
}
pub async fn execute(args: &SearchArgs, json: bool, debug: bool) -> anyhow::Result<()> {
let client = LinearClient::new(None, debug)?;
let issue_result = client
.query_raw(
r#"query($term: String!, $first: Int) {
searchIssues(term: $term, first: $first) {
nodes { id identifier title state { name } assignee { displayName } priority team { key } }
}
}"#,
Some(serde_json::json!({"term": &args.query, "first": args.limit})),
)
.await?;
let project_result = client
.query_raw(
r#"query($term: String!, $first: Int) {
searchProjects(term: $term, first: $first) {
nodes { id name state lead { displayName } }
}
}"#,
Some(serde_json::json!({"term": &args.query, "first": args.limit})),
)
.await?;
let doc_result = client
.query_raw(
r#"query($term: String!, $first: Int) {
searchDocuments(term: $term, first: $first) {
nodes { id title creator { displayName } updatedAt }
}
}"#,
Some(serde_json::json!({"term": &args.query, "first": args.limit})),
)
.await?;
if json {
let combined = serde_json::json!({
"issues": issue_result.pointer("/data/searchIssues/nodes"),
"projects": project_result.pointer("/data/searchProjects/nodes"),
"documents": doc_result.pointer("/data/searchDocuments/nodes"),
});
crate::output::print_json(&combined);
} else {
if let Some(issues) = issue_result
.pointer("/data/searchIssues/nodes")
.and_then(|v| v.as_array())
{
if !issues.is_empty() {
println!("\n {}", crate::output::color::bold("Issues"));
for issue in issues {
crate::output::detail::print_issue_summary(issue);
}
}
}
if let Some(projects) = project_result
.pointer("/data/searchProjects/nodes")
.and_then(|v| v.as_array())
{
if !projects.is_empty() {
println!("\n {}", crate::output::color::bold("Projects"));
let rows: Vec<Vec<String>> = projects
.iter()
.map(|p| {
vec![
p["name"].as_str().unwrap_or("-").to_string(),
p["state"].as_str().unwrap_or("-").to_string(),
p.pointer("/lead/displayName")
.and_then(|v| v.as_str())
.unwrap_or("-")
.to_string(),
]
})
.collect();
crate::output::table::print_table(&["Name", "Status", "Lead"], &rows);
}
}
if let Some(docs) = doc_result
.pointer("/data/searchDocuments/nodes")
.and_then(|v| v.as_array())
{
if !docs.is_empty() {
println!("\n {}", crate::output::color::bold("Documents"));
let rows: Vec<Vec<String>> = docs
.iter()
.map(|d| {
vec![
d["title"].as_str().unwrap_or("-").to_string(),
d.pointer("/creator/displayName")
.and_then(|v| v.as_str())
.unwrap_or("-")
.to_string(),
d["updatedAt"]
.as_str()
.unwrap_or("-")
.chars()
.take(10)
.collect(),
]
})
.collect();
crate::output::table::print_table(&["Title", "Creator", "Updated"], &rows);
}
}
let all_empty = issue_result
.pointer("/data/searchIssues/nodes")
.and_then(|v| v.as_array())
.map(|a| a.is_empty())
.unwrap_or(true)
&& project_result
.pointer("/data/searchProjects/nodes")
.and_then(|v| v.as_array())
.map(|a| a.is_empty())
.unwrap_or(true)
&& doc_result
.pointer("/data/searchDocuments/nodes")
.and_then(|v| v.as_array())
.map(|a| a.is_empty())
.unwrap_or(true);
if all_empty {
println!(" No results found for '{}'.", args.query);
}
}
Ok(())
}