This commit is contained in:
112
src/utils.py
Normal file
112
src/utils.py
Normal file
@@ -0,0 +1,112 @@
|
||||
"""Utility functions for Code Pattern Search CLI."""
|
||||
|
||||
import re
|
||||
from typing import Optional
|
||||
|
||||
|
||||
def validate_regex_pattern(pattern: str) -> tuple[bool, Optional[str]]:
|
||||
"""Validate a regex pattern.
|
||||
|
||||
Returns:
|
||||
tuple: (is_valid, error_message)
|
||||
"""
|
||||
try:
|
||||
re.compile(pattern)
|
||||
return True, None
|
||||
except re.error as e:
|
||||
return False, str(e)
|
||||
|
||||
|
||||
def sanitize_search_query(query: str) -> str:
|
||||
"""Sanitize a search query for safe usage."""
|
||||
return re.sub(r'[^\w\s\-_]', '', query).strip()
|
||||
|
||||
|
||||
def format_number(num: int) -> str:
|
||||
"""Format a number with appropriate suffixes."""
|
||||
if num >= 1_000_000:
|
||||
return f"{num / 1_000_000:.1f}M"
|
||||
elif num >= 1_000:
|
||||
return f"{num / 1_000:.1f}K"
|
||||
else:
|
||||
return str(num)
|
||||
|
||||
|
||||
def format_duration(seconds: float) -> str:
|
||||
"""Format a duration in human-readable format."""
|
||||
if seconds < 60:
|
||||
return f"{seconds:.1f}s"
|
||||
elif seconds < 3600:
|
||||
return f"{seconds / 60:.1f}m"
|
||||
else:
|
||||
return f"{seconds / 3600:.1f}h"
|
||||
|
||||
|
||||
def get_language_from_extension(extension: str) -> Optional[str]:
|
||||
"""Get programming language from file extension."""
|
||||
extension_map = {
|
||||
".py": "Python",
|
||||
".js": "JavaScript",
|
||||
".ts": "TypeScript",
|
||||
".jsx": "JavaScript React",
|
||||
".tsx": "TypeScript React",
|
||||
".java": "Java",
|
||||
".go": "Go",
|
||||
".rs": "Rust",
|
||||
".cpp": "C++",
|
||||
".c": "C",
|
||||
".cs": "C#",
|
||||
".rb": "Ruby",
|
||||
".php": "PHP",
|
||||
".swift": "Swift",
|
||||
".kt": "Kotlin",
|
||||
".scala": "Scala",
|
||||
".html": "HTML",
|
||||
".css": "CSS",
|
||||
".json": "JSON",
|
||||
".yaml": "YAML",
|
||||
".yml": "YAML",
|
||||
".md": "Markdown",
|
||||
".sh": "Shell",
|
||||
".xml": "XML",
|
||||
".vue": "Vue",
|
||||
".svelte": "Svelte",
|
||||
}
|
||||
return extension_map.get(extension.lower())
|
||||
|
||||
|
||||
def truncate_text(text: str, max_length: int = 100, suffix: str = "...") -> str:
|
||||
"""Truncate text to a maximum length."""
|
||||
if len(text) <= max_length:
|
||||
return text
|
||||
return text[:max_length - len(suffix)] + suffix
|
||||
|
||||
|
||||
def calculate_similarity(str1: str, str2: str) -> float:
|
||||
"""Calculate string similarity using Levenshtein distance."""
|
||||
if str1 == str2:
|
||||
return 1.0
|
||||
|
||||
len1, len2 = len(str1), len(str2)
|
||||
|
||||
if len1 == 0 or len2 == 0:
|
||||
return 0.0
|
||||
|
||||
matrix = [[0] * (len2 + 1) for _ in range(len1 + 1)]
|
||||
|
||||
for i in range(len1 + 1):
|
||||
matrix[i][0] = i
|
||||
for j in range(len2 + 1):
|
||||
matrix[0][j] = j
|
||||
|
||||
for i in range(1, len1 + 1):
|
||||
for j in range(1, len2 + 1):
|
||||
cost = 0 if str1[i - 1] == str2[j - 1] else 1
|
||||
matrix[i][j] = min(
|
||||
matrix[i - 1][j] + 1,
|
||||
matrix[i][j - 1] + 1,
|
||||
matrix[i - 1][j - 1] + cost,
|
||||
)
|
||||
|
||||
max_len = max(len1, len2)
|
||||
return 1.0 - matrix[len1][len2] / max_len
|
||||
Reference in New Issue
Block a user