use crate::drawings::DrawingToolType;
use crate::drawings::categories;
pub trait DrawingToolsRepository {
fn get_all_categories(&self) -> Vec<String>;
fn get_category_tools(&self, category: &str) -> Vec<(String, Vec<DrawingToolType>)>;
fn get_tool_category(&self, tool: DrawingToolType) -> Option<String>;
fn search_tools(&self, query: &str) -> Vec<DrawingToolType>;
}
pub struct InMemoryToolsRepository;
impl InMemoryToolsRepository {
pub fn new() -> Self {
Self
}
}
impl Default for InMemoryToolsRepository {
fn default() -> Self {
Self::new()
}
}
impl DrawingToolsRepository for InMemoryToolsRepository {
fn get_all_categories(&self) -> Vec<String> {
vec![
"Lines".to_string(),
"Fibonacci".to_string(),
"Patterns".to_string(),
"Projection".to_string(),
"Brushes/Shapes".to_string(),
"Text/Annotations".to_string(),
"Icons/Emojis".to_string(),
]
}
fn get_category_tools(&self, category: &str) -> Vec<(String, Vec<DrawingToolType>)> {
categories::get_category_sections(category)
.into_iter()
.map(|(section, tools)| (section.to_string(), tools))
.collect()
}
fn get_tool_category(&self, tool: DrawingToolType) -> Option<String> {
categories::get_tool_category(tool).map(|s| s.to_string())
}
fn search_tools(&self, query: &str) -> Vec<DrawingToolType> {
if query.is_empty() {
return self.get_all_tools();
}
let query_lower = query.to_lowercase();
self.get_all_tools()
.into_iter()
.filter(|tool| tool.as_str().to_lowercase().contains(&query_lower))
.collect()
}
}
impl InMemoryToolsRepository {
fn get_all_tools(&self) -> Vec<DrawingToolType> {
let mut all_tools = Vec::new();
for category in self.get_all_categories() {
for (_, tools) in self.get_category_tools(&category) {
all_tools.extend(tools);
}
}
all_tools
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_all_categories() {
let repo = InMemoryToolsRepository::new();
let categories = repo.get_all_categories();
assert!(!categories.is_empty(), "Should have categories");
assert!(categories.contains(&"Lines".to_string()));
assert!(categories.contains(&"Fibonacci".to_string()));
assert!(
!categories.contains(&"Favorites".to_string()),
"Should not include dynamic categories"
);
}
#[test]
fn test_get_category_tools() {
let repo = InMemoryToolsRepository::new();
let tools = repo.get_category_tools("Lines");
assert!(!tools.is_empty(), "Lines category should have tools");
let all_tools: Vec<DrawingToolType> = tools
.iter()
.flat_map(|(_, section_tools)| section_tools.clone())
.collect();
assert!(
all_tools.contains(&DrawingToolType::TrendLine),
"Should contain TrendLine"
);
}
#[test]
fn test_get_tool_category() {
let repo = InMemoryToolsRepository::new();
assert_eq!(
repo.get_tool_category(DrawingToolType::TrendLine),
Some("Lines".to_string())
);
assert_eq!(
repo.get_tool_category(DrawingToolType::FibonacciRetracement),
Some("Fibonacci".to_string())
);
}
#[test]
fn test_search_tools() {
let repo = InMemoryToolsRepository::new();
let results = repo.search_tools("fib");
assert!(!results.is_empty(), "Should find Fib tools");
assert!(results.contains(&DrawingToolType::FibonacciRetracement));
}
#[test]
fn test_search_case_insensitive() {
let repo = InMemoryToolsRepository::new();
let results1 = repo.search_tools("FIB");
let results2 = repo.search_tools("fib");
assert_eq!(
results1.len(),
results2.len(),
"Search should be case-insensitive"
);
}
#[test]
fn test_search_empty_returns_all() {
let repo = InMemoryToolsRepository::new();
let all = repo.get_all_tools();
let search_results = repo.search_tools("");
assert_eq!(
all.len(),
search_results.len(),
"Empty search should return all tools"
);
}
}