diff --git a/src/backing/containers/mod.rs b/src/backing/containers/mod.rs index 79f8058..74cfd32 100644 --- a/src/backing/containers/mod.rs +++ b/src/backing/containers/mod.rs @@ -1,3 +1,5 @@ mod pair; +mod py_item; pub use pair::Pair; +pub use py_item::PyItem; diff --git a/src/backing/containers/pair.rs b/src/backing/containers/pair.rs index a204cf9..d194835 100644 --- a/src/backing/containers/pair.rs +++ b/src/backing/containers/pair.rs @@ -1,8 +1,7 @@ use std::cmp::Ordering; -/// Helper struct to associate an item with its priority +/// Container to associate an item with a priority #[derive(Debug, Clone, Copy)] -// I mean I guess P should be Ord but I want to use f64 so whatever pub struct Pair { data: D, priority: P, @@ -14,23 +13,13 @@ impl Pair { Self { data, priority } } - /// Retrieve the internal data, it would be nicer to implement this using [`From`] or [`Into`], but I don't see a way to do that using generics + /// Retrieves the internal data. + /// It would be nicer to implement this using [`From`] or [`Into`], but I don't see a way to do that using generics. pub fn data(self) -> D { self.data } } -// The relevant Ord implementations are based just on the priority -impl Ord for Pair { - fn cmp(&self, other: &Self) -> Ordering { - // Yeah this is bad design - // My excuse is that i'm still learning Rust - self.priority - .partial_cmp(&other.priority) - .unwrap_or(Ordering::Equal) - } -} - impl PartialOrd for Pair { fn partial_cmp(&self, other: &Self) -> Option { self.priority.partial_cmp(&other.priority) @@ -42,5 +31,3 @@ impl PartialEq for Pair { self.priority == other.priority } } - -impl Eq for Pair {} diff --git a/src/backing/containers/py_item.rs b/src/backing/containers/py_item.rs new file mode 100644 index 0000000..9c7110c --- /dev/null +++ b/src/backing/containers/py_item.rs @@ -0,0 +1,46 @@ +use std::cmp::Ordering; + +use pyo3::prelude::*; + +/// Container that provides PartialOrd based on the Python object it holds +#[derive(Debug, Clone)] +pub struct PyItem(Py); + +impl PyItem { + /// Creates a new instance + fn new(item: Py) -> Self { + PyItem(item) + } + + /// Retrieves the internal data + fn item(self) -> Py { + self.0 + } +} + +impl PartialOrd for PyItem { + fn partial_cmp(&self, other: &Self) -> Option { + Python::with_gil(|py| { + // Bind objects for convenience + let ours = self.0.bind(py); + let theirs = other.0.bind(py); + + // Compare based on Python comparison implementations + match ours.lt(theirs) { + Ok(true) => Some(Ordering::Less), + Ok(false) => match ours.gt(theirs) { + Ok(true) => Some(Ordering::Greater), + Ok(false) => Some(Ordering::Equal), // If the python class is implemented strangely then this may be wrong + Err(_) => None, + }, + Err(_) => None, + } + }) + } +} + +impl PartialEq for PyItem { + fn eq(&self, other: &Self) -> bool { + Python::with_gil(|py| self.0.bind(py).eq(other.0.bind(py)).unwrap_or(false)) + } +} diff --git a/src/backing/indexed/mod.rs b/src/backing/indexed/mod.rs index c729567..6579634 100644 --- a/src/backing/indexed/mod.rs +++ b/src/backing/indexed/mod.rs @@ -2,7 +2,7 @@ use super::{containers::Pair, pure::PureBacking}; /// A data structure usable for backing an "indexed" queue -pub trait IndexedBacking: +pub trait IndexedBacking: PureBacking> { /// Update an item's priority diff --git a/src/backing/pure/binary_heap.rs b/src/backing/pure/binary_heap.rs index 6569a66..cd84f18 100644 --- a/src/backing/pure/binary_heap.rs +++ b/src/backing/pure/binary_heap.rs @@ -2,12 +2,6 @@ use std::fmt; use super::PureBacking; -/// A binary min-heap backed by an array -#[derive(Debug)] -pub struct BinaryHeap { - data: Vec, -} - /// Indicates why a sift failed #[derive(Debug, Clone)] struct SiftError { @@ -33,7 +27,13 @@ impl fmt::Display for SiftError { /// Whether a sift operation succeeded type SiftResult = Result<(), SiftError>; -impl BinaryHeap { +/// A binary min-heap backed by an array +#[derive(Debug)] +pub struct BinaryHeap { + data: Vec, +} + +impl BinaryHeap { /// Instantiates a new (empty) binary heap pub fn new() -> Self { Self { data: vec![] } @@ -108,7 +108,7 @@ impl BinaryHeap { } } -impl FromIterator for BinaryHeap { +impl FromIterator for BinaryHeap { fn from_iter>(iter: U) -> Self { let mut this = Self { data: Vec::from_iter(iter), @@ -120,7 +120,7 @@ impl FromIterator for BinaryHeap { } } -impl PureBacking for BinaryHeap { +impl PureBacking for BinaryHeap { fn add(&mut self, item: T) { // Append item self.data.push(item); diff --git a/src/backing/pure/mod.rs b/src/backing/pure/mod.rs index a8b0259..1ff9d9a 100644 --- a/src/backing/pure/mod.rs +++ b/src/backing/pure/mod.rs @@ -3,7 +3,7 @@ mod binary_heap; pub use binary_heap::BinaryHeap; /// A data structure usable for backing a "pure" queue -pub trait PureBacking: Send + Sync { +pub trait PureBacking: Send + Sync { /// Places an item into the queue fn add(&mut self, item: T); /// Removes the item with minimum priority, if it exists diff --git a/src/queue/paired.rs b/src/queue/paired.rs index 611524e..69aaee4 100644 --- a/src/queue/paired.rs +++ b/src/queue/paired.rs @@ -16,6 +16,7 @@ pub struct PairedQueue { #[pymethods] impl PairedQueue { + /// Enables generic typing #[classmethod] fn __class_getitem__(cls_: Bound<'_, PyType>, _key: Py) -> Bound<'_, PyType> { cls_ @@ -67,6 +68,7 @@ impl PairedQueue { } impl<'py> PairedQueue { + /// Tries to a Python object into a vector suitable for ingestion into the backing fn from_any(object: &Bound<'py, PyAny>) -> PyResult, f64>>> { if let Ok(vec) = object.extract::, f64)>>() { Ok(Self::from_vec(vec)) @@ -87,12 +89,14 @@ impl<'py> PairedQueue { } } + /// Converts a vector of Python objects and priorities into a vector of items fn from_vec(list: Vec<(Py, f64)>) -> Vec, f64>> { list.into_iter() .map(|(data, priority)| Pair::new(data, priority)) .collect() } + /// Converts a Python dictionary into a vector of items fn from_dict(dict: &Bound<'py, PyDict>) -> PyResult, f64>>> { if let Ok(items) = dict .into_iter() @@ -104,7 +108,7 @@ impl<'py> PairedQueue { { Ok(items) } else { - Err(PyErr::new::("Dict keys were not floats")) + Err(PyErr::new::("Dict keys were not numbers")) } } } diff --git a/src/queue/pure.rs b/src/queue/pure.rs index 8cf4912..d040ff8 100644 --- a/src/queue/pure.rs +++ b/src/queue/pure.rs @@ -1,12 +1,32 @@ -use pyo3::prelude::*; +use pyo3::{prelude::*, types::PyType}; + +use crate::backing::{ + containers::PyItem, + pure::{BinaryHeap, PureBacking}, +}; #[pyclass] -pub struct PureQueue {} +pub struct PureQueue { + backing: Box>, +} #[pymethods] impl PureQueue { + /// Enables generic typing + #[classmethod] + fn __class_getitem__(cls_: Bound<'_, PyType>, _key: Py) -> Bound<'_, PyType> { + cls_ + } + #[new] - fn new() -> Self { - Self {} + #[pyo3(signature = (items=None))] + fn new(items: Option>) -> PyResult { + if let Some(py_object) = items { + todo!() + } else { + Ok(Self { + backing: Box::new(BinaryHeap::new()), + }) + } } }