use clap::{Parser, Subcommand};
use std::str::FromStr;
use tarzi::constants::{FORMAT_HTML, FORMAT_JSON, FORMAT_MARKDOWN};
use tarzi::{
Result,
config::{CliConfigParams, Config},
converter::{Converter, Format, convert_search_results},
fetcher::WebFetcher,
search::SearchEngine,
};
use tracing::{debug, info};
#[derive(Parser)]
#[command(name = "tarzi")]
#[command(about = "Rust-native lite search for AI applications")]
#[command(version)]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
Convert {
#[arg(short, long)]
input: String,
#[arg(short, long, default_value = FORMAT_MARKDOWN)]
format: String,
#[arg(short, long)]
output: Option<String>,
#[arg(short, long)]
verbose: bool,
},
Fetch {
#[arg(short, long)]
url: String,
#[arg(short, long, default_value = FORMAT_HTML)]
format: String,
#[arg(short, long)]
output: Option<String>,
#[arg(short, long)]
verbose: bool,
},
Search {
#[arg(short, long)]
query: String,
#[arg(short, long)]
limit: Option<usize>,
#[arg(long)]
engine: Option<String>,
#[arg(long, default_missing_value = "true", num_args = 0..=1, action = clap::ArgAction::Set)]
browser: Option<bool>,
#[arg(short, long, default_value = FORMAT_JSON)]
format: String,
#[arg(short, long)]
output: Option<String>,
#[arg(short, long)]
verbose: bool,
},
SearchAndFetch {
#[arg(short, long)]
query: String,
#[arg(short, long, default_value = "5")]
limit: usize,
#[arg(long)]
engine: Option<String>,
#[arg(long, default_missing_value = "true", num_args = 0..=1, action = clap::ArgAction::Set)]
browser: Option<bool>,
#[arg(short, long, default_value = FORMAT_MARKDOWN)]
format: String,
#[arg(short, long)]
output: Option<String>,
#[arg(short, long)]
verbose: bool,
},
}
#[tokio::main]
async fn main() -> Result<()> {
tracing_subscriber::fmt()
.with_max_level(tracing::Level::INFO)
.init();
let cli = Cli::parse();
let mut config = Config::load()?;
let mut cli_params = CliConfigParams::new();
match cli.command {
Commands::Convert {
input,
format,
output,
verbose: _,
} => {
debug!("Input length: {} characters", input.len());
let converter = Converter::new();
let format = Format::from_str(&format)?;
let result = converter.convert(&input, format).await?;
if let Some(output_path) = output {
std::fs::write(&output_path, result)?;
info!("Output written to file: {}", output_path);
} else {
println!("{result}");
}
}
Commands::Fetch {
url,
format,
output,
verbose: _,
} => {
debug!("Using format: {}", format);
cli_params.fetcher_format = Some(format.clone());
config.apply_cli_params(&cli_params);
let mut fetcher = WebFetcher::from_config(&config);
let format = Format::from_str(&format)?;
let result = fetcher.fetch(&url, format).await?;
if let Some(output_path) = output {
std::fs::write(&output_path, result)?;
info!("Output written to file: {}", output_path);
} else {
println!("{result}");
}
}
Commands::Search {
query,
limit,
engine,
browser,
format,
output,
verbose: _,
} => {
let effective_limit = limit.unwrap_or(config.search.limit);
cli_params.search_limit = Some(effective_limit);
if let Some(engine) = engine {
cli_params.search_engine = Some(engine);
}
if let Some(browser) = browser {
cli_params.search_browser = Some(browser);
}
config.apply_cli_params(&cli_params);
let mut search_engine = SearchEngine::from_config(&config);
let results = search_engine.search(&query, effective_limit).await?;
debug!("Processing results for output format: {}", format);
let format = Format::from_str(&format)?;
let result = convert_search_results(&results, format)?;
if let Some(output_path) = output {
std::fs::write(&output_path, result)?;
info!("Output written to file: {}", output_path);
} else {
println!("{result}");
}
search_engine.shutdown().await;
}
Commands::SearchAndFetch {
query,
limit,
engine,
browser,
format,
output,
verbose: _,
} => {
cli_params.search_limit = Some(limit);
cli_params.fetcher_format = Some(format.clone());
if let Some(engine) = engine {
cli_params.search_engine = Some(engine);
}
if let Some(browser) = browser {
cli_params.search_browser = Some(browser);
}
config.apply_cli_params(&cli_params);
let mut search_engine = SearchEngine::from_config(&config);
let format = Format::from_str(&format)?;
let results_with_content = search_engine
.search_with_content(&query, limit, format)
.await?;
let result = serde_json::to_string_pretty(&results_with_content)?;
if let Some(output_path) = output {
std::fs::write(&output_path, result)?;
info!("Output written to file: {}", output_path);
} else {
println!("{result}");
}
search_engine.shutdown().await;
}
}
Ok(())
}