use {
crate::{
errors::{ConfError, PatternError},
},
fnv::FnvHashMap,
std::convert::TryFrom,
};
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum SearchObject {
Name,
Path,
Content,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum SearchKind {
Exact,
Fuzzy,
Regex,
Unspecified,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SearchMode {
NameExact,
NameFuzzy,
NameRegex,
PathExact,
PathFuzzy,
PathRegex,
ContentExact,
ContentRegex,
}
pub static SEARCH_MODES: &[SearchMode] = &[
SearchMode::NameFuzzy,
SearchMode::NameRegex,
SearchMode::NameExact,
SearchMode::PathFuzzy,
SearchMode::PathRegex,
SearchMode::PathExact,
SearchMode::ContentExact,
SearchMode::ContentRegex,
];
impl SearchMode {
fn new(search_object: SearchObject, search_kind: SearchKind) -> Option<Self> {
use {
SearchObject::*,
SearchKind::*,
};
match (search_object, search_kind) {
(Name, Unspecified) => Some(Self::NameFuzzy),
(Name, Exact) => Some(Self::NameExact),
(Name, Fuzzy) => Some(Self::NameFuzzy),
(Name, Regex) => Some(Self::NameRegex),
(Path, Unspecified) => Some(Self::PathFuzzy),
(Path, Exact) => Some(Self::PathExact),
(Path, Fuzzy) => Some(Self::PathFuzzy),
(Path, Regex) => Some(Self::PathRegex),
(Content, Unspecified) => Some(Self::ContentExact),
(Content, Exact) => Some(Self::ContentExact),
(Content, Fuzzy) => None, (Content, Regex) => Some(Self::ContentRegex),
}
}
pub fn object(&self) -> SearchObject {
match self {
Self::NameExact | Self::NameFuzzy | Self::NameRegex => SearchObject::Name,
Self::PathExact | Self::PathFuzzy | Self::PathRegex => SearchObject::Path,
Self::ContentExact | Self::ContentRegex => SearchObject::Content,
}
}
pub fn kind(&self) -> SearchKind {
match self {
Self::NameExact => SearchKind::Exact,
Self::NameFuzzy => SearchKind::Fuzzy,
Self::NameRegex => SearchKind::Regex,
Self::PathExact => SearchKind::Exact,
Self::PathFuzzy => SearchKind::Fuzzy,
Self::PathRegex => SearchKind::Regex,
Self::ContentExact => SearchKind::Exact,
Self::ContentRegex => SearchKind::Regex,
}
}
}
#[derive(Debug, Clone)]
pub struct SearchModeMapEntry {
pub key: Option<String>,
pub mode: SearchMode,
}
#[derive(Debug, Clone)]
pub struct SearchModeMap {
pub entries: Vec<SearchModeMapEntry>,
}
impl SearchModeMapEntry {
pub fn parse(conf_key: &str, conf_mode: &str) -> Result<Self, ConfError> {
let s = conf_mode.to_lowercase();
let s = s.trim();
let name = s.contains("name");
let path = s.contains("path");
let content = s.contains("content");
let search_object = match (name, path, content) {
(true, false, false) => SearchObject::Name,
(false, true, false) => SearchObject::Path,
(false, false, true) => SearchObject::Content,
_ => {
return Err(ConfError::InvalidSearchMode {
details: "you must have exactly one of \"name\", \"path\" or \"content".to_string()
});
}
};
let exact = s.contains("exact");
let fuzzy = s.contains("fuzzy");
let regex = s.contains("regex");
let search_kind = match (exact, fuzzy, regex) {
(false, false, false) => SearchKind::Unspecified,
(true, false, false) => SearchKind::Exact,
(false, true, false) => SearchKind::Fuzzy,
(false, false, true) => SearchKind::Regex,
_ => {
return Err(ConfError::InvalidSearchMode {
details: "you may have at most one of \"exact\", \"fuzzy\" or \"regex\"".to_string()
});
}
};
let mode = match SearchMode::new(search_object, search_kind) {
Some(mode) => mode,
None => {
return Err(ConfError::InvalidSearchMode {
details: "Unsupported combination of search object and kind".to_string()
});
},
};
let key = if conf_key.is_empty() || conf_key == "<empty>" {
None
} else if regex!(r"^\w*/$").is_match(conf_key) {
Some(conf_key[0..conf_key.len() - 1].to_string())
} else {
return Err(ConfError::InvalidKey {
raw: conf_key.to_string(),
});
};
Ok(SearchModeMapEntry { key, mode })
}
}
impl Default for SearchModeMap {
fn default() -> Self {
let mut smm = SearchModeMap {
entries: Vec::new(),
};
smm.setm(&["ne", "en", "e"], SearchMode::NameExact);
smm.setm(&["nf", "fn", "n", "f"], SearchMode::NameFuzzy);
smm.setm(&["r", "nr", "rn", ""], SearchMode::NameRegex);
smm.setm(&["pe", "ep"], SearchMode::PathExact);
smm.setm(&["pf", "fp", "p"], SearchMode::PathFuzzy);
smm.setm(&["pr", "rp"], SearchMode::PathRegex);
smm.setm(&["ce", "ec", "c"], SearchMode::ContentExact);
smm.setm(&["rx", "cr"], SearchMode::ContentRegex);
smm.set(SearchModeMapEntry { key: None, mode: SearchMode::NameFuzzy });
smm
}
}
impl TryFrom<&FnvHashMap<String, String>> for SearchModeMap {
type Error = ConfError;
fn try_from(map: &FnvHashMap<String, String>) -> Result<Self, Self::Error> {
let mut entries = Vec::with_capacity(map.len());
for (k, v) in map {
entries.push(SearchModeMapEntry::parse(k, v)?);
}
Ok(Self { entries })
}
}
impl SearchModeMap {
pub fn setm(&mut self, keys: &[&str], mode: SearchMode) {
for key in keys {
self.set(SearchModeMapEntry {
key: Some(key.to_string()),
mode,
});
}
}
pub fn set(&mut self, entry: SearchModeMapEntry) {
self.entries.push(entry);
}
pub fn search_mode(&self, key: Option<&String>) -> Result<SearchMode, PatternError> {
for entry in self.entries.iter().rev() {
if entry.key.as_ref() == key {
return Ok(entry.mode);
}
}
Err(PatternError::InvalidMode {
mode: if let Some(key) = key {
format!("{}/", key)
} else {
"".to_string()
},
})
}
pub fn key(&self, search_mode: SearchMode) -> Option<&String> {
for entry in self.entries.iter().rev() {
if entry.mode == search_mode {
return entry.key.as_ref();
}
}
warn!("search mode key not found for {:?}", search_mode); None
}
}