use crate::JumpStartInstance;
use crate::{Config, LocalStarter, starter::parse_starters};
use anyhow::{Context, Result};
use log::debug;
use regex::Regex;
use serde_json::json;
use std::collections::HashMap;
use std::fs::read_to_string;
use std::path::PathBuf;
pub fn find(config: Config, search_term: &str, json: bool) -> Result<()> {
debug!("Finding {search_term}");
let pattern = make_pattern(&search_term)?;
for instance in config.instances {
debug!("Searching instance {:?}", instance.name);
let matches = search_instance(instance.path.clone(), &pattern)?;
for starter in matches {
println!("{}", format_result(&instance, &starter, json)?);
}
}
Ok(())
}
pub fn format_result(
instance: &JumpStartInstance,
starter: &LocalStarter,
json: bool,
) -> Result<String> {
if json {
let result_json = json!({
"instance": {
"path": instance.path,
"name": instance.name,
},
"starter": {
"group": starter.group,
"name": starter.name,
"main_file": starter.config.as_ref().and_then(|c| c.main_file.as_ref()),
}
});
Ok(serde_json::to_string(&result_json)?)
} else {
Ok(format!(
"{}{}",
instance.path.to_string_lossy(),
starter.path
))
}
}
pub fn make_pattern(search_term: &str) -> Result<Regex> {
let pattern = Regex::new(search_term)?;
Ok(pattern)
}
pub fn search_instance(path: PathBuf, pattern: &Regex) -> Result<Vec<LocalStarter>> {
let mut matches = HashMap::new();
let starter_groups = parse_starters(&path)?;
for group in starter_groups {
for starter in group.1 {
if pattern.is_match(&starter.path) {
matches.insert(starter.path.clone(), starter.clone());
} else {
let full_starter_path = path.join(&starter.path);
let starter_file_matches =
search_starter_files(&starter, full_starter_path, &pattern).with_context(
|| {
format!(
"Error searching starter files for instance at path {:?}",
path
)
},
)?;
matches.extend(starter_file_matches);
}
}
}
Ok(matches.into_values().collect())
}
fn search_starter_files(
starter: &LocalStarter,
path: PathBuf,
pattern: &Regex,
) -> Result<HashMap<String, LocalStarter>> {
let mut matches = HashMap::new();
for entry_result in path.read_dir()? {
let entry = entry_result?;
if entry.file_type()?.is_dir() {
let recursed_matches = search_starter_files(starter, entry.path(), pattern)?;
matches.extend(recursed_matches);
} else if entry.file_type()?.is_file() {
if pattern.is_match(&entry.file_name().to_string_lossy()) {
matches.insert(starter.path.clone(), starter.clone());
} else {
let contents = read_to_string(entry.path())?;
if pattern.is_match(&contents) {
matches.insert(starter.path.clone(), starter.clone());
}
}
}
}
Ok(matches)
}