fix: able to insert duplicate keys into collection

This replaces the custom `Collection` implementation with `IndexMap` from the crate of the same name.

Fixes #28.
This commit is contained in:
Jake Stanger 2022-11-05 17:32:01 +00:00
parent 5ebc84c7b9
commit 3a83bd31ab
No known key found for this signature in database
GPG Key ID: C51FC8F9CB0BEA61
8 changed files with 24 additions and 185 deletions

1
Cargo.lock generated
View File

@ -1146,6 +1146,7 @@ dependencies = [
"glib",
"gtk",
"gtk-layer-shell",
"indexmap",
"lazy_static",
"libcorn",
"mpd_client",

View File

@ -17,8 +17,6 @@ tracing-error = "0.2.0"
tracing-appender = "0.2.2"
strip-ansi-escapes = "0.1.1"
color-eyre = "0.6.2"
futures-util = "0.3.21"
chrono = "0.4.19"
serde = { version = "1.0.141", features = ["derive"] }
serde_json = "1.0.82"
serde_yaml = "0.9.4"
@ -26,6 +24,9 @@ toml = "0.5.9"
libcorn = "0.4.0"
lazy_static = "1.4.0"
async_once = "0.2.6"
indexmap = "1.9.1"
futures-util = "0.3.21"
chrono = "0.4.19"
regex = { version = "1.6.0", default-features = false, features = ["std"] }
stray = { version = "0.1.2" }
dirs = "4.0.0"

View File

@ -1,161 +0,0 @@
use serde::Serialize;
use std::slice::{Iter, IterMut};
use std::vec;
/// An ordered map.
/// Internally this is just two vectors -
/// one for keys and one for values.
#[derive(Debug, Clone, Serialize)]
pub struct Collection<TKey, TData> {
keys: Vec<TKey>,
values: Vec<TData>,
}
impl<TKey: PartialEq, TData> Collection<TKey, TData> {
/// Creates a new empty collection.
pub const fn new() -> Self {
Self {
keys: vec![],
values: vec![],
}
}
/// Inserts a new key/value pair at the end of the collection.
pub fn insert(&mut self, key: TKey, value: TData) {
self.keys.push(key);
self.values.push(value);
assert_eq!(self.keys.len(), self.values.len());
}
/// Gets a reference of the value for the specified key
/// if it exists in the collection.
pub fn get(&self, key: &TKey) -> Option<&TData> {
let index = self.keys.iter().position(|k| k == key);
match index {
Some(index) => self.values.get(index),
None => None,
}
}
/// Gets a mutable reference for the value with the specified key
/// if it exists in the collection.
pub fn get_mut(&mut self, key: &TKey) -> Option<&mut TData> {
let index = self.keys.iter().position(|k| k == key);
match index {
Some(index) => self.values.get_mut(index),
None => None,
}
}
/// Checks if a value for the given key exists inside the collection
pub fn contains(&self, key: &TKey) -> bool {
self.keys.contains(key)
}
/// Removes the key/value from the collection
/// if it exists
/// and returns the removed value.
pub fn remove(&mut self, key: &TKey) -> Option<TData> {
assert_eq!(self.keys.len(), self.values.len());
let index = self.keys.iter().position(|k| k == key);
if let Some(index) = index {
self.keys.remove(index);
Some(self.values.remove(index))
} else {
None
}
}
/// Gets the length of the collection.
pub fn len(&self) -> usize {
self.keys.len()
}
/// Gets a reference to the first value in the collection.
pub fn first(&self) -> Option<&TData> {
self.values.first()
}
/// Gets the values as a slice.
pub fn as_slice(&self) -> &[TData] {
self.values.as_slice()
}
/// Checks whether the collection is empty.
pub fn is_empty(&self) -> bool {
self.keys.is_empty()
}
/// Gets an iterator for the collection.
pub fn iter(&self) -> Iter<'_, TData> {
self.values.iter()
}
/// Gets a mutable iterator for the collection
pub fn iter_mut(&mut self) -> IterMut<'_, TData> {
self.values.iter_mut()
}
}
impl<TKey: PartialEq, TData> From<(TKey, TData)> for Collection<TKey, TData> {
fn from((key, value): (TKey, TData)) -> Self {
let mut collection = Self::new();
collection.insert(key, value);
collection
}
}
impl<TKey: PartialEq, TData> FromIterator<(TKey, TData)> for Collection<TKey, TData> {
fn from_iter<T: IntoIterator<Item = (TKey, TData)>>(iter: T) -> Self {
let mut collection = Self::new();
for (key, value) in iter {
collection.insert(key, value);
}
collection
}
}
impl<'a, TKey: PartialEq, TData> IntoIterator for &'a Collection<TKey, TData> {
type Item = &'a TData;
type IntoIter = CollectionIntoIterator<'a, TKey, TData>;
fn into_iter(self) -> Self::IntoIter {
CollectionIntoIterator {
collection: self,
index: 0,
}
}
}
pub struct CollectionIntoIterator<'a, TKey, TData> {
collection: &'a Collection<TKey, TData>,
index: usize,
}
impl<'a, TKey: PartialEq, TData> Iterator for CollectionIntoIterator<'a, TKey, TData> {
type Item = &'a TData;
fn next(&mut self) -> Option<Self::Item> {
let res = self.collection.values.get(self.index);
self.index += 1;
res
}
}
impl<TKey: PartialEq, TData> Default for Collection<TKey, TData> {
fn default() -> Self {
Self::new()
}
}
impl<TKey: PartialEq, TData> IntoIterator for Collection<TKey, TData> {
type Item = TData;
type IntoIter = vec::IntoIter<TData>;
fn into_iter(self) -> Self::IntoIter {
self.values.into_iter()
}
}

