pub mod mtg; pub mod yugioh; use Link; use std::any::Any; use std::collections::BTreeMap; pub trait Searcher { fn fuzzy_search (&self, name: &str) -> Option { self.exact_search(name) } fn exact_search (&self, name: &str) -> Option; } type SearchFn = Box Option>>; pub struct AggregateSearcher { searchers: BTreeMap } impl AggregateSearcher { pub fn new () -> AggregateSearcher { AggregateSearcher { searchers: BTreeMap::new() } } pub fn add_searcher (&mut self, prefix: &str, searcher: Box>) { let searcher_closure = move |name: String, fuzzy: bool| { (if fuzzy { searcher.fuzzy_search(&name[..]) } else { searcher.exact_search(&name[..]) }).map(|value| Box::new(value) as Box) }; self.searchers.insert(String::from(prefix), Box::new(searcher_closure)); } fn run_all_searchers (&self, name: &str) -> Option> { for (_, searcher) in &self.searchers { let possible_value = searcher(String::from(name), false); if possible_value.is_some() { return possible_value; } } None } } impl Searcher> for AggregateSearcher { fn exact_search (&self, full_name: &str) -> Option> { match split_prefix(full_name, ":") { Some((prefix, name)) => { self.searchers.get(prefix).and_then(|searcher| searcher(String::from(name), true)) .or_else(|| self.run_all_searchers(full_name)) }, None => { self.run_all_searchers(full_name) } } } } pub fn split_prefix<'a> (input: &'a str, separator: &str) -> Option<(&'a str, &'a str)> { if input.contains(separator) { match input.split(separator).into_iter().next() { Some(prefix) => { Some((prefix, input[prefix.len() + separator.len() ..].trim())) }, None => None } } else { None } }