Skip to content

Commit 42a8c1c

Browse files
author
ODAncona
committed
consolidated config
1 parent a1bd729 commit 42a8c1c

File tree

2 files changed

+214
-188
lines changed

2 files changed

+214
-188
lines changed

crates/code2prompt/src/config.rs

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
//! Configuration parsing and session creation utilities.
2+
//!
3+
//! This module handles the conversion of command-line arguments into
4+
//! Code2PromptSession instances, consolidating all configuration parsing
5+
//! logic in one place for better maintainability and separation of concerns.
6+
7+
use anyhow::{Context, Result};
8+
use code2prompt_core::{
9+
configuration::Code2PromptConfig, session::Code2PromptSession, sort::FileSortMethod,
10+
template::extract_undefined_variables, tokenizer::TokenizerType,
11+
};
12+
use inquire::Text;
13+
use log::error;
14+
use std::{path::PathBuf, str::FromStr};
15+
16+
use crate::args::Cli;
17+
18+
/// Create a Code2PromptSession from command line arguments
19+
///
20+
/// # Arguments
21+
///
22+
/// * `args` - The parsed command line arguments
23+
/// * `tui_mode` - Whether the application is running in TUI mode
24+
///
25+
/// # Returns
26+
///
27+
/// * `Result<Code2PromptSession>` - The configured session or an error
28+
pub fn create_session_from_args(args: &Cli, tui_mode: bool) -> Result<Code2PromptSession> {
29+
let mut configuration = Code2PromptConfig::builder();
30+
31+
configuration.path(args.path.clone());
32+
33+
// Handle comma-separated patterns, but preserve brace expansion patterns
34+
let include_patterns = expand_comma_separated_patterns(&args.include);
35+
let exclude_patterns = expand_comma_separated_patterns(&args.exclude);
36+
37+
configuration
38+
.include_patterns(include_patterns)
39+
.exclude_patterns(exclude_patterns);
40+
41+
let output_format = args.output_format.clone();
42+
configuration
43+
.line_numbers(args.line_numbers)
44+
.absolute_path(args.absolute_paths)
45+
.full_directory_tree(args.full_directory_tree)
46+
.output_format(output_format);
47+
48+
let sort_method = args
49+
.sort
50+
.as_deref()
51+
.map(FileSortMethod::from_str)
52+
.transpose()
53+
.unwrap_or_else(|err| {
54+
eprintln!("{}", err);
55+
std::process::exit(1);
56+
})
57+
.unwrap_or(FileSortMethod::NameAsc);
58+
59+
configuration.sort_method(sort_method);
60+
61+
let tokenizer_type = args
62+
.encoding
63+
.as_deref()
64+
.unwrap_or("cl100k")
65+
.parse::<TokenizerType>()
66+
.unwrap_or_default();
67+
68+
configuration
69+
.encoding(tokenizer_type)
70+
.token_format(args.tokens.clone());
71+
72+
let (template_str, template_name) = parse_template(&args.template).unwrap_or_else(|e| {
73+
error!("Failed to parse template: {}", e);
74+
std::process::exit(1);
75+
});
76+
77+
configuration
78+
.template_str(template_str.clone())
79+
.template_name(template_name);
80+
81+
let diff_branches = parse_branch_argument(&args.git_diff_branch);
82+
let log_branches = parse_branch_argument(&args.git_log_branch);
83+
84+
configuration
85+
.diff_enabled(args.diff)
86+
.diff_branches(diff_branches)
87+
.log_branches(log_branches);
88+
89+
configuration
90+
.no_ignore(args.no_ignore)
91+
.hidden(args.hidden)
92+
.no_codeblock(args.no_codeblock)
93+
.follow_symlinks(args.follow_symlinks)
94+
.token_map_enabled(args.token_map || tui_mode);
95+
96+
let session = Code2PromptSession::new(configuration.build()?);
97+
Ok(session)
98+
}
99+
100+
/// Parses the branch argument from command line options.
101+
///
102+
/// Takes an optional vector of strings and converts it to a tuple of two branch names
103+
/// if exactly two branches are provided.
104+
///
105+
/// # Arguments
106+
///
107+
/// * `branch_arg` - An optional vector containing branch names
108+
///
109+
/// # Returns
110+
///
111+
/// * `Option<(String, String)>` - A tuple of (from_branch, to_branch) if two branches were provided, None otherwise
112+
pub fn parse_branch_argument(branch_arg: &Option<Vec<String>>) -> Option<(String, String)> {
113+
match branch_arg {
114+
Some(branches) if branches.len() == 2 => Some((branches[0].clone(), branches[1].clone())),
115+
_ => None,
116+
}
117+
}
118+
119+
/// Loads a template from a file path or returns default values.
120+
///
121+
/// # Arguments
122+
///
123+
/// * `template_arg` - An optional path to a template file
124+
///
125+
/// # Returns
126+
///
127+
/// * `Result<(String, String)>` - A tuple containing (template_content, template_name)
128+
/// where template_name is "custom" for user-provided templates or "default" otherwise
129+
pub fn parse_template(template_arg: &Option<PathBuf>) -> Result<(String, String)> {
130+
match template_arg {
131+
Some(path) => {
132+
let template_str =
133+
std::fs::read_to_string(path).context("Failed to load custom template file")?;
134+
Ok((template_str, "custom".to_string()))
135+
}
136+
None => Ok(("".to_string(), "default".to_string())),
137+
}
138+
}
139+
140+
/// Handles user-defined variables in the template and adds them to the data.
141+
///
142+
/// This function extracts undefined variables from the template and prompts
143+
/// the user to provide values for them through interactive input.
144+
///
145+
/// # Arguments
146+
///
147+
/// * `data` - The JSON data object to modify
148+
/// * `template_content` - The template content string to analyze
149+
///
150+
/// # Returns
151+
///
152+
/// * `Result<()>` - An empty result indicating success or an error
153+
pub fn handle_undefined_variables(
154+
data: &mut serde_json::Value,
155+
template_content: &str,
156+
) -> Result<()> {
157+
let undefined_variables = extract_undefined_variables(template_content);
158+
let mut user_defined_vars = serde_json::Map::new();
159+
160+
for var in undefined_variables.iter() {
161+
if !data.as_object().unwrap().contains_key(var) {
162+
let prompt = format!("Enter value for '{}': ", var);
163+
let answer = Text::new(&prompt)
164+
.with_help_message("Fill user defined variable in template")
165+
.prompt()
166+
.unwrap_or_default();
167+
user_defined_vars.insert(var.clone(), serde_json::Value::String(answer));
168+
}
169+
}
170+
171+
if let Some(obj) = data.as_object_mut() {
172+
for (key, value) in user_defined_vars {
173+
obj.insert(key, value);
174+
}
175+
}
176+
Ok(())
177+
}
178+
179+
/// Expands comma-separated patterns while preserving brace expansion patterns
180+
///
181+
/// This function handles the expansion of comma-separated include/exclude patterns
182+
/// while being careful not to split patterns that contain brace expansion syntax.
183+
///
184+
/// # Arguments
185+
///
186+
/// * `patterns` - A vector of pattern strings that may contain comma-separated values
187+
///
188+
/// # Returns
189+
///
190+
/// * `Vec<String>` - A vector of individual patterns
191+
fn expand_comma_separated_patterns(patterns: &[String]) -> Vec<String> {
192+
let mut expanded = Vec::new();
193+
194+
for pattern in patterns {
195+
// If the pattern contains braces, don't split on commas (preserve brace expansion)
196+
if pattern.contains('{') && pattern.contains('}') {
197+
expanded.push(pattern.clone());
198+
} else {
199+
// Split on commas for regular patterns
200+
for part in pattern.split(',') {
201+
let trimmed = part.trim();
202+
if !trimmed.is_empty() {
203+
expanded.push(trimmed.to_string());
204+
}
205+
}
206+
}
207+
}
208+
209+
expanded
210+
}

0 commit comments

Comments
 (0)