#![allow(clippy::expect_used, clippy::unwrap_used, clippy::manual_assert)]
use crate::error::SurgeonError;
use crate::surgeon::{ExtractedSymbol, Surgeon};
use pathfinder_common::types::{SemanticPath, SymbolScope};
use std::path::{Path, PathBuf};
use std::sync::Mutex;
#[derive(Debug, Default)]
pub struct MockSurgeon {
pub read_symbol_scope_results: Mutex<Vec<Result<SymbolScope, SurgeonError>>>,
#[allow(clippy::type_complexity)]
pub read_source_file_results:
Mutex<Vec<Result<(String, String, Vec<ExtractedSymbol>), SurgeonError>>>,
pub extract_symbols_results: Mutex<Vec<Result<Vec<ExtractedSymbol>, SurgeonError>>>,
pub enclosing_symbol_results: Mutex<Vec<Result<Option<String>, SurgeonError>>>,
pub generate_skeleton_results: Mutex<Vec<Result<crate::repo_map::RepoMapResult, SurgeonError>>>,
pub node_type_at_position_results: Mutex<Vec<Result<String, SurgeonError>>>,
pub read_symbol_scope_calls: Mutex<Vec<(PathBuf, SemanticPath)>>,
pub read_source_file_calls: Mutex<Vec<(PathBuf, PathBuf)>>,
pub extract_symbols_calls: Mutex<Vec<(PathBuf, PathBuf)>>,
pub enclosing_symbol_calls: Mutex<Vec<(PathBuf, PathBuf, usize)>>,
#[allow(clippy::type_complexity)]
pub generate_skeleton_calls:
Mutex<Vec<(PathBuf, PathBuf, crate::repo_map::SkeletonConfig<'static>)>>,
pub node_type_at_position_calls: Mutex<Vec<(PathBuf, PathBuf, usize, usize)>>,
}
impl MockSurgeon {
#[must_use]
pub fn new() -> Self {
Self::default()
}
fn semantic_path_dispatch<T>(
calls_mutex: &Mutex<Vec<(PathBuf, SemanticPath)>>,
results_mutex: &Mutex<Vec<Result<T, SurgeonError>>>,
workspace_root: &Path,
semantic_path: &SemanticPath,
method_name: &str,
) -> Result<T, SurgeonError> {
calls_mutex
.lock()
.expect("mutex poisoned")
.push((workspace_root.to_path_buf(), semantic_path.clone()));
let mut results = results_mutex.lock().expect("mutex poisoned");
assert!(
!results.is_empty(),
"MockSurgeon: Unexpected call to {method_name}"
);
results.remove(0)
}
fn file_path_dispatch<T>(
calls_mutex: &Mutex<Vec<(PathBuf, PathBuf)>>,
results_mutex: &Mutex<Vec<Result<T, SurgeonError>>>,
workspace_root: &Path,
file_path: &Path,
method_name: &str,
) -> Result<T, SurgeonError> {
calls_mutex
.lock()
.expect("mutex poisoned")
.push((workspace_root.to_path_buf(), file_path.to_path_buf()));
let mut results = results_mutex.lock().expect("mutex poisoned");
assert!(
!results.is_empty(),
"MockSurgeon: Unexpected call to {method_name}"
);
results.remove(0)
}
}
#[async_trait::async_trait]
impl Surgeon for MockSurgeon {
async fn read_symbol_scope(
&self,
workspace_root: &Path,
semantic_path: &SemanticPath,
) -> Result<SymbolScope, SurgeonError> {
Self::semantic_path_dispatch(
&self.read_symbol_scope_calls,
&self.read_symbol_scope_results,
workspace_root,
semantic_path,
"read_symbol_scope",
)
}
async fn read_source_file(
&self,
workspace_root: &Path,
file_path: &Path,
) -> Result<(String, String, Vec<ExtractedSymbol>), SurgeonError> {
Self::file_path_dispatch(
&self.read_source_file_calls,
&self.read_source_file_results,
workspace_root,
file_path,
"read_source_file",
)
}
async fn extract_symbols(
&self,
workspace_root: &Path,
file_path: &Path,
) -> Result<Vec<ExtractedSymbol>, SurgeonError> {
Self::file_path_dispatch(
&self.extract_symbols_calls,
&self.extract_symbols_results,
workspace_root,
file_path,
"extract_symbols",
)
}
async fn enclosing_symbol(
&self,
workspace_root: &Path,
file_path: &Path,
line: usize,
) -> Result<Option<String>, SurgeonError> {
self.enclosing_symbol_calls
.lock()
.expect("mutex poisoned")
.push((workspace_root.to_path_buf(), file_path.to_path_buf(), line));
let mut results = self
.enclosing_symbol_results
.lock()
.expect("mutex poisoned");
assert!(
!results.is_empty(),
"MockSurgeon: Unexpected call to enclosing_symbol"
);
results.remove(0)
}
async fn generate_skeleton(
&self,
_workspace_root: &Path,
path: &Path,
config: &crate::repo_map::SkeletonConfig<'_>,
) -> Result<crate::repo_map::RepoMapResult, SurgeonError> {
let static_config = crate::repo_map::SkeletonConfig {
max_tokens: config.max_tokens,
depth: config.depth,
visibility: if config.visibility == "public" {
"public"
} else {
"all"
},
max_tokens_per_file: config.max_tokens_per_file,
changed_files: config.changed_files.clone(),
include_extensions: config.include_extensions.clone(),
exclude_extensions: config.exclude_extensions.clone(),
include_tests: config.include_tests,
};
self.generate_skeleton_calls.lock().unwrap().push((
path.to_path_buf(),
path.to_path_buf(),
static_config,
));
let mut results = self.generate_skeleton_results.lock().unwrap();
assert!(
!results.is_empty(),
"MockSurgeon: Unexpected call to generate_skeleton"
);
results.remove(0)
}
async fn node_type_at_position(
&self,
workspace_root: &Path,
file_path: &Path,
line: usize,
column: usize,
) -> Result<String, SurgeonError> {
self.node_type_at_position_calls
.lock()
.expect("mutex poisoned")
.push((
workspace_root.to_path_buf(),
file_path.to_path_buf(),
line,
column,
));
let mut results = self
.node_type_at_position_results
.lock()
.expect("mutex poisoned");
if results.is_empty() {
Ok("code".to_owned())
} else {
results.remove(0)
}
}
}