use std::collections::{HashMap, HashSet};
use crate::shared::cli_error::CliResult;
#[derive(Debug, Clone)]
pub struct TreeParser {
components: HashMap<String, ComponentEntry>,
}
#[derive(Debug, Clone)]
pub struct ComponentEntry {
pub name: String,
pub category: String,
pub dependencies: Vec<String>,
pub cargo_deps: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct ResolvedSet {
pub components: HashSet<String>,
pub cargo_deps: HashSet<String>,
pub parent_dirs: HashSet<String>,
}
impl TreeParser {
pub fn parse_tree_md(content: &str) -> CliResult<Self> {
let mut components = HashMap::new();
let mut current_component: Option<ComponentEntry> = None;
let mut dependency_stack: Vec<String> = Vec::new();
for line in content.lines() {
let line = line.trim();
if line.is_empty() || line.starts_with("```") {
continue;
}
if let Some(line_content) = line.strip_prefix("* ") {
if let Some(component) = current_component.take() {
components.insert(component.name.clone(), component);
}
if let Some((name_part, category_part)) = line_content.rsplit_once(" (") {
let name = name_part.trim().to_string();
let category = category_part.trim_end_matches(')').to_string();
current_component = Some(ComponentEntry {
name: name.clone(),
category,
dependencies: Vec::new(),
cargo_deps: Vec::new(),
});
dependency_stack.clear();
dependency_stack.push(name);
}
}
else if let Some(dep_content) = line.strip_prefix("** ") {
if let Some(cargo_dep_name) = dep_content.strip_prefix("cargo: ") {
let cargo_dep = cargo_dep_name.trim().to_string();
if let Some(ref mut component) = current_component {
component.cargo_deps.push(cargo_dep);
}
} else if let Some((dep_name, _)) = dep_content.rsplit_once(" (") {
let dep_name = dep_name.trim().to_string();
if let Some(ref mut component) = current_component {
component.dependencies.push(dep_name.clone());
}
dependency_stack.truncate(1); dependency_stack.push(dep_name);
}
}
else if let Some(dep_content) = line.strip_prefix("*** ") {
if let Some(cargo_dep_name) = dep_content.strip_prefix("cargo: ") {
let cargo_dep = cargo_dep_name.trim().to_string();
if let Some(ref mut component) = current_component {
component.cargo_deps.push(cargo_dep);
}
} else if let Some((dep_name, _)) = dep_content.rsplit_once(" (") {
let dep_name = dep_name.trim().to_string();
if let Some(ref mut component) = current_component {
component.dependencies.push(dep_name);
}
}
}
}
if let Some(component) = current_component {
components.insert(component.name.clone(), component);
}
Ok(TreeParser { components })
}
pub fn get_all_component_names(&self) -> Vec<String> {
let mut names: Vec<String> = self.components.keys().cloned().collect();
names.sort();
names
}
pub fn resolve_dependencies(&self, user_components: &[String]) -> CliResult<ResolvedSet> {
let mut resolved_components = HashSet::new();
let mut resolved_cargo_deps = HashSet::new();
let mut resolved_parent_dirs = HashSet::new();
for component_name in user_components {
if let Some(component_entry) = self.components.get(component_name) {
resolved_components.insert(component_name.clone());
resolved_parent_dirs.insert(component_entry.category.clone());
for dep in &component_entry.dependencies {
resolved_components.insert(dep.clone());
if let Some(dep_entry) = self.components.get(dep) {
resolved_parent_dirs.insert(dep_entry.category.clone());
}
}
for cargo_dep in &component_entry.cargo_deps {
resolved_cargo_deps.insert(cargo_dep.clone());
}
} else {
println!("⚠️ Component '{}' not found in registry. Skipping...", component_name);
}
}
println!("📦 Final set of resolved components: {:?}", resolved_components);
println!("📦 Final set of cargo dependencies: {:?}", resolved_cargo_deps);
Ok(ResolvedSet {
components: resolved_components,
cargo_deps: resolved_cargo_deps,
parent_dirs: resolved_parent_dirs,
})
}
}