View File

@ -1,6 +1,5 @@
mod bar;
mod bridge_channel;
mod collection;
mod config;
mod icon;
mod logging;

View File

@ -47,10 +47,10 @@ impl Module<gtk::Box> for FocusedModule {
.expect("Failed to get read lock on toplevels")
.clone();
toplevels.into_iter().find(|(top, _)| top.active)
toplevels.into_iter().find(|(_, (top, _))| top.active)
});
if let Some((top, _)) = focused {
if let Some((_, (top, _))) = focused {
tx.try_send(ModuleUpdateEvent::Update((top.title.clone(), top.app_id)))?;
}

View File

@ -1,5 +1,4 @@
use super::open_state::OpenState;
use crate::collection::Collection;
use crate::icon::get_icon;
use crate::modules::launcher::{ItemEvent, LauncherUpdate};
use crate::modules::ModuleUpdateEvent;
@ -7,6 +6,7 @@ use crate::popup::Popup;
use crate::wayland::ToplevelInfo;
use gtk::prelude::*;
use gtk::{Button, IconTheme, Image, Orientation};
use indexmap::IndexMap;
use std::rc::Rc;
use std::sync::RwLock;
use tokio::sync::mpsc::Sender;
@ -16,17 +16,17 @@ pub struct Item {
pub app_id: String,
pub favorite: bool,
pub open_state: OpenState,
pub windows: Collection<usize, Window>,
pub windows: IndexMap<usize, Window>,
pub name: String,
}
impl Item {
pub const fn new(app_id: String, open_state: OpenState, favorite: bool) -> Self {
pub fn new(app_id: String, open_state: OpenState, favorite: bool) -> Self {
Self {
app_id,
favorite,
open_state,
windows: Collection::new(),
windows: IndexMap::new(),
name: String::new(),
}
}
@ -78,7 +78,7 @@ impl Item {
&self
.windows
.iter()
.map(|win| &win.open_state)
.map(|(_, win)| &win.open_state)
.collect::<Vec<_>>(),
);
self.open_state = new_state;
@ -91,7 +91,7 @@ impl From<ToplevelInfo> for Item {
let name = toplevel.title.clone();
let app_id = toplevel.app_id.clone();
let mut windows = Collection::new();
let mut windows = IndexMap::new();
windows.insert(toplevel.id, toplevel.into());
Self {

View File

@ -3,7 +3,6 @@ mod open_state;
use self::item::{Item, ItemButton, Window};
use self::open_state::OpenState;
use crate::collection::Collection;
use crate::icon::find_desktop_file;
use crate::modules::{Module, ModuleInfo, ModuleUpdateEvent, ModuleWidget, WidgetContext};
use crate::wayland;
@ -12,6 +11,7 @@ use color_eyre::{Help, Report};
use glib::Continue;
use gtk::prelude::*;
use gtk::{Button, IconTheme, Orientation};
use indexmap::IndexMap;
use serde::Deserialize;
use std::process::{Command, Stdio};
use std::sync::{Arc, Mutex};
@ -90,8 +90,8 @@ impl Module<gtk::Box> for LauncherModule {
Item::new(app_id.to_string(), OpenState::Closed, true),
)
})
.collect::<Collection<_, _>>(),
None => Collection::new(),
.collect::<IndexMap<_, _>>(),
None => IndexMap::new(),
};
let items = Arc::new(Mutex::new(items));
@ -108,7 +108,7 @@ impl Module<gtk::Box> for LauncherModule {
let mut items = items.lock().expect("Failed to get lock on items");
for (window, _) in open_windows.clone() {
for (_, (window, _)) in open_windows.clone() {
let item = items.get_mut(&window.app_id);
match item {
Some(item) => {
@ -121,7 +121,7 @@ impl Module<gtk::Box> for LauncherModule {
}
let items = items.iter();
for item in items {
for (_, item) in items {
tx.try_send(ModuleUpdateEvent::Update(LauncherUpdate::AddItem(
item.clone(),
)))?;
@ -282,7 +282,7 @@ impl Module<gtk::Box> for LauncherModule {
let id = match event {
ItemEvent::FocusItem(app_id) => items
.get(&app_id)
.and_then(|item| item.windows.first().map(|win| win.id)),
.and_then(|item| item.windows.first().map(|(_, win)| win.id)),
ItemEvent::FocusWindow(id) => Some(id),
ItemEvent::OpenItem(_) => unreachable!(),
};
@ -325,7 +325,7 @@ impl Module<gtk::Box> for LauncherModule {
let show_icons = self.show_icons;
let orientation = info.bar_position.get_orientation();
let mut buttons = Collection::<String, ItemButton>::new();
let mut buttons = IndexMap::<String, ItemButton>::new();
let controller_tx2 = context.controller_tx.clone();
context.widget_rx.attach(None, move |event| {
@ -431,7 +431,7 @@ impl Module<gtk::Box> for LauncherModule {
placeholder.set_width_request(MAX_WIDTH);
container.add(&placeholder);
let mut buttons = Collection::<String, Collection<usize, Button>>::new();
let mut buttons = IndexMap::<String, IndexMap<usize, Button>>::new();
{
let container = container.clone();
@ -443,7 +443,7 @@ impl Module<gtk::Box> for LauncherModule {
let window_buttons = item
.windows
.into_iter()
.map(|win| {
.map(|(_, win)| {
let button = Button::builder()
.label(&clamp(&win.name))
.height_request(40)
@ -509,7 +509,7 @@ impl Module<gtk::Box> for LauncherModule {
// add app's buttons
if let Some(buttons) = buttons.get(&app_id) {
for button in buttons {
for (_, button) in buttons {
button.style_context().add_class("popup-item");
container.add(button);
}

View File

@ -1,5 +1,4 @@
use super::{Env, ToplevelHandler};
use crate::collection::Collection;
use crate::wayland::toplevel::{ToplevelEvent, ToplevelInfo};
use crate::wayland::toplevel_manager::listen_for_toplevels;
use crate::wayland::ToplevelChange;
@ -21,7 +20,7 @@ use wayland_protocols::wlr::unstable::foreign_toplevel::v1::client::{
pub struct WaylandClient {
pub outputs: Vec<OutputInfo>,
pub seats: Vec<WlSeat>,
pub toplevels: Arc<RwLock<Collection<usize, (ToplevelInfo, ZwlrForeignToplevelHandleV1)>>>,
pub toplevels: Arc<RwLock<IndexMap<usize, (ToplevelInfo, ZwlrForeignToplevelHandleV1)>>>,
toplevel_tx: broadcast::Sender<ToplevelEvent>,
_toplevel_rx: broadcast::Receiver<ToplevelEvent>,
}
@ -35,7 +34,7 @@ impl WaylandClient {
let toplevel_tx2 = toplevel_tx.clone();
let toplevels = Arc::new(RwLock::new(Collection::new()));
let toplevels = Arc::new(RwLock::new(IndexMap::new()));
let toplevels2 = toplevels.clone();
// `queue` is not send so we need to handle everything inside the task