use std::collections::BTreeSet;
use std::path::PathBuf;
use chrono::Utc;
use serde_json::Value;
use khive_runtime::{KhiveRuntime, Namespace, RuntimeConfig, RuntimeError};
use crate::db_target::resolve_target_db;
use crate::manifest::LANGUAGES;
use crate::source_ingest::{run_code_ingest, CodeSourceIngestOptions};
use crate::CodePack;
impl CodePack {
pub(crate) async fn handle_ingest(&self, params: Value) -> Result<Value, RuntimeError> {
let path_raw = params
.get("path")
.and_then(Value::as_str)
.ok_or_else(|| RuntimeError::InvalidInput("code.ingest requires path".into()))?;
let path = PathBuf::from(path_raw);
if !path.is_dir() {
return Err(RuntimeError::InvalidInput(format!(
"path {path_raw:?} does not exist or is not a directory"
)));
}
let languages: BTreeSet<&'static str> = match params.get("languages") {
None | Some(Value::Null) => LANGUAGES.iter().copied().collect(),
Some(v) => {
let arr = v.as_array().ok_or_else(|| {
RuntimeError::InvalidInput("languages must be an array of strings".into())
})?;
let mut set = BTreeSet::new();
for entry in arr {
let s = entry.as_str().ok_or_else(|| {
RuntimeError::InvalidInput("languages entries must be strings".into())
})?;
let canonical =
LANGUAGES
.iter()
.find(|l| **l == s)
.copied()
.ok_or_else(|| {
RuntimeError::InvalidInput(format!(
"unknown language {s:?}; valid: {}",
LANGUAGES.join(", ")
))
})?;
set.insert(canonical);
}
set
}
};
let db_param = params.get("db").and_then(Value::as_str);
let runtime_db_path = self.runtime.config().db_path.clone();
let db_path = resolve_target_db(db_param, &path, runtime_db_path.as_deref())
.map_err(RuntimeError::InvalidInput)?;
let config = RuntimeConfig {
db_path: Some(db_path.clone()),
packs: vec!["kg".to_string(), "code".to_string()],
..RuntimeConfig::no_embeddings()
};
let target_rt = KhiveRuntime::new(config).map_err(|e| {
RuntimeError::InvalidInput(format!("opening target db {db_path:?}: {e}"))
})?;
let token = target_rt
.authorize(Namespace::local())
.map_err(|e| RuntimeError::InvalidInput(format!("authorizing target db: {e}")))?;
let report = run_code_ingest(
&target_rt,
&token,
CodeSourceIngestOptions {
path: &path,
languages,
sweep_time: Utc::now(),
},
)
.await
.map_err(|e| RuntimeError::InvalidInput(e.to_string()))?;
let mut value = serde_json::to_value(&report)
.map_err(|e| RuntimeError::InvalidInput(format!("serializing report: {e}")))?;
value["db_path"] = Value::String(db_path.display().to_string());
Ok(value)
}
}