use anyhow::Result;
use gnaw_adapters::git::{get_git_diff, get_git_diff_between_branches, get_git_log};
use gnaw_adapters::{
ChangedChunker, ChangedPathsSource, ChangedScope, CommitRangeSource, FullWalkTree,
HandlebarsRenderer, IdentityChunker, ItemsTree, PatternSelector, RendererConfig,
SecretScrubber, TakeUntilBudget, TiktokenCounter, Uniform, WorkingTreeSource,
};
use gnaw_core::builtin_templates::BuiltinTemplates;
use gnaw_core::configuration::GnawConfig;
use gnaw_core::path::display_name;
use gnaw_core::pipeline::ports::{Chunker, ContextSource, TreeBuilder};
use gnaw_core::pipeline::{PipelineSpec, Rendered, SourceOpts, run};
pub fn build_spec(config: &GnawConfig) -> Result<PipelineSpec> {
let selector = Box::new(PatternSelector::new(
&config.include_patterns,
&config.exclude_patterns,
));
let scrubber = Box::new(SecretScrubber::new(config));
let ranker = Box::new(Uniform);
let budgeter = Box::new(TakeUntilBudget::new(Box::new(TiktokenCounter::new(
config.encoding,
))));
let renderer = Box::new(HandlebarsRenderer::new(renderer_config_for(config)?));
let root_label = display_name(&config.path);
let (source, chunker, tree_builder): (
Box<dyn ContextSource>,
Box<dyn Chunker>,
Box<dyn TreeBuilder>,
) = if let Some((ref1, ref2)) = config.diff_shas.clone() {
(
Box::new(CommitRangeSource::new(config.clone(), ref1, ref2)),
Box::new(ChangedChunker),
Box::new(ItemsTree),
)
} else if let Some(scope) = git_narrative_scope(config) {
(
Box::new(ChangedPathsSource::new(config.clone(), scope)),
Box::new(IdentityChunker),
Box::new(ItemsTree),
)
} else {
let tree: Box<dyn TreeBuilder> = if config.full_directory_tree {
Box::new(FullWalkTree::new(config.clone()))
} else {
Box::new(ItemsTree)
};
(
Box::new(WorkingTreeSource::new(config.clone())),
Box::new(IdentityChunker),
tree,
)
};
Ok(PipelineSpec {
source,
selector,
chunker,
scrubber,
ranker,
budgeter,
renderer,
tree_builder,
budget: 0,
root_label,
sort_method: config.sort_method,
})
}
pub fn run_extraction(config: &GnawConfig) -> Result<Rendered> {
let spec = build_spec(config)?;
let rendered = run(&spec, &SourceOpts)?;
Ok(rendered)
}
fn default_renderer_config(config: &GnawConfig) -> Result<RendererConfig> {
let git_diff = if config.diff_enabled {
Some(get_git_diff(&config.path, config.diff_mode)?)
} else {
None
};
let git_diff_branch = match &config.diff_branches {
Some((a, b)) => Some(get_git_diff_between_branches(&config.path, a, b)?),
None => None,
};
let git_log_branch = match &config.log_branches {
Some((a, b)) => Some(get_git_log(&config.path, a, b)?),
None => None,
};
Ok(RendererConfig {
no_codeblock: config.no_codeblock,
line_numbers: config.line_numbers,
git_diff,
git_diff_branch,
git_log_branch,
template_str: config.template_str.clone(),
template_name: config.template_name.clone(),
output_format: config.output_format,
user_variables: config.user_variables.clone(),
})
}
fn changed_renderer_config(config: &GnawConfig) -> Result<RendererConfig> {
let (template_str, template_name) = if config.template_str.is_empty() {
let t = BuiltinTemplates::get_template("git-diff-shas-pipeline")
.ok_or_else(|| anyhow::anyhow!("builtin git-diff-shas-pipeline template missing"))?;
(t.content.to_string(), "git-diff-shas-pipeline".to_string())
} else {
(config.template_str.clone(), config.template_name.clone())
};
Ok(RendererConfig {
no_codeblock: config.no_codeblock,
line_numbers: config.line_numbers,
git_diff: None,
git_diff_branch: None,
git_log_branch: None,
template_str,
template_name,
output_format: config.output_format,
user_variables: config.user_variables.clone(),
})
}
fn renderer_config_for(config: &GnawConfig) -> Result<RendererConfig> {
if config.diff_shas.is_some() {
changed_renderer_config(config)
} else {
default_renderer_config(config)
}
}
pub fn build_renderer_for(config: &GnawConfig) -> Result<HandlebarsRenderer> {
Ok(HandlebarsRenderer::new(renderer_config_for(config)?))
}
fn git_narrative_scope(config: &GnawConfig) -> Option<ChangedScope> {
if !config.git_narrative {
return None;
}
if let Some((b1, b2)) = config
.diff_branches
.clone()
.or_else(|| config.log_branches.clone())
{
Some(ChangedScope::Refs(b1, b2)) } else if config.diff_enabled {
Some(ChangedScope::WorkingTree(config.diff_mode)) } else {
None
}
}