use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
use std::path::{Path, PathBuf};
use walkdir::WalkDir;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DependencyGraph {
pub nodes: HashMap<String, DependencyNode>,
pub edges: Vec<DependencyEdge>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DependencyNode {
pub path: String,
pub module_name: String,
pub imports: Vec<String>,
pub exports: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DependencyEdge {
pub from: String,
pub to: String,
pub edge_type: EdgeType,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum EdgeType {
Import,
Export,
Call,
}
impl DependencyGraph {
pub fn new() -> Self {
Self {
nodes: HashMap::new(),
edges: Vec::new(),
}
}
pub fn add_node(&mut self, node: DependencyNode) {
self.nodes.insert(node.path.clone(), node);
}
pub fn add_edge(&mut self, from: String, to: String, edge_type: EdgeType) {
self.edges.push(DependencyEdge {
from,
to,
edge_type,
});
}
pub fn get_dependencies(&self, path: &str) -> Vec<String> {
self.edges
.iter()
.filter(|e| e.from == path)
.map(|e| e.to.clone())
.collect()
}
pub fn get_dependents(&self, path: &str) -> Vec<String> {
self.edges
.iter()
.filter(|e| e.to == path)
.map(|e| e.from.clone())
.collect()
}
pub fn find_circular_dependencies(&self) -> Vec<Vec<String>> {
let mut cycles = Vec::new();
let mut visited = HashSet::new();
let mut rec_stack = HashSet::new();
for node_path in self.nodes.keys() {
if !visited.contains(node_path) {
self.detect_cycle(
node_path,
&mut visited,
&mut rec_stack,
&mut Vec::new(),
&mut cycles,
);
}
}
cycles
}
fn detect_cycle(
&self,
node: &str,
visited: &mut HashSet<String>,
rec_stack: &mut HashSet<String>,
path: &mut Vec<String>,
cycles: &mut Vec<Vec<String>>,
) {
visited.insert(node.to_string());
rec_stack.insert(node.to_string());
path.push(node.to_string());
for dep in self.get_dependencies(node) {
if !visited.contains(&dep) {
self.detect_cycle(&dep, visited, rec_stack, path, cycles);
} else if rec_stack.contains(&dep) {
if let Some(cycle_start) = path.iter().position(|p| p == &dep) {
let cycle = path[cycle_start..].to_vec();
if !cycles.contains(&cycle) {
cycles.push(cycle);
}
}
}
}
path.pop();
rec_stack.remove(node);
}
pub fn get_dependency_depth(&self, path: &str) -> usize {
let mut max_depth = 0;
let mut visited = HashSet::new();
self.calculate_depth(path, 0, &mut visited, &mut max_depth);
max_depth
}
fn calculate_depth(
&self,
node: &str,
depth: usize,
visited: &mut HashSet<String>,
max_depth: &mut usize,
) {
if visited.contains(node) {
return;
}
visited.insert(node.to_string());
*max_depth = (*max_depth).max(depth);
for dep in self.get_dependencies(node) {
self.calculate_depth(&dep, depth + 1, visited, max_depth);
}
}
pub fn get_leaf_nodes(&self) -> Vec<String> {
self.nodes
.keys()
.filter(|path| self.get_dependencies(path).is_empty())
.cloned()
.collect()
}
pub fn get_root_nodes(&self) -> Vec<String> {
self.nodes
.keys()
.filter(|path| self.get_dependents(path).is_empty())
.cloned()
.collect()
}
pub fn to_svg(&self) -> String {
use crate::svg_export::*;
let mut engine = LayoutEngine::new();
for (path, node) in &self.nodes {
engine.nodes.push(SvgNode {
id: path.clone(),
label: node.module_name.clone(),
x: 0.0,
y: 0.0,
width: NODE_WIDTH,
height: NODE_HEIGHT,
fill: "#1e3a5f".to_string(),
stroke: "#3b82f6".to_string(),
text_color: "#e2e8f0".to_string(),
detail: Some(format!(
"path: {}\nimports: {}, exports: {}",
node.path,
node.imports.len(),
node.exports.len()
)),
});
}
for (i, edge) in self.edges.iter().enumerate() {
let color = match edge.edge_type {
EdgeType::Import => "#60a5fa",
EdgeType::Export => "#34d399",
EdgeType::Call => "#f87171",
};
engine.edges.push(SvgEdge {
id: format!("e-{}", i),
source: edge.from.clone(),
target: edge.to.clone(),
label: Some(format!("{:?}", edge.edge_type).to_lowercase()),
color: color.to_string(),
});
}
engine.layout();
engine.to_svg()
}
pub fn to_dot(&self) -> String {
let mut dot = String::from("digraph Dependencies {\n");
dot.push_str(" rankdir=LR;\n");
dot.push_str(" node [shape=box];\n\n");
for (path, node) in &self.nodes {
let label = node.module_name.clone();
dot.push_str(&format!(" \"{}\" [label=\"{}\"];\n", path, label));
}
dot.push('\n');
for edge in &self.edges {
let color = match edge.edge_type {
EdgeType::Import => "blue",
EdgeType::Export => "green",
EdgeType::Call => "red",
};
dot.push_str(&format!(
" \"{}\" -> \"{}\" [color={}];\n",
edge.from, edge.to, color
));
}
dot.push_str("}\n");
dot
}
}
pub fn build_dependency_graph(
path: &Path,
extensions: Option<&[String]>,
exclude: Option<&[String]>,
) -> Result<DependencyGraph, Box<dyn std::error::Error>> {
let mut graph = DependencyGraph::new();
let walker = WalkDir::new(path)
.into_iter()
.filter_entry(|e| {
if let Some(name) = e.file_name().to_str() {
if let Some(exclude_dirs) = exclude {
for exclude_dir in exclude_dirs {
if name == exclude_dir {
return false;
}
}
}
}
true
})
.filter_map(|e| e.ok())
.filter(|e| e.file_type().is_file());
let files: Vec<PathBuf> = walker
.filter(|entry| {
let file_path = entry.path();
if let Some(exts) = extensions {
if let Some(ext) = file_path.extension().and_then(|s| s.to_str()) {
exts.iter().any(|e| e == ext)
} else {
false
}
} else {
true
}
})
.map(|e| e.path().to_path_buf())
.collect();
for file in &files {
if let Ok(node) = extract_dependencies(file) {
graph.add_node(node);
}
}
for file in &files {
let file_str = file.to_string_lossy().to_string();
let imports: Vec<String> = if let Some(node) = graph.nodes.get(&file_str) {
node.imports.clone()
} else {
Vec::new()
};
for import in imports {
if let Some(target_path) = resolve_import(&import, file, &files) {
graph.add_edge(file_str.clone(), target_path, EdgeType::Import);
}
}
}
Ok(graph)
}
fn extract_dependencies(path: &Path) -> Result<DependencyNode, Box<dyn std::error::Error>> {
let content = std::fs::read_to_string(path)?;
let ext = path.extension().and_then(|s| s.to_str()).unwrap_or("");
let imports = extract_imports_from_content(&content, ext);
let exports = extract_exports_from_content(&content, ext);
let module_name = path
.file_stem()
.and_then(|s| s.to_str())
.unwrap_or("unknown")
.to_string();
Ok(DependencyNode {
path: path.to_string_lossy().to_string(),
module_name,
imports,
exports,
})
}
fn extract_imports_from_content(content: &str, ext: &str) -> Vec<String> {
let mut imports = Vec::new();
let patterns: Vec<&str> = match ext {
"rs" => vec![
r"use\s+([\w:]+)",
r"extern\s+crate\s+([\w]+)",
],
"py" | "pyw" | "pyi" => vec![
r"import\s+([\w.]+)",
r"from\s+([\w.]+)\s+import",
],
"js" | "jsx" | "ts" | "tsx" | "mjs" | "cjs" => vec['"]"##,
r##"require\(['"]([^'"]+)['"]\)"##,
r##"import\s*\(\s*['"]([^'"]+)['"]\s*\)"##,
r##"import\s+['"]([^'"]+)['"]"##,
],
"go" => vec![
r##"import\s+"([^"]+)""##,
r##"\s*"([^"]+)""##,
],
"c" | "cpp" | "cxx" | "cc" | "h" | "hpp" => vec[">]"##,
],
"java" => vec![
r"import\s+([\w.]+);",
],
"kt" => vec![
r"import\s+([\w.]+);?",
],
"cs" => vec![
r"using\s+([\w.]+);",
r"using\s+static\s+([\w.]+);",
],
"rb" | "rake" => vec['"]"##,
r##"require_relative\s+['"]([^'"]+)['"]"##,
r##"load\s+['"]([^'"]+)['"]"##,
],
"php" => vec![
r"use\s+([\w\\]+);",
r##"include\s+['"]?([^;'"]+)['"]?;"##,
r##"require\s+['"]?([^;'"]+)['"]?;"##,
],
"swift" => vec![
r"import\s+([\w.]+)",
],
"lua" => vec['"]\s*\)?"##,
],
"sh" | "bash" | "zsh" => vec![
r##"source\s+['"]?([^'"\s]+)['"]?"##,
r##"\.\s+['"]?([^'"\s]+)['"]?"##,
],
"ex" | "exs" => vec![
r"alias\s+([\w.]+)",
r"import\s+([\w.]+)",
r"require\s+([\w.]+)",
r"use\s+([\w.]+)",
],
"pl" | "pm" => vec![
r"use\s+([\w:]+)",
r##"require\s+['"]([^'"]+)['"]"##,
],
"r" | "R" => vec![
r##"library\s*\(\s*['"]?([^'")]+)['"]?\s*\)"##,
r##"require\s*\(\s*['"]?([^'")]+)['"]?\s*\)"##,
r##"source\s*\(\s*['"]?([^'")]+)['"]?\s*\)"##,
],
"m" => vec![
r"import\s+([\w.]+)",
],
"scala" | "sc" => vec![
r"import\s+([\w.]+)",
],
"groovy" | "gvy" => vec![
r"import\s+([\w.]+)",
],
"hs" => vec![
r"import\s+(?:qualified\s+)?([\w.]+)",
],
"clj" | "cljs" => vec![
r"\(:require\s+\[?([\w.-]+)",
r"\(:use\s+\[?([\w.-]+)",
r"\(:import\s+\[?([\w.-]+)",
],
"dart" => vec['"]"##,
],
"jl" => vec![
r"using\s+([\w.]+)",
r"import\s+([\w.]+)",
],
"nim" => vec![
r"import\s+([\w.]+)",
],
"zig" => vec['"]\s*\)"##,
],
"odin" => vec['"]"##,
],
"v" | "vv" => vec![
r"import\s+([\w.]+)",
],
_ => vec![
r##"import\s+['"]?([^'";\n]+)['"]?"##,
r##"include\s+['"]?([^'";\n]+)['"]?"##,
r"use\s+([\w.]+)",
],
};
for pattern in patterns {
if let Ok(re) = regex::Regex::new(pattern) {
for line in content.lines() {
if let Some(caps) = re.captures(line) {
if let Some(import) = caps.get(1) {
let trimmed = import.as_str().trim();
if !trimmed.is_empty() {
imports.push(trimmed.to_string());
}
}
}
}
}
}
imports
}
fn extract_exports_from_content(content: &str, ext: &str) -> Vec<String> {
let mut exports = Vec::new();
let patterns: Vec<&str> = match ext {
"rs" => vec![
r"pub\s+fn\s+(\w+)",
r"pub\s+struct\s+(\w+)",
r"pub\s+enum\s+(\w+)",
r"pub\s+trait\s+(\w+)",
r"pub\s+type\s+(\w+)",
r"pub\s+mod\s+(\w+)",
r"pub\s+const\s+(\w+)",
r"pub\s+static\s+(\w+)",
],
"py" | "pyw" | "pyi" => vec![
r"def\s+(\w+)",
r"class\s+(\w+)",
r##"__all__\s*=\s*\[[^\]]*['"](\w+)['"]"##,
],
"js" | "jsx" | "ts" | "tsx" | "mjs" | "cjs" => vec![
r"export\s+(?:default\s+)?(?:function|class|const|let|var|async\s+function)\s+(\w+)",
r"export\s+\{\s*(\w+)",
r"module\.exports\.(\w+)",
r##"exports\.(\w+)\s*="##,
],
"go" => vec![
r"func\s+(\w+)",
r"func\s+\([^)]*\)\s*(\w+)",
r"type\s+(\w+)",
r"var\s+(\w+)",
r"const\s+(\w+)",
],
"java" => vec![
r"(?:public|protected)\s+(?:static\s+)?(?:\w+\s+)*(\w+)\s*\(",
r"public\s+class\s+(\w+)",
r"public\s+interface\s+(\w+)",
],
"kt" => vec![
r"fun\s+(\w+)",
r"class\s+(\w+)",
r"interface\s+(\w+)",
],
"cs" => vec![
r"(?:public|protected|internal)\s+(?:static\s+)?(?:\w+\s+)*(\w+)\s*\(",
r"public\s+class\s+(\w+)",
r"public\s+interface\s+(\w+)",
],
"c" | "cpp" | "cxx" | "cc" | "h" | "hpp" => vec![
r"(?:\w+\s+)+?(\w+)\s*\([^)]*\)\s*\{",
],
"rb" | "rake" | "cr" => vec![
r"def\s+(?:self\.)?(\w+)",
r"class\s+(\w+)",
r"module\s+(\w+)",
],
"php" => vec![
r"function\s+(\w+)",
r"class\s+(\w+)",
],
"swift" => vec![
r"func\s+(\w+)",
r"class\s+(\w+)",
r"struct\s+(\w+)",
],
"lua" => vec![
r"function\s+(?:\w+[:\.])?(\w+)",
r"local\s+function\s+(\w+)",
],
"ex" | "exs" => vec![
r"def\s+(?:\w+\.)?(\w+)",
r"defmodule\s+(\w+)",
],
"scala" | "sc" => vec![
r"def\s+(\w+)",
r"class\s+(\w+)",
],
"groovy" | "gvy" => vec![
r"def\s+(\w+)",
r"class\s+(\w+)",
],
"dart" => vec![
r"\b(\w+)\s*\([^)]*\)\s*(?:async\s*)?\{",
],
"zig" => vec![
r"fn\s+(\w+)",
],
"odin" => vec![
r"(\w+)\s*::\s*proc",
],
"v" | "vv" => vec![
r"fn\s+(\w+)",
],
_ => vec![
r"(?:fn|def|function|func)\s+(\w+)",
r"class\s+(\w+)",
],
};
for pattern in patterns {
if let Ok(re) = regex::Regex::new(pattern) {
for line in content.lines() {
if let Some(caps) = re.captures(line) {
if let Some(export) = caps.get(1) {
let name = export.as_str();
if !is_likely_keyword_dep(name) {
exports.push(name.to_string());
}
}
}
}
}
}
exports
}
fn is_likely_keyword_dep(name: &str) -> bool {
let keywords: &[&str] = &[
"if", "else", "while", "for", "switch", "case", "return", "break", "continue",
"try", "catch", "finally", "with", "new", "delete", "typeof", "instanceof",
"void", "null", "true", "false", "this", "super", "class", "interface",
"struct", "enum", "union", "typedef", "namespace", "module", "package",
"import", "export", "from", "as", "in", "of", "await", "yield", "throw",
"public", "private", "protected", "internal", "static", "const", "final",
"abstract", "virtual", "override", "extends", "implements", "throws",
];
keywords.contains(&name)
}
fn resolve_import(import: &str, _current_file: &Path, all_files: &[PathBuf]) -> Option<String> {
for file in all_files {
if let Some(stem) = file.file_stem().and_then(|s| s.to_str()) {
if import.contains(stem) {
return Some(file.to_string_lossy().to_string());
}
}
}
None
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dependency_graph_creation() {
let mut graph = DependencyGraph::new();
let node = DependencyNode {
path: "test.rs".to_string(),
module_name: "test".to_string(),
imports: vec!["std::io".to_string()],
exports: vec!["main".to_string()],
};
graph.add_node(node);
assert_eq!(graph.nodes.len(), 1);
}
#[test]
fn test_add_edge() {
let mut graph = DependencyGraph::new();
graph.add_edge("a.rs".to_string(), "b.rs".to_string(), EdgeType::Import);
assert_eq!(graph.edges.len(), 1);
assert_eq!(graph.edges[0].from, "a.rs");
assert_eq!(graph.edges[0].to, "b.rs");
}
#[test]
fn test_get_dependencies() {
let mut graph = DependencyGraph::new();
graph.add_edge("a.rs".to_string(), "b.rs".to_string(), EdgeType::Import);
graph.add_edge("a.rs".to_string(), "c.rs".to_string(), EdgeType::Import);
let deps = graph.get_dependencies("a.rs");
assert_eq!(deps.len(), 2);
}
#[test]
fn test_circular_dependency_detection() {
let mut graph = DependencyGraph::new();
graph.add_node(DependencyNode {
path: "a.rs".to_string(),
module_name: "a".to_string(),
imports: vec![],
exports: vec![],
});
graph.add_node(DependencyNode {
path: "b.rs".to_string(),
module_name: "b".to_string(),
imports: vec![],
exports: vec![],
});
graph.add_edge("a.rs".to_string(), "b.rs".to_string(), EdgeType::Import);
graph.add_edge("b.rs".to_string(), "a.rs".to_string(), EdgeType::Import);
let cycles = graph.find_circular_dependencies();
assert!(!cycles.is_empty());
}
#[test]
fn test_extract_rust_imports() {
let code = r#"
use std::io::Read;
use crate::utils::helper;
extern crate serde;
"#;
let imports = extract_imports_from_content(code, "rs");
assert!(imports.iter().any(|i| i.contains("std::io")));
assert!(imports.iter().any(|i| i.contains("crate::utils")));
assert!(imports.iter().any(|i| i == "serde"));
}
#[test]
fn test_extract_python_imports() {
let code = r#"
import os
import json
from collections import OrderedDict
from mypackage.utils import helper
"#;
let imports = extract_imports_from_content(code, "py");
assert!(imports.contains(&"os".to_string()));
assert!(imports.contains(&"json".to_string()));
assert!(imports.contains(&"collections".to_string()));
assert!(imports.contains(&"mypackage.utils".to_string()));
}
#[test]
fn test_extract_javascript_imports() {
let code = r#"
import React from 'react';
import { useState } from 'react';
import './styles.css';
const fs = require('fs');
"#;
let imports = extract_imports_from_content(code, "js");
assert!(imports.iter().any(|i| i.contains("react")));
assert!(imports.iter().any(|i| i.contains("styles.css")));
assert!(imports.iter().any(|i| i.contains("fs")));
}
#[test]
fn test_extract_go_imports() {
let code = r#"
import "fmt"
import "os"
import (
"net/http"
"strings"
)
"#;
let imports = extract_imports_from_content(code, "go");
assert!(imports.iter().any(|i| i.contains("fmt")));
assert!(imports.iter().any(|i| i.contains("net/http")));
}
#[test]
fn test_extract_c_includes() {
let code = r#"
#include <stdio.h>
#include <stdlib.h>
#include "myheader.h"
"#;
let imports = extract_imports_from_content(code, "c");
assert!(imports.contains(&"stdio.h".to_string()));
assert!(imports.contains(&"stdlib.h".to_string()));
assert!(imports.contains(&"myheader.h".to_string()));
}
#[test]
fn test_extract_java_imports() {
let code = r#"
import java.util.List;
import java.io.File;
"#;
let imports = extract_imports_from_content(code, "java");
assert!(imports.contains(&"java.util.List".to_string()));
assert!(imports.contains(&"java.io.File".to_string()));
}
#[test]
fn test_extract_cs_using() {
let code = r#"
using System;
using System.Collections.Generic;
using static System.Math;
"#;
let imports = extract_imports_from_content(code, "cs");
assert!(imports.contains(&"System".to_string()));
assert!(imports.contains(&"System.Collections.Generic".to_string()));
assert!(imports.contains(&"System.Math".to_string()));
}
#[test]
fn test_extract_ruby_requires() {
let code = r#"
require 'json'
require_relative './helper'
load 'config.rb'
"#;
let imports = extract_imports_from_content(code, "rb");
assert!(imports.contains(&"json".to_string()));
assert!(imports.contains(&"./helper".to_string()));
assert!(imports.contains(&"config.rb".to_string()));
}
#[test]
fn test_extract_php_imports() {
let code = r#"
use App\\Models\\User;
include 'header.php';
require 'vendor/autoload.php';
"#;
let imports = extract_imports_from_content(code, "php");
assert!(imports.iter().any(|i| i.contains("App")));
assert!(imports.iter().any(|i| i.contains("header.php")));
assert!(imports.iter().any(|i| i.contains("vendor")));
}
#[test]
fn test_extract_swift_imports() {
let code = r#"
import Foundation
import UIKit
"#;
let imports = extract_imports_from_content(code, "swift");
assert!(imports.contains(&"Foundation".to_string()));
assert!(imports.contains(&"UIKit".to_string()));
}
#[test]
fn test_extract_lua_requires() {
let code = r#"
local json = require('cjson')
local utils = require("utils")
"#;
let imports = extract_imports_from_content(code, "lua");
assert!(imports.contains(&"cjson".to_string()));
assert!(imports.contains(&"utils".to_string()));
}
#[test]
fn test_extract_shell_sources() {
let code = r#"
source ./common.sh
. ./utils.sh
"#;
let imports = extract_imports_from_content(code, "sh");
assert!(imports.contains(&"./common.sh".to_string()));
assert!(imports.contains(&"./utils.sh".to_string()));
}
#[test]
fn test_extract_elixir_imports() {
let code = r#"
alias MyApp.User
import Enum
require Logger
use GenServer
"#;
let imports = extract_imports_from_content(code, "ex");
assert!(imports.contains(&"MyApp.User".to_string()));
assert!(imports.contains(&"Enum".to_string()));
assert!(imports.contains(&"Logger".to_string()));
assert!(imports.contains(&"GenServer".to_string()));
}
#[test]
fn test_extract_rust_exports() {
let code = r#"
pub fn calculate() {}
pub struct Point {}
pub enum Status {}
pub trait Drawable {}
"#;
let exports = extract_exports_from_content(code, "rs");
assert!(exports.contains(&"calculate".to_string()));
assert!(exports.contains(&"Point".to_string()));
assert!(exports.contains(&"Status".to_string()));
assert!(exports.contains(&"Drawable".to_string()));
}
#[test]
fn test_extract_python_exports() {
let code = r#"
def helper():
pass
class Calculator:
pass
"#;
let exports = extract_exports_from_content(code, "py");
assert!(exports.contains(&"helper".to_string()));
assert!(exports.contains(&"Calculator".to_string()));
}
#[test]
fn test_extract_javascript_exports() {
let code = r#"
export function greet() {}
export class Person {}
export const PI = 3.14;
module.exports.helper = function() {}
exports.util = () => {}
"#;
let exports = extract_exports_from_content(code, "js");
assert!(exports.contains(&"greet".to_string()));
assert!(exports.contains(&"Person".to_string()));
assert!(exports.contains(&"helper".to_string()));
assert!(exports.contains(&"util".to_string()));
}
#[test]
fn test_extract_go_exports() {
let code = r#"
func Add(a, b int) int { return a + b }
func (r *Rect) Area() int { return 0 }
type Point struct{}
"#;
let exports = extract_exports_from_content(code, "go");
assert!(exports.contains(&"Add".to_string()));
assert!(exports.contains(&"Area".to_string()));
assert!(exports.contains(&"Point".to_string()));
}
#[test]
fn test_extract_java_exports() {
let code = r#"
public class Calculator {
public int add(int a, int b) { return a + b; }
}
"#;
let exports = extract_exports_from_content(code, "java");
assert!(exports.contains(&"Calculator".to_string()));
assert!(exports.contains(&"add".to_string()));
}
#[test]
fn test_extract_cpp_exports() {
let code = r#"
class Calculator {
public:
int multiply(int a, int b) { return a * b; }
};
void helper() {}
"#;
let exports = extract_exports_from_content(code, "cpp");
assert!(exports.contains(&"multiply".to_string()));
assert!(exports.contains(&"helper".to_string()));
}
#[test]
fn test_extract_zig_imports() {
let code = r#"
const std = @import("std");
const math = @import("math.zig");
"#;
let imports = extract_imports_from_content(code, "zig");
assert!(imports.contains(&"std".to_string()));
assert!(imports.contains(&"math.zig".to_string()));
}
#[test]
fn test_extract_dart_imports() {
let code = r#"
import 'package:flutter/material.dart';
import 'utils.dart';
"#;
let imports = extract_imports_from_content(code, "dart");
assert!(imports.iter().any(|i| i.contains("flutter")));
assert!(imports.contains(&"utils.dart".to_string()));
}
#[test]
fn test_extract_julia_imports() {
let code = r#"
using DataFrames
import JSON
"#;
let imports = extract_imports_from_content(code, "jl");
assert!(imports.contains(&"DataFrames".to_string()));
assert!(imports.contains(&"JSON".to_string()));
}
#[test]
fn test_extract_kotlin_imports() {
let code = r#"
import java.util.List
import kotlin.collections.Map
"#;
let imports = extract_imports_from_content(code, "kt");
assert!(imports.contains(&"java.util.List".to_string()));
assert!(imports.contains(&"kotlin.collections.Map".to_string()));
}
#[test]
fn test_extract_typescript_imports() {
let code = r#"
import { useState } from 'react';
import type { Config } from './types';
import * as utils from './utils';
"#;
let imports = extract_imports_from_content(code, "ts");
assert!(imports.iter().any(|i| i.contains("react")));
assert!(imports.iter().any(|i| i.contains("types")));
assert!(imports.iter().any(|i| i.contains("utils")));
}
#[test]
fn test_extract_scala_imports() {
let code = r#"
import scala.collection.mutable.ListBuffer
import java.io.File
"#;
let imports = extract_imports_from_content(code, "scala");
assert!(imports.contains(&"scala.collection.mutable.ListBuffer".to_string()));
assert!(imports.contains(&"java.io.File".to_string()));
}
#[test]
fn test_extract_haskell_imports() {
let code = r#"
import Data.List
import qualified Data.Map as Map
"#;
let imports = extract_imports_from_content(code, "hs");
assert!(imports.contains(&"Data.List".to_string()));
assert!(imports.contains(&"Data.Map".to_string()));
}
#[test]
fn test_extract_clojure_requires() {
let code = r#"
(:require [clojure.string :as str])
(:use [clojure.test :only [deftest is]])
"#;
let imports = extract_imports_from_content(code, "clj");
assert!(imports.iter().any(|i| i.contains("clojure.string")));
assert!(imports.iter().any(|i| i.contains("clojure.test")));
}
#[test]
fn test_extract_groovy_imports() {
let code = r#"
import groovy.sql.Sql
import java.util.Date
"#;
let imports = extract_imports_from_content(code, "groovy");
assert!(imports.contains(&"groovy.sql.Sql".to_string()));
assert!(imports.contains(&"java.util.Date".to_string()));
}
#[test]
fn test_extract_perl_imports() {
let code = r#"
use strict;
use warnings;
use JSON qw(encode_json);
"#;
let imports = extract_imports_from_content(code, "pl");
assert!(imports.contains(&"strict".to_string()));
assert!(imports.contains(&"warnings".to_string()));
assert!(imports.contains(&"JSON".to_string()));
}
#[test]
fn test_extract_r_imports() {
let code = r#"
library(dplyr)
require(ggplot2)
"#;
let imports = extract_imports_from_content(code, "r");
assert!(imports.contains(&"dplyr".to_string()));
assert!(imports.contains(&"ggplot2".to_string()));
}
#[test]
fn test_extract_kotlin_exports() {
let code = r#"
fun add(a: Int, b: Int): Int = a + b
class Calculator {}
"#;
let exports = extract_exports_from_content(code, "kt");
assert!(exports.contains(&"add".to_string()));
assert!(exports.contains(&"Calculator".to_string()));
}
#[test]
fn test_extract_swift_exports() {
let code = r#"
public func greet() {}
open class Vehicle {}
"#;
let exports = extract_exports_from_content(code, "swift");
assert!(exports.contains(&"greet".to_string()));
assert!(exports.contains(&"Vehicle".to_string()));
}
#[test]
fn test_extract_php_exports() {
let code = r#"
function helper() {}
class User {}
"#;
let exports = extract_exports_from_content(code, "php");
assert!(exports.contains(&"helper".to_string()));
assert!(exports.contains(&"User".to_string()));
}
#[test]
fn test_extract_ruby_exports() {
let code = r#"
def process_data
end
class Worker
end
"#;
let exports = extract_exports_from_content(code, "rb");
assert!(exports.contains(&"process_data".to_string()));
assert!(exports.contains(&"Worker".to_string()));
}
}