pub mod code_graph;
pub mod code_symbols;
pub mod codebase_pack;
pub mod compression;
pub mod memory;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IntegrationKind {
None,
CodebasePack,
CodeGraph,
CodeSymbols,
Memory,
Compression,
}
impl IntegrationKind {
#[must_use]
pub fn parse(s: &str) -> Self {
match s.trim().to_ascii_lowercase().replace('_', "-").as_str() {
"codebase-pack" | "pack" | "repomix" => Self::CodebasePack,
"code-graph" | "graph" | "callgraph" => Self::CodeGraph,
"code-symbols" | "symbols" | "lsp" => Self::CodeSymbols,
"memory" | "mem" => Self::Memory,
"compression" | "compress" | "compressor" => Self::Compression,
_ => Self::None,
}
}
#[must_use]
pub fn from_categories(categories: &[String]) -> Self {
categories
.iter()
.map(|c| Self::parse(c))
.find(|k| !k.is_none())
.unwrap_or(Self::None)
}
#[must_use]
pub fn as_str(self) -> &'static str {
match self {
Self::None => "none",
Self::CodebasePack => "codebase-pack",
Self::CodeGraph => "code-graph",
Self::CodeSymbols => "code-symbols",
Self::Memory => "memory",
Self::Compression => "compression",
}
}
#[must_use]
pub fn is_none(self) -> bool {
matches!(self, Self::None)
}
}
#[must_use]
pub fn ingest_spawn(
kind: IntegrationKind,
server: &str,
tool: &str,
text: &str,
project_root: &str,
) -> bool {
let (server, tool, text, root) = (
server.to_string(),
tool.to_string(),
text.to_string(),
project_root.to_string(),
);
match kind {
IntegrationKind::CodeGraph => {
std::thread::spawn(move || code_graph::ingest(&server, &tool, &text, &root));
true
}
IntegrationKind::CodeSymbols => {
std::thread::spawn(move || code_symbols::ingest(&server, &tool, &text, &root));
true
}
IntegrationKind::Memory => {
std::thread::spawn(move || memory::ingest(&server, &tool, &text, &root));
true
}
IntegrationKind::None | IntegrationKind::CodebasePack | IntegrationKind::Compression => {
false
}
}
}
#[must_use]
pub fn transform(
kind: IntegrationKind,
server: &str,
tool: &str,
text: &str,
budget_tokens: usize,
) -> Option<String> {
match kind {
IntegrationKind::CodebasePack => {
codebase_pack::transform(server, tool, text, budget_tokens)
}
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_canonical_and_aliases() {
assert_eq!(
IntegrationKind::parse("codebase-pack"),
IntegrationKind::CodebasePack
);
assert_eq!(
IntegrationKind::parse("repomix"),
IntegrationKind::CodebasePack
);
assert_eq!(IntegrationKind::parse("GRAPH"), IntegrationKind::CodeGraph);
assert_eq!(IntegrationKind::parse("mem"), IntegrationKind::Memory);
assert_eq!(
IntegrationKind::parse("compressor"),
IntegrationKind::Compression
);
assert_eq!(IntegrationKind::parse("whatever"), IntegrationKind::None);
}
#[test]
fn slug_round_trips() {
for k in [
IntegrationKind::CodebasePack,
IntegrationKind::CodeGraph,
IntegrationKind::CodeSymbols,
IntegrationKind::Memory,
IntegrationKind::Compression,
] {
assert_eq!(IntegrationKind::parse(k.as_str()), k);
}
}
#[test]
fn from_categories_finds_first_match() {
let cats = vec!["workflow".into(), "graph".into(), "search".into()];
assert_eq!(
IntegrationKind::from_categories(&cats),
IntegrationKind::CodeGraph
);
let none = vec!["workflow".into(), "plans".into()];
assert_eq!(
IntegrationKind::from_categories(&none),
IntegrationKind::None
);
}
#[test]
fn untyped_kinds_do_not_claim_ingestion() {
assert!(!ingest_spawn(IntegrationKind::None, "s", "t", "x", "/tmp"));
assert!(!ingest_spawn(
IntegrationKind::CodebasePack,
"s",
"t",
"x",
"/tmp"
));
assert!(!ingest_spawn(
IntegrationKind::Compression,
"s",
"t",
"x",
"/tmp"
));
}
}