use crate::{
core::RefIter,
plugins::{Plugin, PluginName},
};
use gtk::{
gio::{self, prelude::*},
glib::{self, subclass::prelude::*},
};
use std::{cell::Ref, convert::TryInto};
mod imp {
use crate::plugins::{Plugin, PluginObject};
use gtk::subclass::prelude::*;
use gtk::{
gio,
glib::{self, Cast, StaticType},
};
use std::{
cell::RefCell,
convert::{TryFrom, TryInto},
};
#[derive(Debug, Default)]
pub struct PluginList {
pub vec: RefCell<Vec<Box<dyn Plugin>>>,
}
#[glib::object_subclass]
impl ObjectSubclass for PluginList {
const NAME: &'static str = "HealthPluginList";
type ParentType = glib::Object;
type Type = super::PluginList;
type Interfaces = (gio::ListModel,);
}
impl ObjectImpl for PluginList {}
impl ListModelImpl for PluginList {
fn item_type(&self) -> glib::Type {
PluginObject::static_type()
}
fn n_items(&self) -> u32 {
self.vec.borrow().len().try_into().unwrap()
}
fn item(&self, position: u32) -> Option<glib::Object> {
self.vec
.borrow()
.get(usize::try_from(position).unwrap())
.map(|o| PluginObject::new(o.clone()).upcast())
}
}
}
glib::wrapper! {
pub struct PluginList(ObjectSubclass<imp::PluginList>) @implements gio::ListModel;
}
impl Default for PluginList {
fn default() -> Self {
Self::new(Vec::new())
}
}
impl PluginList {
pub fn contains(&self, plugin_name: PluginName) -> bool {
self.imp()
.vec
.borrow()
.iter()
.any(|p| p.name() == plugin_name)
}
pub fn is_empty(&self) -> bool {
self.imp().vec.borrow().is_empty()
}
pub fn first(&self) -> Option<Box<dyn Plugin>> {
self.imp().vec.borrow().first().cloned()
}
pub fn get(&self, index: usize) -> Option<Box<dyn Plugin>> {
self.imp().vec.borrow().get(index).cloned()
}
pub fn iter(&self) -> RefIter<Box<dyn Plugin>> {
RefIter::new(Ref::map(self.imp().vec.borrow(), |v| &v[..]))
}
pub fn last(&self) -> Option<Box<dyn Plugin>> {
self.imp().vec.borrow().last().cloned()
}
pub fn len(&self) -> usize {
self.imp().vec.borrow().len()
}
pub fn new(plugin_list: Vec<Box<dyn Plugin>>) -> Self {
let o: Self = glib::Object::new();
o.imp().vec.replace(plugin_list);
o
}
pub fn push(&self, plugin: Box<dyn Plugin>) {
let len = {
let mut vec = self.imp().vec.borrow_mut();
vec.push(plugin);
vec.len() - 1
};
self.items_changed(len.try_into().unwrap(), 0, 1);
}
pub fn remove(&self, plugin_name: PluginName) -> Option<Box<dyn Plugin>> {
let mut changed_position: Option<usize> = None;
let mut ret: Option<Box<dyn Plugin>> = None;
{
let mut vec = self.imp().vec.borrow_mut();
if let Some(f) = vec.iter().position(|x| x.name() == plugin_name) {
ret = Some(vec.remove(f));
changed_position = Some(f);
}
}
if let Some(pos) = changed_position {
self.items_changed(pos.try_into().unwrap(), 1, 0);
}
ret
}
}
#[cfg(test)]
mod test {
use super::PluginList;
use crate::plugins::{Plugin, PluginName, StepsPlugin};
#[test]
fn new() {
PluginList::new(Vec::new());
}
#[test]
fn remove() {
let list = PluginList::new(Vec::new());
assert!(list.is_empty());
assert!(list.remove(PluginName::Steps).is_none());
let plugin = Box::new(StepsPlugin::new());
list.push(plugin.clone());
assert!(!list.is_empty());
assert!(list.contains(PluginName::Steps));
assert_eq!(list.remove(plugin.name()).unwrap().name(), plugin.name());
assert!(!list.contains(PluginName::Calories));
}
}