1use serde::Serialize;
2
3mod block_parser;
4pub mod blocks;
5pub mod diff_parser;
6pub mod flags;
7pub mod language_parsers;
8mod tag_parser;
9pub mod validators;
10
11#[derive(Serialize, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
12struct Position {
13 line: usize,
15 character: usize,
17}
18
19impl Position {
20 pub fn new(line: usize, character: usize) -> Self {
21 Self { line, character }
22 }
23}
24
25#[cfg(test)]
26mod test_utils {
27 use crate::blocks::{FileBlocks, FileSystem, PathChecker, parse_blocks};
28 use crate::diff_parser::LineChange;
29 use crate::language_parsers;
30 use crate::validators::ValidationContext;
31 use std::collections::{HashMap, HashSet};
32 use std::ops::Range;
33 use std::path::{Path, PathBuf};
34 use std::sync::Arc;
35
36 pub(crate) fn substr_range(input: &str, substr: &str) -> Range<usize> {
42 let pos = input.find(substr).unwrap();
43 pos..(pos + substr.len())
44 }
45
46 pub(crate) struct FakeFileSystem {
47 files: HashMap<String, String>,
48 }
49
50 impl FakeFileSystem {
51 pub(crate) fn new(files: HashMap<String, String>) -> Self {
52 Self { files }
53 }
54 }
55
56 impl FileSystem for FakeFileSystem {
57 fn read_to_string(&self, path: &Path) -> anyhow::Result<String> {
58 Ok(self
59 .files
60 .get(&path.display().to_string())
61 .unwrap_or_else(|| panic!("File {} not found", path.display()))
62 .clone())
63 }
64
65 fn walk(&self) -> impl Iterator<Item = anyhow::Result<PathBuf>> {
66 self.files.keys().map(|p| Ok(PathBuf::from(p)))
67 }
68 }
69
70 pub(crate) struct FakePathChecker {
71 ignored_paths: HashSet<String>,
72 }
73
74 impl FakePathChecker {
75 pub(crate) fn with_ignored_paths(ignored_paths: HashSet<String>) -> Self {
76 Self { ignored_paths }
77 }
78
79 pub(crate) fn allow_all() -> Self {
80 Self::with_ignored_paths(HashSet::new())
81 }
82 }
83
84 impl PathChecker for FakePathChecker {
85 fn should_allow(&self, _unused_path: &Path) -> bool {
86 true
87 }
88
89 fn should_ignore(&self, path: &Path) -> bool {
90 self.ignored_paths.contains(&path.display().to_string())
91 }
92 }
93
94 pub(crate) fn validation_context(file_name: &str, contents: &str) -> Arc<ValidationContext> {
96 let line_changes: Vec<LineChange> = contents
97 .lines()
98 .enumerate()
99 .map(|(line, _)| LineChange {
100 line: line + 1,
101 ranges: None,
102 })
103 .collect();
104 validation_context_with_changes(file_name, contents, line_changes)
105 }
106
107 pub(crate) fn validation_context_with_changes(
109 file_name: &str,
110 contents: &str,
111 line_changes: Vec<LineChange>,
112 ) -> Arc<ValidationContext> {
113 let file_system = FakeFileSystem::new(HashMap::from([(
114 file_name.to_string(),
115 contents.to_string(),
116 )]));
117 let line_changes_by_file = HashMap::from([(file_name.into(), line_changes)]);
118 Arc::new(ValidationContext::new(
119 parse_blocks(
120 line_changes_by_file,
121 false,
122 &file_system,
123 &FakePathChecker::allow_all(),
124 language_parsers::language_parsers().unwrap(),
125 HashMap::new(),
126 )
127 .unwrap(),
128 ))
129 }
130
131 pub(crate) fn merge_validation_contexts(
132 contexts: Vec<Arc<ValidationContext>>,
133 ) -> Arc<ValidationContext> {
134 let mut merged_modified_blocks = HashMap::new();
135 for context in contexts {
136 for (file_path, file_blocks) in &context.blocks {
137 merged_modified_blocks
138 .entry(file_path.clone())
139 .or_insert_with(|| FileBlocks {
140 file_content: file_blocks.file_content.clone(),
141 blocks_with_context: vec![],
142 })
143 .blocks_with_context
144 .extend(file_blocks.blocks_with_context.clone());
145 }
146 }
147 Arc::new(ValidationContext::new(merged_modified_blocks))
148 }
149}