relay_knowledge/code/source/layout/
preview.rs1use std::{collections::BTreeMap, path::PathBuf};
2
3use crate::{
4 code::{
5 CodeIndexError, generated_detection, languages::language_id,
6 parser::dependency_manifest_language_ids,
7 },
8 domain::{
9 CodeRepositoryExcludedPath, CodeRepositoryLanguagePreview, CodeRepositoryLargestFile,
10 CodeRepositoryRegistration, CodeRepositoryScopePreview, CodeRepositorySelector,
11 },
12};
13
14use super::{
15 discovery::discover_source_layout,
16 scoped_snapshot::{
17 filesystem_policy_for_selector, registration_allows_filesystem_ref,
18 scoped_filesystem_tree_hash, source_snapshot_for_scope,
19 },
20 selection::selection_exclusion_reason_for_source,
21};
22
23const PREVIEW_MAX_EXCLUDED_PATHS: usize = 50;
24const PREVIEW_MAX_LARGEST_FILES: usize = 10;
25const DEFAULT_TEXT_FILE_BUDGET_BYTES: usize = 512 * 1024;
26
27pub fn preview_repository_scope(
29 registration: &CodeRepositoryRegistration,
30 selector: &CodeRepositorySelector,
31) -> Result<CodeRepositoryScopePreview, CodeIndexError> {
32 let root = PathBuf::from(®istration.root_path);
33 let filesystem_policy = filesystem_policy_for_selector(registration, selector);
34 let allow_filesystem_ref =
35 registration_allows_filesystem_ref(registration, &root, &selector.ref_selector)?;
36 let snapshot = source_snapshot_for_scope(
37 &root,
38 &selector.ref_selector,
39 filesystem_policy,
40 allow_filesystem_ref,
41 )?;
42 let mut selected_byte_count = 0usize;
43 let mut selected_file_count = 0usize;
44 let mut unsupported_file_count = 0usize;
45 let mut generated_or_heavy_file_count = 0usize;
46 let mut expected_degraded_file_count = 0usize;
47 let mut language_distribution = BTreeMap::<String, (usize, usize)>::new();
48 let mut largest_files = Vec::<CodeRepositoryLargestFile>::new();
49 let mut excluded_paths = Vec::<CodeRepositoryExcludedPath>::new();
50
51 let entries = snapshot.entries;
52 let source_layout = discover_source_layout(&entries);
53 let mut selected_entries = Vec::new();
54 for entry in entries {
55 if let Some(reason) = selection_exclusion_reason_for_source(
56 &entry.path,
57 registration,
58 selector,
59 &source_layout,
60 snapshot.kind,
61 ) {
62 if excluded_paths.len() < PREVIEW_MAX_EXCLUDED_PATHS {
63 excluded_paths.push(CodeRepositoryExcludedPath {
64 path: entry.path,
65 reason,
66 });
67 }
68 continue;
69 }
70 let language = preview_language_id(&entry.path);
71 selected_file_count += 1;
72 selected_byte_count = selected_byte_count.saturating_add(entry.byte_count);
73 let bucket = language_distribution
74 .entry(language.to_owned())
75 .or_insert((0, 0));
76 bucket.0 += 1;
77 bucket.1 = bucket.1.saturating_add(entry.byte_count);
78 let is_unsupported = language == "unknown";
79 let is_generated = generated_detection::path_has_generated_signal(&entry.path);
80 let is_heavy = entry.byte_count > DEFAULT_TEXT_FILE_BUDGET_BYTES;
81 if is_unsupported {
82 unsupported_file_count += 1;
83 }
84 if is_generated || is_heavy {
85 generated_or_heavy_file_count += 1;
86 }
87 if is_unsupported || is_heavy {
88 expected_degraded_file_count += 1;
89 }
90 largest_files.push(CodeRepositoryLargestFile {
91 path: entry.path.clone(),
92 byte_count: entry.byte_count,
93 });
94 selected_entries.push(entry);
95 }
96 let (resolved_commit_sha, tree_hash, _) = if snapshot.kind.is_filesystem() {
97 scoped_filesystem_tree_hash(&snapshot.root, &selected_entries, &selector.ref_selector)?
98 } else {
99 (
100 snapshot.resolved_commit_sha,
101 snapshot.tree_hash,
102 BTreeMap::new(),
103 )
104 };
105 largest_files.sort_by(|left, right| {
106 right
107 .byte_count
108 .cmp(&left.byte_count)
109 .then_with(|| left.path.cmp(&right.path))
110 });
111 largest_files.truncate(PREVIEW_MAX_LARGEST_FILES);
112
113 Ok(CodeRepositoryScopePreview {
114 repository_id: registration.repository_id.clone(),
115 alias: registration.alias.clone(),
116 requested_ref: selector.ref_selector.clone(),
117 resolved_commit_sha,
118 tree_hash,
119 selected_file_count,
120 selected_byte_count,
121 unsupported_file_count,
122 generated_or_heavy_file_count,
123 expected_degraded_file_count,
124 language_distribution: language_distribution
125 .into_iter()
126 .map(
127 |(language_id, (file_count, byte_count))| CodeRepositoryLanguagePreview {
128 language_id,
129 file_count,
130 byte_count,
131 },
132 )
133 .collect(),
134 largest_files,
135 excluded_paths,
136 })
137}
138
139fn preview_language_id(path: &str) -> &'static str {
140 language_id(path).unwrap_or_else(|| {
141 dependency_manifest_language_ids(path)
142 .and_then(|languages| languages.first().copied())
143 .unwrap_or("unknown")
144 })
145}
146
147#[cfg(test)]
148#[path = "preview_tests.rs"]
149mod tests;