use crate::prelude::*;
use anyhow::{Context, Result};
use std::fs;
use std::path::Path;
pub mod search;
pub mod transform;
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
pub enum SupportedLanguage {
Rust,
JavaScript,
TypeScript,
Python,
Go,
Java,
C,
Cpp,
}
impl SupportedLanguage {
pub fn get_language_name(&self) -> &'static str {
match self {
SupportedLanguage::Rust => "rust",
SupportedLanguage::JavaScript => "javascript",
SupportedLanguage::TypeScript => "typescript",
SupportedLanguage::Python => "python",
SupportedLanguage::Go => "go",
SupportedLanguage::Java => "java",
SupportedLanguage::C => "c",
SupportedLanguage::Cpp => "cpp",
}
}
pub fn from_extension(ext: &str) -> Option<Self> {
match ext.to_lowercase().as_str() {
"rs" => Some(SupportedLanguage::Rust),
"js" | "jsx" => Some(SupportedLanguage::JavaScript),
"ts" | "tsx" => Some(SupportedLanguage::TypeScript),
"py" => Some(SupportedLanguage::Python),
"go" => Some(SupportedLanguage::Go),
"java" => Some(SupportedLanguage::Java),
"c" | "h" => Some(SupportedLanguage::C),
"cpp" | "cc" | "cxx" | "hpp" => Some(SupportedLanguage::Cpp),
_ => None,
}
}
}
pub struct CodeAnalyzer {
language: SupportedLanguage,
}
impl CodeAnalyzer {
pub fn new(language: SupportedLanguage) -> Self {
Self { language }
}
pub fn from_file_path(path: &Path) -> Result<Self> {
let extension = path
.extension()
.and_then(|ext| ext.to_str())
.context("Failed to get file extension")?;
let language = SupportedLanguage::from_extension(extension)
.context("Unsupported file type for AST analysis")?;
Ok(Self::new(language))
}
pub fn parse_source(&self, source: &str) -> Result<String> {
info!(
"Would parse source code with {} language",
self.language.get_language_name()
);
Ok(source.to_string())
}
pub fn parse_file(&self, path: &Path) -> Result<(String, String)> {
let source = fs::read_to_string(path)
.with_context(|| format!("Failed to read file: {}", path.display()))?;
info!("Parsed file: {}", path.display());
Ok((source.clone(), source))
}
pub fn find_matches(&self, content: &str, pattern: &str) -> Result<Vec<String>> {
debug!("Searching for pattern: {}", pattern);
let matches: Vec<_> = content
.lines()
.filter(|line| line.contains(pattern))
.map(|line| line.to_string())
.collect();
info!("Found {} matches for pattern: {}", matches.len(), pattern);
Ok(matches)
}
pub fn language(&self) -> SupportedLanguage {
self.language
}
}
pub mod utils {
use super::*;
pub fn is_supported_file(path: &Path) -> bool {
path.extension()
.and_then(|ext| ext.to_str())
.and_then(SupportedLanguage::from_extension)
.is_some()
}
pub fn supported_extensions() -> Vec<&'static str> {
vec![
"rs", "js", "jsx", "ts", "tsx", "py", "go", "java", "c", "h", "cpp", "cc", "cxx", "hpp",
]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_language_detection() {
assert!(matches!(
SupportedLanguage::from_extension("rs"),
Some(SupportedLanguage::Rust)
));
assert!(matches!(
SupportedLanguage::from_extension("js"),
Some(SupportedLanguage::JavaScript)
));
assert!(SupportedLanguage::from_extension("unknown").is_none());
}
#[test]
fn test_analyzer_creation() {
let analyzer = CodeAnalyzer::new(SupportedLanguage::Rust);
assert!(matches!(analyzer.language(), SupportedLanguage::Rust));
}
#[test]
fn test_rust_parsing() -> Result<()> {
let analyzer = CodeAnalyzer::new(SupportedLanguage::Rust);
let source = "fn main() { println!(\"Hello, world!\"); }";
let _ast = analyzer.parse_source(source)?;
assert!(source.contains("fn main"));
Ok(())
}
#[test]
fn test_file_support_detection() {
assert!(utils::is_supported_file(Path::new("test.rs")));
assert!(utils::is_supported_file(Path::new("test.js")));
assert!(!utils::is_supported_file(Path::new("test.txt")));
}
}