use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SearchMode {
Plain,
Wildcard,
CaseInsensitive,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ChannelLocation {
pub data_group_index: usize,
pub channel_group_index: usize,
pub channel_index: usize,
}
impl ChannelLocation {
pub fn new(data_group_index: usize, channel_group_index: usize, channel_index: usize) -> Self {
Self {
data_group_index,
channel_group_index,
channel_index,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct ChannelsDB {
db: HashMap<String, Vec<ChannelLocation>>,
total_channels: usize,
}
impl ChannelsDB {
pub fn new() -> Self {
Self::default()
}
pub fn with_capacity(unique_names: usize) -> Self {
Self {
db: HashMap::with_capacity(unique_names),
total_channels: 0,
}
}
pub fn insert(&mut self, name: impl Into<String>, location: ChannelLocation) {
let name = name.into();
self.db.entry(name).or_default().push(location);
self.total_channels += 1;
}
pub fn find_first(&self, name: &str) -> Option<&ChannelLocation> {
self.db.get(name).and_then(|locs| locs.first())
}
pub fn find_all(&self, name: &str) -> &[ChannelLocation] {
self.db.get(name).map(|v| v.as_slice()).unwrap_or(&[])
}
pub fn contains(&self, name: &str) -> bool {
self.db.contains_key(name)
}
pub fn is_empty(&self) -> bool {
self.db.is_empty()
}
pub fn unique_name_count(&self) -> usize {
self.db.len()
}
pub fn total_channel_count(&self) -> usize {
self.total_channels
}
pub fn names(&self) -> impl Iterator<Item = &str> {
let mut names: Vec<&str> = self.db.keys().map(String::as_str).collect();
names.sort_unstable();
names.into_iter()
}
pub fn iter(&self) -> impl Iterator<Item = (&str, &[ChannelLocation])> {
let mut items: Vec<(&str, &[ChannelLocation])> = self
.db
.iter()
.map(|(k, v)| (k.as_str(), v.as_slice()))
.collect();
items.sort_unstable_by_key(|(name, _)| *name);
items.into_iter()
}
pub fn search(&self, pattern: &str, mode: SearchMode) -> Vec<&str> {
let needle = match mode {
SearchMode::CaseInsensitive => pattern.to_lowercase(),
_ => String::new(),
};
let mut matches: Vec<&str> = self
.db
.keys()
.filter(|name| match mode {
SearchMode::Plain => name.contains(pattern),
SearchMode::CaseInsensitive => name.to_lowercase().contains(&needle),
SearchMode::Wildcard => wildcard_match(name, pattern),
})
.map(String::as_str)
.collect();
matches.sort_unstable();
matches.dedup();
matches
}
pub fn clear(&mut self) {
self.db.clear();
self.total_channels = 0;
}
}
pub(crate) fn wildcard_match(text: &str, pattern: &str) -> bool {
let chars: Vec<char> = text.chars().collect();
let pats: Vec<char> = pattern.chars().collect();
fn match_at(chars: &[char], pats: &[char], i: usize, j: usize) -> bool {
if j == pats.len() {
return i == chars.len();
}
match pats[j] {
'*' => {
for k in i..=chars.len() {
if match_at(chars, pats, k, j + 1) {
return true;
}
}
false
}
'?' => i < chars.len() && match_at(chars, pats, i + 1, j + 1),
c => i < chars.len() && chars[i] == c && match_at(chars, pats, i + 1, j + 1),
}
}
match_at(&chars, &pats, 0, 0)
}
#[derive(Debug, Clone, Default)]
pub struct MastersDB {
db: HashMap<(usize, usize), usize>,
}
impl MastersDB {
pub fn new() -> Self {
Self::default()
}
pub fn insert(
&mut self,
data_group_index: usize,
channel_group_index: usize,
channel_index: usize,
) {
self.db
.insert((data_group_index, channel_group_index), channel_index);
}
pub fn find(&self, data_group_index: usize, channel_group_index: usize) -> Option<usize> {
self.db
.get(&(data_group_index, channel_group_index))
.copied()
}
pub fn len(&self) -> usize {
self.db.len()
}
pub fn is_empty(&self) -> bool {
self.db.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_channels_db_basic() {
let mut db = ChannelsDB::new();
db.insert("Speed", ChannelLocation::new(0, 0, 0));
db.insert("RPM", ChannelLocation::new(0, 0, 1));
assert_eq!(db.find_first("Speed"), Some(&ChannelLocation::new(0, 0, 0)));
assert_eq!(db.find_first("RPM"), Some(&ChannelLocation::new(0, 0, 1)));
assert_eq!(db.find_first("Unknown"), None);
}
#[test]
fn test_channels_db_duplicate_names() {
let mut db = ChannelsDB::new();
db.insert("Speed", ChannelLocation::new(0, 0, 0));
db.insert("Speed", ChannelLocation::new(1, 0, 0));
db.insert("Speed", ChannelLocation::new(2, 0, 0));
assert_eq!(db.unique_name_count(), 1);
assert_eq!(db.total_channel_count(), 3);
let all = db.find_all("Speed");
assert_eq!(all.len(), 3);
}
#[test]
fn test_channels_db_iteration() {
let mut db = ChannelsDB::new();
db.insert("A", ChannelLocation::new(0, 0, 0));
db.insert("B", ChannelLocation::new(0, 0, 1));
db.insert("C", ChannelLocation::new(0, 0, 2));
let names: Vec<&str> = db.names().collect();
assert_eq!(names.len(), 3);
}
#[test]
fn test_masters_db() {
let mut db = MastersDB::new();
db.insert(0, 0, 0); db.insert(0, 1, 2); db.insert(1, 0, 0);
assert_eq!(db.find(0, 0), Some(0));
assert_eq!(db.find(0, 1), Some(2));
assert_eq!(db.find(1, 0), Some(0));
assert_eq!(db.find(2, 0), None);
}
}