Skip to main content

light_fw_cli_core/
project.rs

1use std::collections::BTreeSet;
2use std::path::Path;
3
4use crate::error::Result;
5use crate::manifest::{collect_dependencies, read_cargo_toml};
6use crate::registry::ComponentRegistry;
7use crate::report::{Issue, ProjectReport, Recommendation};
8use crate::resolve::needed_components;
9use crate::scan::{ScanError, scan_templates};
10
11/// Анализирует проект в каталоге `project_root`.
12///
13/// Читает `Cargo.toml`, сканирует шаблоны и сопоставляет использование
14/// компонентов с установленными зависимостями. Ничего не пишет на диск.
15pub fn analyze_project(project_root: &Path) -> Result<ProjectReport> {
16    let cargo_path = project_root.join("Cargo.toml");
17    let cargo_toml = read_cargo_toml(&cargo_path)?;
18    let dependencies = collect_dependencies(&cargo_toml)?;
19
20    let scan = scan_templates(project_root)?;
21    let registry = ComponentRegistry::builtin();
22    let needed = needed_components(&scan, &registry);
23
24    // «Установлено» — только те зависимости, которые реестр знает как компоненты.
25    let installed: Vec<_> = dependencies
26        .iter()
27        .filter(|dep| registry.by_name(&dep.name).is_some())
28        .cloned()
29        .collect();
30    let installed_names: BTreeSet<&str> = installed.iter().map(|c| c.name.as_str()).collect();
31
32    let missing: Vec<_> = needed
33        .iter()
34        .filter(|n| !installed_names.contains(n.name.as_str()))
35        .cloned()
36        .collect();
37
38    let needed_names: BTreeSet<&str> = needed.iter().map(|n| n.name.as_str()).collect();
39    let unused: Vec<_> = installed
40        .iter()
41        .filter(|c| !needed_names.contains(c.name.as_str()))
42        .cloned()
43        .collect();
44
45    let issues: Vec<Issue> = scan
46        .errors
47        .iter()
48        .map(|ScanError { file, message }| Issue {
49            message: format!("{}: {message}", file.display()),
50        })
51        .collect();
52
53    let recommendations: Vec<Recommendation> = missing
54        .iter()
55        .map(|m| {
56            let requirement = registry
57                .by_name(&m.name)
58                .map(|c| c.requirement())
59                .unwrap_or_else(|| m.name.clone());
60            Recommendation {
61                name: m.name.clone(),
62                requirement,
63            }
64        })
65        .collect();
66
67    Ok(ProjectReport {
68        scan,
69        installed,
70        missing,
71        unused,
72        issues,
73        recommendations,
74    })
75}