use crate::errors::AppError;
use crate::namespace;
use crate::output;
use serde::Serialize;
#[derive(clap::Args)]
pub struct NamespaceDetectArgs {
#[arg(long)]
pub namespace: Option<String>,
#[arg(long, env = "SQLITE_GRAPHRAG_DB_PATH")]
pub db: Option<String>,
#[arg(long, default_value_t = false)]
pub json: bool,
}
#[derive(Serialize)]
struct NamespaceDetectResponse {
namespace: String,
source: namespace::NamespaceSource,
cwd: String,
elapsed_ms: u64,
}
pub fn run(args: NamespaceDetectArgs) -> Result<(), AppError> {
let inicio = std::time::Instant::now();
let _ = args.db;
let _ = args.json; let resolution = namespace::detect_namespace(args.namespace.as_deref())?;
output::emit_json(&NamespaceDetectResponse {
namespace: resolution.namespace,
source: resolution.source,
cwd: resolution.cwd,
elapsed_ms: inicio.elapsed().as_millis() as u64,
})?;
Ok(())
}
#[cfg(test)]
mod testes {
use super::*;
use crate::namespace::NamespaceSource;
use clap::Parser;
use serial_test::serial;
#[test]
#[serial]
fn namespace_detect_default_retorna_global_via_detect() {
std::env::remove_var("SQLITE_GRAPHRAG_NAMESPACE");
let resolution = namespace::detect_namespace(None).unwrap();
assert_eq!(resolution.namespace, "global");
assert_eq!(resolution.source, NamespaceSource::Default);
}
#[test]
#[serial]
fn namespace_detect_explicit_flag_sobrepoe_env() {
std::env::set_var("SQLITE_GRAPHRAG_NAMESPACE", "env-namespace");
let resolution = namespace::detect_namespace(Some("flag-namespace")).unwrap();
assert_eq!(resolution.namespace, "flag-namespace");
assert_eq!(resolution.source, NamespaceSource::ExplicitFlag);
std::env::remove_var("SQLITE_GRAPHRAG_NAMESPACE");
}
#[test]
#[serial]
fn namespace_detect_env_var_usada_quando_sem_flag() {
std::env::remove_var("SQLITE_GRAPHRAG_NAMESPACE");
std::env::set_var("SQLITE_GRAPHRAG_NAMESPACE", "namespace-de-env");
let resolution = namespace::detect_namespace(None).unwrap();
assert_eq!(resolution.namespace, "namespace-de-env");
assert_eq!(resolution.source, NamespaceSource::Environment);
std::env::remove_var("SQLITE_GRAPHRAG_NAMESPACE");
}
#[test]
fn namespace_detect_response_serializa_todos_campos() {
let resp = NamespaceDetectResponse {
namespace: "meu-projeto".to_string(),
source: NamespaceSource::ExplicitFlag,
cwd: "/home/usuario/projeto".to_string(),
elapsed_ms: 3,
};
let json = serde_json::to_value(&resp).unwrap();
assert_eq!(json["namespace"], "meu-projeto");
assert_eq!(json["source"], "explicit_flag");
assert!(json["cwd"].is_string());
assert_eq!(json["elapsed_ms"], 3);
}
#[test]
fn namespace_source_serializa_em_snake_case() {
let casos = vec![
(NamespaceSource::ExplicitFlag, "explicit_flag"),
(NamespaceSource::Environment, "environment"),
(NamespaceSource::Default, "default"),
];
for (source, esperado) in casos {
let json = serde_json::to_value(source).unwrap();
assert_eq!(
json, esperado,
"NamespaceSource::{source:?} deve serializar como \"{esperado}\""
);
}
}
#[test]
fn namespace_detect_aceita_db_como_noop() {
let cli = crate::cli::Cli::try_parse_from([
"sqlite-graphrag",
"namespace-detect",
"--db",
"/tmp/graphrag.sqlite",
])
.expect("namespace-detect deve aceitar --db como no-op");
match cli.command {
crate::cli::Commands::NamespaceDetect(args) => {
assert_eq!(args.db.as_deref(), Some("/tmp/graphrag.sqlite"));
}
_ => panic!("comando incorreto parseado"),
}
}
}