use anyhow::Result;
#[derive(clap::ValueEnum, Clone, Debug, PartialEq, Eq, Default)]
pub enum OutputFormat {
#[default]
Text,
Json,
}
impl OutputFormat {
pub fn is_json(&self) -> bool {
matches!(self, OutputFormat::Json)
}
}
fn looks_like_logic_query(query: &str) -> bool {
query.contains('(') && query.contains(')')
}
pub async fn handle_query(
query: &str,
hybrid: bool,
pipeline: bool,
top_k: usize,
logic_filter: Option<&str>,
format: &OutputFormat,
) -> Result<()> {
use crate::output;
let json_output = format.is_json();
if pipeline {
return crate::commands::logic::logic_filter(query, json_output, ".ipfrs").await;
}
if hybrid {
if !json_output {
output::print_header(&format!("Hybrid Query: \"{}\"", query));
println!();
println!("--- Semantic Results ---");
}
let semantic_cids =
crate::commands::semantic::semantic_query_with_cids(query, top_k, 0.0, json_output)
.await?;
if let Some(filter_predicate) = logic_filter {
if !json_output {
println!();
println!("--- Logic Filter ---");
println!("Predicate: {}", filter_predicate);
}
crate::commands::logic::logic_filter_cids(
&semantic_cids,
filter_predicate,
json_output,
)
.await?;
} else {
if !json_output {
println!();
println!("--- Logic Results ---");
}
if looks_like_logic_query(query) {
crate::commands::logic::logic_query_streaming(query, 10, json_output, 30).await?;
} else if !json_output {
output::info(
"Query does not look like a logic predicate (no parentheses). Skipping logic search.",
);
}
}
} else if looks_like_logic_query(query) {
crate::commands::logic::logic_query_streaming(query, 10, json_output, 30).await?;
} else {
crate::commands::semantic::semantic_query(query, top_k, 0.0, json_output).await?;
}
Ok(())
}
#[cfg(test)]
mod query_tests {
use super::*;
#[test]
fn test_output_format_is_json_text() {
let fmt = OutputFormat::Text;
assert!(!fmt.is_json());
}
#[test]
fn test_output_format_is_json_json() {
let fmt = OutputFormat::Json;
assert!(fmt.is_json());
}
#[test]
fn test_output_format_default_is_text() {
let fmt = OutputFormat::default();
assert_eq!(fmt, OutputFormat::Text);
}
#[test]
fn test_output_format_text() {
let json_output = OutputFormat::Text.is_json();
assert!(
!json_output,
"text format must not produce json_output=true"
);
let cid = "bafkreiabc123";
let score = 0.9512_f32;
let text_line = format!(" CID: {} (score: {:.2})", cid, score);
assert!(
text_line.contains("CID:"),
"text output should contain 'CID:' label"
);
assert!(
text_line.contains("score:"),
"text output should contain 'score:' label"
);
assert!(
!text_line.contains('{'),
"text output should not contain JSON braces"
);
}
#[test]
fn test_output_format_json() {
let json_output = OutputFormat::Json.is_json();
assert!(json_output, "json format must produce json_output=true");
let cid = "bafkreiabc123";
let score = 0.9512_f32;
let json_line = format!(" {{\"cid\": \"{}\", \"score\": {:.4}}}", cid, score);
let parsed: serde_json::Value =
serde_json::from_str(json_line.trim()).expect("JSON output must be valid JSON");
assert_eq!(
parsed["cid"].as_str().expect("cid field"),
cid,
"cid field must match"
);
let got_score = parsed["score"].as_f64().expect("score field") as f32;
assert!(
(got_score - score).abs() < 1e-3,
"score field must be close to {}",
score
);
}
#[test]
fn test_hybrid_query_struct() {
let hybrid = true;
let query = "test";
let logic_filter: &str = "foo(X)";
let top_k: usize = 10;
assert!(hybrid, "hybrid flag must be true when set");
assert_eq!(query, "test");
assert_eq!(logic_filter, "foo(X)");
assert_eq!(top_k, 10);
assert!(
looks_like_logic_query("foo(X)"),
"foo(X) should be identified as a logic query"
);
assert!(
looks_like_logic_query("ancestor(X, bob)"),
"ancestor(X, bob) should be identified as a logic query"
);
assert!(
!looks_like_logic_query("machine learning"),
"plain text should not be identified as a logic query"
);
assert_eq!(logic_filter, "foo(X)");
}
#[test]
fn test_looks_like_logic_query_with_parens() {
assert!(looks_like_logic_query("parent(alice, bob)"));
assert!(looks_like_logic_query("fact()"));
}
#[test]
fn test_looks_like_logic_query_without_parens() {
assert!(!looks_like_logic_query("natural language query"));
assert!(!looks_like_logic_query("tensor operations in IPFS"));
}
#[test]
fn test_looks_like_logic_query_partial_parens() {
assert!(!looks_like_logic_query("half("));
assert!(!looks_like_logic_query(")close"));
}
}