#![feature(trait_alias)]
pub mod conversion;
pub mod error;
pub mod facade;
pub mod traits;
pub mod types;
pub use types::{
AnalysisContext, AnalysisDepth, CodeMatch, CrossFileRelationship, ExecutionScope,
ParsedDocument, SupportLang, SupportLangErr,
};
pub use error::{
AnalysisError, ContextualError, ContextualResult, ErrorContextExt, ParseError,
RecoverableError, ServiceError, ServiceResult,
};
pub use traits::{
AnalysisPerformanceProfile, AnalyzerCapabilities, CodeAnalyzer, CodeParser, ParserCapabilities,
};
#[cfg(feature = "ast-grep-backend")]
pub use types::{
AstNode,
AstNodeMatch,
AstPosition,
AstRoot,
};
#[cfg(feature = "storage-traits")]
pub use traits::{CacheService, StorageService};
use std::path::Path;
use thiserror::Error;
#[derive(Error, Debug)]
#[deprecated(since = "0.1.0", note = "Use ServiceError instead")]
pub enum LegacyServiceError {
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("Configuration error: {0}")]
Config(String),
#[error("Execution error: {0}")]
Execution(String),
}
pub trait ExecutionContext {
fn read_content(&self, source: &str) -> Result<String, ServiceError>;
fn write_content(&self, destination: &str, content: &str) -> Result<(), ServiceError>;
fn list_sources(&self) -> Result<Vec<String>, ServiceError>;
}
pub struct FileSystemContext {
base_path: std::path::PathBuf,
}
impl FileSystemContext {
pub fn new<P: AsRef<Path>>(base_path: P) -> Self {
Self {
base_path: base_path.as_ref().to_path_buf(),
}
}
}
impl ExecutionContext for FileSystemContext {
fn read_content(&self, source: &str) -> Result<String, ServiceError> {
let path = self.base_path.join(source);
Ok(std::fs::read_to_string(path)?)
}
fn write_content(&self, destination: &str, content: &str) -> Result<(), ServiceError> {
let path = self.base_path.join(destination);
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent)?;
}
Ok(std::fs::write(path, content)?)
}
fn list_sources(&self) -> Result<Vec<String>, ServiceError> {
let mut sources = Vec::new();
for entry in std::fs::read_dir(&self.base_path)? {
let entry = entry?;
if entry.file_type()?.is_file()
&& let Some(name) = entry.file_name().to_str()
{
sources.push(name.to_string());
}
}
Ok(sources)
}
}
pub struct MemoryContext {
content: thread_utilities::RapidMap<String, String>,
}
impl MemoryContext {
pub fn new() -> Self {
Self {
content: thread_utilities::RapidMap::default(),
}
}
pub fn add_content(&mut self, name: String, content: String) {
self.content.insert(name, content);
}
}
impl Default for MemoryContext {
fn default() -> Self {
Self::new()
}
}
impl ExecutionContext for MemoryContext {
fn read_content(&self, source: &str) -> Result<String, ServiceError> {
self.content
.get(source)
.cloned()
.ok_or_else(|| ServiceError::execution_dynamic(format!("Source not found: {source}")))
}
fn write_content(&self, _destination: &str, _content: &str) -> Result<(), ServiceError> {
Ok(())
}
fn list_sources(&self) -> Result<Vec<String>, ServiceError> {
Ok(self.content.keys().cloned().collect())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_memory_context() {
let mut ctx = MemoryContext::new();
ctx.add_content("test.rs".to_string(), "fn main() {}".to_string());
let content = ctx.read_content("test.rs").unwrap();
assert_eq!(content, "fn main() {}");
let sources = ctx.list_sources().unwrap();
assert_eq!(sources, vec!["test.rs"]);
}
}