1use std::{
7 collections::{BTreeMap, HashMap},
8 fs,
9 path::{Path, PathBuf},
10};
11
12use rayon::prelude::*;
13use serde::{Deserialize, Serialize};
14
15use crate::{
16 db,
17 parser::{InjectSummary, ParseFile, Snippet},
18 walk::Walk,
19};
20
21#[derive(Debug, Serialize, Deserialize)]
23pub struct Collector {
24 pub root_folder: PathBuf,
25 pub snippets: BTreeMap<PathBuf, Vec<Snippet>>,
26}
27
28#[derive(Debug, Serialize, Deserialize)]
30pub struct Injector {
31 pub root_folder: PathBuf,
32 pub results: BTreeMap<PathBuf, InjectStatus>,
33}
34
35#[derive(Debug, Serialize, Deserialize)]
37pub enum InjectStatus {
38 Injected(InjectSummary),
40 None,
43 Error(String),
45}
46
47impl Collector {
48 #[must_use]
51 pub fn on_files(walk: &Walk) -> Self {
52 let snippets = walk
53 .get_files()
54 .par_iter()
55 .flat_map(|path| Self::on_file(path.as_path()).map(|findings| (path.clone(), findings)))
56 .collect::<BTreeMap<_, _>>();
57
58 Self {
59 root_folder: walk.folder.clone(),
60 snippets,
61 }
62 }
63
64 fn on_file(path: &Path) -> Option<Vec<Snippet>> {
71 let span = tracing::info_span!("parse_file", path = %path.display());
72 let _guard = span.enter();
73 let input = match fs::read_to_string(path) {
74 Ok(file_content) => file_content,
75 Err(err) => {
76 tracing::debug!(err = %err,"could not read file content");
77 return None;
78 }
79 };
80
81 match ParseFile::new(&input).parse() {
82 Ok(findings) => {
83 tracing::debug!("parsed successfully");
84 Some(findings)
85 }
86 Err(err) => {
87 tracing::debug!(err = %err, "could not parse the file");
88 None
89 }
90 }
91 }
92}
93
94impl Injector {
95 #[must_use]
98 pub fn on_files(walk: &Walk, db_data: &db::DBData) -> Self {
99 let mut snippets_from = HashMap::new();
100
101 for (snippet_id, snippet_data) in &db_data.snippets {
102 if !snippet_data.inject {
103 snippets_from.insert(snippet_id.clone(), snippet_data);
104 }
105 }
106
107 let results = walk
108 .get_files()
109 .par_iter()
110 .map(|path| {
111 let status = match fs::read_to_string(path) {
112 Ok(file_content) => Self::inject(&file_content, &snippets_from),
113 Err(err) => {
114 tracing::debug!(err = %err,"could not read file content");
115 InjectStatus::Error(err.to_string())
116 }
117 };
118 (path.clone(), status)
119 })
120 .collect::<BTreeMap<PathBuf, InjectStatus>>();
121
122 Self {
123 root_folder: walk.folder.clone(),
124 results,
125 }
126 }
127
128 pub fn inject(input: &str, snippets: &HashMap<String, &db::DbDataSnippet>) -> InjectStatus {
135 match ParseFile::new(input).inject(snippets) {
136 Ok(summary) => {
137 if summary.actions.is_empty() {
138 tracing::debug!("not found inject content");
139 InjectStatus::None
140 } else {
141 tracing::debug!("content injected");
142 InjectStatus::Injected(summary)
143 }
144 }
145 Err(err) => {
146 tracing::debug!(err = %err, "could not parse the given content");
147 InjectStatus::Error(err.to_string())
148 }
149 }
150 }
151}