qlty_analysis/workspace_entries/
args_source.rs

1use crate::{walker::WalkerBuilder, WorkspaceEntry, WorkspaceEntryKind, WorkspaceEntrySource};
2use core::fmt;
3use path_absolutize::Absolutize;
4use rayon::prelude::*;
5use std::{path::PathBuf, sync::Arc};
6
7// qlty-ignore(semgrep/derive-debug): manual Debug impl below
8pub struct ArgsSource {
9    pub root: PathBuf,
10    pub paths: Vec<PathBuf>,
11    pub entries: Arc<Vec<WorkspaceEntry>>,
12}
13
14impl fmt::Debug for ArgsSource {
15    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
16        write!(f, "ArgsSource[{:?}, {:?}]", self.root, self.paths)
17    }
18}
19
20impl ArgsSource {
21    pub fn new(root: PathBuf, paths: Vec<PathBuf>) -> Self {
22        Self {
23            entries: Arc::new(Self::build(&root, &paths)),
24            root,
25            paths,
26        }
27    }
28
29    fn build(root: &PathBuf, paths: &Vec<PathBuf>) -> Vec<WorkspaceEntry> {
30        WalkerBuilder::new()
31            .build(paths)
32            .par_bridge()
33            .map(|entry| {
34                let entry = entry.unwrap();
35                let path = entry.path();
36
37                let workspace_entry_kind = if path.is_dir() {
38                    WorkspaceEntryKind::Directory
39                } else {
40                    WorkspaceEntryKind::File
41                };
42
43                let clean_path = path
44                    .absolutize()
45                    .ok()
46                    .unwrap()
47                    .strip_prefix(root)
48                    .ok()
49                    .unwrap()
50                    .to_path_buf();
51                let metadata = entry.metadata().ok().unwrap();
52
53                WorkspaceEntry {
54                    path: clean_path,
55                    content_modified: metadata.modified().ok().unwrap(),
56                    contents_size: metadata.len(),
57                    kind: workspace_entry_kind,
58                    language_name: None,
59                }
60            })
61            .collect::<Vec<_>>()
62    }
63}
64
65impl WorkspaceEntrySource for ArgsSource {
66    fn entries(&self) -> Arc<Vec<WorkspaceEntry>> {
67        self.entries.clone()
68    }
69}
70
71#[cfg(test)]
72mod test {
73    use super::*;
74    use itertools::Itertools;
75    use qlty_test_utilities::git::build_sample_project;
76
77    #[test]
78    fn test_args_source_next() {
79        let root = build_sample_project();
80        let args = vec![
81            root.path().to_path_buf().join("lib/tasks/ops"),
82            root.path().to_path_buf().join("greetings.rb"),
83        ];
84        let source = ArgsSource::new(root.path().to_path_buf(), args);
85
86        let mut paths = vec![];
87
88        for workspace_entry in source.entries().iter() {
89            let workspace_entry = workspace_entry.clone();
90            paths.push((workspace_entry.path, workspace_entry.kind));
91        }
92
93        let expected_paths = build_expected_workspace_entries(vec![
94            ("lib/tasks/ops", WorkspaceEntryKind::Directory),
95            ("lib/tasks/ops/deploy.rb", WorkspaceEntryKind::File),
96            ("lib/tasks/ops/setup.rb", WorkspaceEntryKind::File),
97            ("greetings.rb", WorkspaceEntryKind::File),
98        ]);
99
100        assert_eq!(
101            paths
102                .iter()
103                .cloned()
104                .sorted()
105                .collect::<Vec<(PathBuf, WorkspaceEntryKind)>>(),
106            expected_paths
107        );
108    }
109
110    #[test]
111    fn test_args_source_includes_hidden_files() {
112        let root = build_sample_project();
113        std::fs::write(
114            root.path().join("lib/tasks/ops/.hidden"),
115            "This is a hidden file.",
116        )
117        .unwrap();
118        let args = vec![root.path().to_path_buf().join("lib/tasks/ops")];
119        let source = ArgsSource::new(root.path().to_path_buf(), args);
120
121        let mut paths = vec![];
122
123        for workspace_entry in source.entries().iter() {
124            paths.push(workspace_entry.clone().path);
125        }
126
127        assert!(
128            paths.contains(&PathBuf::from("lib/tasks/ops/.hidden")),
129            "Expected .hidden file to be included in the paths, but it wasn't."
130        );
131    }
132
133    fn build_expected_workspace_entries(
134        workspace_entries: Vec<(&str, WorkspaceEntryKind)>,
135    ) -> Vec<(PathBuf, WorkspaceEntryKind)> {
136        workspace_entries
137            .into_iter()
138            .map(|(s, tt)| (PathBuf::from(s), tt))
139            .sorted()
140            .collect()
141    }
142}