Attempt 2 to make mutability-independent UI
This commit is contained in:
parent
f295dc7a12
commit
09b732cfbc
@ -1,7 +1,4 @@
|
||||
use super::{
|
||||
_ConstMutSwitchUiCallback, _ConstMutSwitchUiTableBaseCallback,
|
||||
_ConstMutSwitchUiTableRowCallback, ConstMutSwitchUi, ConstRef, RefType,
|
||||
};
|
||||
use super::{_ConstMutSwitchUiCallback, ConstMutSwitchUi, ConstRef, RefType, _ConstMutSwitchUiTableHeaderCallback};
|
||||
use eframe::egui::{ScrollArea, Ui};
|
||||
use eframe::emath::Numeric;
|
||||
use std::ops::RangeInclusive;
|
||||
@ -52,9 +49,8 @@ impl ConstMutSwitchUi for ConstUI<'_> {
|
||||
self.ui.label("");
|
||||
}
|
||||
|
||||
fn horizontal<F: _ConstMutSwitchUiCallback<ConstRef>>(&mut self, clojure: &mut F::Clojure<'_>) {
|
||||
self.ui
|
||||
.horizontal(|ui| F::render(&mut ConstUI { ui }, clojure));
|
||||
fn horizontal(&mut self, scope: impl _ConstMutSwitchUiCallback<ConstRef>) {
|
||||
self.ui.horizontal(|ui| scope.render(&mut ConstUI { ui }));
|
||||
}
|
||||
|
||||
fn button<T>(&mut self, label: &str, _storage: &mut &T, _on_click: impl FnOnce(&mut T)) {
|
||||
@ -65,33 +61,22 @@ impl ConstMutSwitchUi for ConstUI<'_> {
|
||||
self.ui.separator();
|
||||
}
|
||||
|
||||
fn scroll_area_2<F: _ConstMutSwitchUiCallback<Self::RefType>>(
|
||||
fn scroll_area_2(
|
||||
&mut self,
|
||||
clojure: &mut F::Clojure<'_>,
|
||||
scope: impl _ConstMutSwitchUiCallback<Self::RefType>,
|
||||
) {
|
||||
ScrollArea::both()
|
||||
.auto_shrink([false, false])
|
||||
.show_viewport(self.ui, |ui, _| F::render(&mut ConstUI { ui }, clojure));
|
||||
.show_viewport(self.ui, |ui, _|scope.render(&mut ConstUI { ui }));
|
||||
}
|
||||
|
||||
fn table<
|
||||
'a,
|
||||
F: _ConstMutSwitchUiTableBaseCallback<ConstRef, Body = _B>,
|
||||
_B: _ConstMutSwitchUiTableRowCallback<ConstRef, Clojure<'a> = _BC>,
|
||||
_BC,
|
||||
>(
|
||||
|
||||
fn table(
|
||||
&mut self,
|
||||
vscroll: bool,
|
||||
columns: usize,
|
||||
header_clojure: <F::Header as _ConstMutSwitchUiTableRowCallback<ConstRef>>::Clojure<'_>,
|
||||
body_data: impl Iterator<Item = _BC>,
|
||||
header: impl _ConstMutSwitchUiTableHeaderCallback<Self::RefType>,
|
||||
) {
|
||||
super::render_table::<ConstUI, F, Self>(
|
||||
self.ui,
|
||||
vscroll,
|
||||
columns,
|
||||
header_clojure,
|
||||
body_data,
|
||||
);
|
||||
super::render_table::<ConstRef, Self>(self.ui, vscroll, columns, header);
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,9 +6,9 @@ mod r#trait;
|
||||
|
||||
pub use r#const::ConstUI;
|
||||
pub use r#mut::MutUI;
|
||||
pub use r#ref::{ConstRef, MutRef, RefGetter, RefType, _IteratorGetter};
|
||||
pub use r#ref::{ConstRef, MutRef, RefType};
|
||||
use table::render_table;
|
||||
pub use table::{
|
||||
_ConstMutSwitchUiTableBaseCallback, _ConstMutSwitchUiTableRowCallback, ConstMutSwitchUiTableRow,
|
||||
_ConstMutSwitchUiTableHeaderCallback, _ConstMutSwitchUiTableRowCallback, ConstMutSwitchUiTableRow,
|
||||
};
|
||||
pub use r#trait::{_ConstMutSwitchUiCallback, ConstMutSwitchUi};
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use super::{ConstMutSwitchUi, MutRef, RefType, _ConstMutSwitchUiTableBaseCallback, _ConstMutSwitchUiTableRowCallback};
|
||||
use super::{_ConstMutSwitchUiTableHeaderCallback, ConstMutSwitchUi, MutRef, RefType};
|
||||
use crate::gui::const_mut_switch::r#trait::_ConstMutSwitchUiCallback;
|
||||
use eframe::egui::{ScrollArea, Ui};
|
||||
use eframe::emath::Numeric;
|
||||
@ -8,6 +8,12 @@ pub struct MutUI<'ui> {
|
||||
ui: &'ui mut Ui,
|
||||
}
|
||||
|
||||
impl<'ui> MutUI<'ui> {
|
||||
pub fn wrap(ui: &'ui mut Ui) -> Self {
|
||||
return Self { ui };
|
||||
}
|
||||
}
|
||||
|
||||
impl ConstMutSwitchUi for MutUI<'_> {
|
||||
type RefType = MutRef;
|
||||
|
||||
@ -15,7 +21,12 @@ impl ConstMutSwitchUi for MutUI<'_> {
|
||||
self.ui.label(s);
|
||||
}
|
||||
|
||||
fn slider<T: Numeric>(&mut self, range: RangeInclusive<T>, step: f64, storage: &mut <Self::RefType as RefType>::Ref<'_, T>) {
|
||||
fn slider<T: Numeric>(
|
||||
&mut self,
|
||||
range: RangeInclusive<T>,
|
||||
step: f64,
|
||||
storage: &mut <Self::RefType as RefType>::Ref<'_, T>,
|
||||
) {
|
||||
crate::gui::slider(self.ui, *storage, range, step);
|
||||
}
|
||||
|
||||
@ -33,9 +44,8 @@ impl ConstMutSwitchUi for MutUI<'_> {
|
||||
self.ui.label("");
|
||||
}
|
||||
|
||||
fn horizontal<I: _ConstMutSwitchUiCallback<MutRef>>(&mut self, clojure: &mut I::Clojure<'_>) {
|
||||
self.ui
|
||||
.horizontal(|ui| I::render(&mut MutUI { ui }, clojure));
|
||||
fn horizontal(&mut self, scope: impl _ConstMutSwitchUiCallback<MutRef>) {
|
||||
self.ui.horizontal(|ui| scope.render(&mut MutUI { ui }));
|
||||
}
|
||||
|
||||
fn button<T>(&mut self, label: &str, storage: &mut &mut T, on_click: impl FnOnce(&mut T)) {
|
||||
@ -48,27 +58,18 @@ impl ConstMutSwitchUi for MutUI<'_> {
|
||||
self.ui.separator();
|
||||
}
|
||||
|
||||
fn scroll_area_2<F: _ConstMutSwitchUiCallback<Self::RefType>>(
|
||||
&mut self,
|
||||
clojure: &mut F::Clojure<'_>,
|
||||
) {
|
||||
fn scroll_area_2(&mut self, scope: impl _ConstMutSwitchUiCallback<MutRef>) {
|
||||
ScrollArea::both()
|
||||
.auto_shrink([false, false])
|
||||
.show_viewport(self.ui, |ui, _| F::render(&mut MutUI { ui }, clojure));
|
||||
.show_viewport(self.ui, |ui, _| scope.render(&mut MutUI { ui }));
|
||||
}
|
||||
|
||||
fn table<
|
||||
'a,
|
||||
F: _ConstMutSwitchUiTableBaseCallback<MutRef, Body = _B>,
|
||||
_B: _ConstMutSwitchUiTableRowCallback<MutRef, Clojure<'a> = _BC>,
|
||||
_BC,
|
||||
>(
|
||||
fn table(
|
||||
&mut self,
|
||||
vscroll: bool,
|
||||
columns: usize,
|
||||
header_clojure: <F::Header as _ConstMutSwitchUiTableRowCallback<MutRef>>::Clojure<'_>,
|
||||
body_data: impl Iterator<Item = _BC>,
|
||||
header: impl _ConstMutSwitchUiTableHeaderCallback<Self::RefType>,
|
||||
) {
|
||||
super::render_table::<MutUI, F, Self>(self.ui, vscroll, columns, header_clojure, body_data);
|
||||
super::render_table::<MutRef, Self>(self.ui, vscroll, columns, header);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
use crate::gui::const_mut_switch::ConstMutSwitchUi;
|
||||
use std::ops::Deref;
|
||||
use std::ptr::NonNull;
|
||||
|
||||
pub trait RefType {
|
||||
type Ref<'a, T: 'a>: Deref<Target = T>;
|
||||
type Ref<'a, T: 'a>: Deref<Target=T>;
|
||||
|
||||
fn _get<'r, 'f: 'r, 's: 'f, S, M>(
|
||||
storge: &'f mut Self::Ref<'s, S>,
|
||||
@ -13,32 +12,6 @@ pub trait RefType {
|
||||
|
||||
unsafe fn _to_ptr<T>(r: Self::Ref<'_, T>) -> NonNull<T>;
|
||||
unsafe fn _from_ptr<'a, T>(r: NonNull<T>) -> Self::Ref<'a, T>;
|
||||
|
||||
fn _iter<'r, 'f: 'r, 's: 'f, F: _IteratorGetter>(
|
||||
storge: &'f mut Self::Ref<'s, F::C>,
|
||||
) -> impl Iterator<Item = Self::Ref<'r, F::T<'r>>>;
|
||||
|
||||
fn _enum_iter<'r, 'f: 'r, 's: 'f, F: _IteratorGetter>(
|
||||
storge: &'f mut Self::Ref<'s, F::C>,
|
||||
) -> impl Iterator<Item = Self::Ref<'r, F::T<'r>>>;
|
||||
}
|
||||
|
||||
pub trait _IteratorGetter {
|
||||
type C;
|
||||
type T<'a>: 'a;
|
||||
fn get_const_iterator<'a>(cont: &'a Self::C) -> impl Iterator<Item = &Self::T<'a>>;
|
||||
|
||||
fn get_mut_iterator<'a>(cont: &'a mut Self::C) -> impl Iterator<Item = &mut Self::T<'a>>;
|
||||
}
|
||||
|
||||
pub trait _EnumeratedIteratorGetter {
|
||||
type C;
|
||||
type T<'a>: 'a;
|
||||
fn get_const_iterator<'a>(cont: &'a Self::C) -> impl Iterator<Item = (usize, &Self::T<'a>)>;
|
||||
|
||||
fn get_mut_iterator<'a>(
|
||||
cont: &'a mut Self::C,
|
||||
) -> impl Iterator<Item = (usize, &mut Self::T<'a>)>;
|
||||
}
|
||||
|
||||
pub struct ConstRef {}
|
||||
@ -61,12 +34,6 @@ impl RefType for ConstRef {
|
||||
unsafe fn _from_ptr<'a, T>(r: NonNull<T>) -> Self::Ref<'a, T> {
|
||||
return r.as_ref();
|
||||
}
|
||||
|
||||
fn _iter<'r, 'f: 'r, 's: 'f, F: _IteratorGetter>(
|
||||
storge: &'f mut Self::Ref<'s, F::C>,
|
||||
) -> impl Iterator<Item = &'r F::T<'r>> {
|
||||
return F::get_const_iterator(storge);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MutRef {}
|
||||
@ -89,18 +56,4 @@ impl RefType for MutRef {
|
||||
unsafe fn _from_ptr<'a, T>(mut r: NonNull<T>) -> Self::Ref<'a, T> {
|
||||
return r.as_mut();
|
||||
}
|
||||
|
||||
fn _iter<'r, 'f: 'r, 's: 'f, F: _IteratorGetter>(
|
||||
storge: &'f mut Self::Ref<'s, F::C>,
|
||||
) -> impl Iterator<Item = &'r mut F::T<'r>> {
|
||||
return F::get_mut_iterator(storge);
|
||||
}
|
||||
}
|
||||
|
||||
pub trait RefGetter {
|
||||
type Ref<'a, T: 'a>;
|
||||
}
|
||||
|
||||
impl<C: ConstMutSwitchUi> RefGetter for C {
|
||||
type Ref<'a, T: 'a> = <C::RefType as RefType>::Ref<'a, T>;
|
||||
}
|
||||
|
||||
@ -1,30 +1,32 @@
|
||||
use super::{_ConstMutSwitchUiCallback, ConstMutSwitchUi, ConstRef, ConstUI, MutRef, MutUI, RefType};
|
||||
#![allow(deprecated)]
|
||||
|
||||
use super::{
|
||||
_ConstMutSwitchUiCallback, ConstMutSwitchUi, ConstRef, ConstUI, MutRef, MutUI, RefType,
|
||||
};
|
||||
use crate::LambdaIterator;
|
||||
use eframe::egui::Ui;
|
||||
use egui_extras::{Column, TableBuilder, TableRow};
|
||||
use std::marker::PhantomData;
|
||||
use std::mem::uninitialized;
|
||||
|
||||
pub trait _ConstMutSwitchUiTableBaseCallback<RefType: super::RefType> {
|
||||
type Header: _ConstMutSwitchUiTableRowCallback<RefType>;
|
||||
type Body: _ConstMutSwitchUiTableRowCallback<RefType>;
|
||||
pub trait _ConstMutSwitchUiTableHeaderCallback<RefType: super::RefType> {
|
||||
type RowRender: _ConstMutSwitchUiTableRowCallback<RefType>;
|
||||
type RowsIterator: LambdaIterator<Item = Self::RowRender>;
|
||||
|
||||
fn header_to_body<'a>(
|
||||
header: &mut <Self::Header as _ConstMutSwitchUiTableRowCallback<RefType>>::Clojure<'a>,
|
||||
) -> impl Iterator<Item = <Self::Body as _ConstMutSwitchUiTableRowCallback<RefType>>::Clojure<'a>>;
|
||||
fn render_header(
|
||||
self,
|
||||
ctx: &mut impl ConstMutSwitchUiTableRow<RefType = RefType>,
|
||||
) -> Self::RowsIterator;
|
||||
}
|
||||
|
||||
pub trait _ConstMutSwitchUiTableRowCallback<RefType: super::RefType> {
|
||||
type Clojure<'a>;
|
||||
|
||||
fn render_row<Row: ConstMutSwitchUiTableRow<RefType = RefType>>(
|
||||
row: &mut Row,
|
||||
clojure: &mut Self::Clojure<'_>,
|
||||
);
|
||||
fn render_row(self, row: &mut impl ConstMutSwitchUiTableRow<RefType = RefType>);
|
||||
}
|
||||
|
||||
pub trait ConstMutSwitchUiTableRow {
|
||||
type RefType: RefType;
|
||||
|
||||
fn cell<F: _ConstMutSwitchUiCallback<Self::RefType>>(&mut self, clojure: &mut F::Clojure<'_>);
|
||||
fn cell(&mut self, scope: impl _ConstMutSwitchUiCallback<Self::RefType>);
|
||||
}
|
||||
|
||||
pub(super) struct ConstMutSwitchUiTableRowImpl<
|
||||
@ -42,13 +44,12 @@ impl<RefType: super::RefType, Constructor: __SwitchUiConstructor1<RefType>> Cons
|
||||
{
|
||||
type RefType = RefType;
|
||||
|
||||
fn cell<F: _ConstMutSwitchUiCallback<Self::RefType>>(&mut self, clojure: &mut F::Clojure<'_>) {
|
||||
self.row
|
||||
.col(|ui| F::render(&mut Constructor::__wrap(ui), clojure));
|
||||
fn cell(&mut self, cell: impl _ConstMutSwitchUiCallback<Self::RefType>) {
|
||||
self.row.col(|ui| cell.render(&mut Constructor::__wrap(ui)));
|
||||
}
|
||||
}
|
||||
|
||||
trait __SwitchUiConstructor1<RefType: super::RefType> {
|
||||
pub(super) trait __SwitchUiConstructor1<RefType: super::RefType> {
|
||||
type Ctx<'a>: ConstMutSwitchUi<RefType = RefType>;
|
||||
|
||||
fn __wrap<'a>(ui: &'a mut Ui) -> Self::Ctx<'a>;
|
||||
@ -58,53 +59,47 @@ impl __SwitchUiConstructor1<ConstRef> for ConstUI<'_> {
|
||||
type Ctx<'a> = ConstUI<'a>;
|
||||
|
||||
fn __wrap<'a>(ui: &'a mut Ui) -> Self::Ctx<'a> {
|
||||
todo!()
|
||||
return ConstUI::wrap(ui);
|
||||
}
|
||||
}
|
||||
impl __SwitchUiConstructor1<MutRef> for MutUI<'_> {
|
||||
type Ctx<'a> = MutUI<'a>;
|
||||
|
||||
fn __wrap<'a>(ui: &'a mut Ui) -> Self::Ctx<'a> {
|
||||
todo!()
|
||||
return MutUI::wrap(ui);
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn render_table<
|
||||
'a,
|
||||
Ctx: ConstMutSwitchUi,
|
||||
F: _ConstMutSwitchUiTableBaseCallback<Ctx::RefType>,
|
||||
Constructor: __SwitchUiConstructor1<Ctx::RefType>,
|
||||
RefType: super::RefType,
|
||||
Constructor: __SwitchUiConstructor1<RefType>,
|
||||
>(
|
||||
ui: &mut Ui,
|
||||
vscroll: bool,
|
||||
columnsCount: usize,
|
||||
headerClojure: &mut <F::Header as _ConstMutSwitchUiTableRowCallback<Ctx::RefType>>::Clojure<'_>,
|
||||
header_render: impl _ConstMutSwitchUiTableHeaderCallback<RefType>,
|
||||
) {
|
||||
let mut rows_it = unsafe { uninitialized() };
|
||||
TableBuilder::new(ui)
|
||||
.striped(true) // Alternating row colors
|
||||
.resizable(true)
|
||||
.vscroll(vscroll)
|
||||
.columns(Column::remainder(), columnsCount)
|
||||
.header(20.0, |row| {
|
||||
F::Header::render_row(
|
||||
&mut ConstMutSwitchUiTableRowImpl::<_, Constructor> {
|
||||
rows_it =
|
||||
header_render.render_header(&mut ConstMutSwitchUiTableRowImpl::<_, Constructor> {
|
||||
row,
|
||||
__phantom: PhantomData::default(),
|
||||
},
|
||||
headerClojure,
|
||||
)
|
||||
});
|
||||
})
|
||||
.body(|mut body| {
|
||||
for mut rowData in F::header_to_body(headerClojure) {
|
||||
rows_it.consume(|row_render| {
|
||||
body.row(20.0, |row| {
|
||||
F::Body::render_row(
|
||||
&mut ConstMutSwitchUiTableRowImpl::<_, Constructor> {
|
||||
row_render.render_row(&mut ConstMutSwitchUiTableRowImpl::<_, Constructor> {
|
||||
row,
|
||||
__phantom: PhantomData::default(),
|
||||
},
|
||||
&mut rowData,
|
||||
)
|
||||
})
|
||||
})
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
use super::{
|
||||
_ConstMutSwitchUiTableBaseCallback, _ConstMutSwitchUiTableRowCallback, ConstRef, RefType,
|
||||
};
|
||||
use super::{_ConstMutSwitchUiTableHeaderCallback, RefType};
|
||||
use eframe::emath::Numeric;
|
||||
use std::ops::RangeInclusive;
|
||||
|
||||
@ -26,10 +24,7 @@ pub trait ConstMutSwitchUi {
|
||||
|
||||
fn space(&mut self);
|
||||
|
||||
fn horizontal<F: _ConstMutSwitchUiCallback<Self::RefType>>(
|
||||
&mut self,
|
||||
clojure: &mut F::Clojure<'_>,
|
||||
);
|
||||
fn horizontal(&mut self, clojure: impl _ConstMutSwitchUiCallback<Self::RefType>);
|
||||
|
||||
fn button<T>(
|
||||
&mut self,
|
||||
@ -40,29 +35,16 @@ pub trait ConstMutSwitchUi {
|
||||
|
||||
fn separator(&mut self);
|
||||
|
||||
fn scroll_area_2<F: _ConstMutSwitchUiCallback<Self::RefType>>(
|
||||
&mut self,
|
||||
clojure: &mut F::Clojure<'_>,
|
||||
);
|
||||
fn scroll_area_2(&mut self, clojure: impl _ConstMutSwitchUiCallback<Self::RefType>);
|
||||
|
||||
fn table<
|
||||
'a,
|
||||
F: _ConstMutSwitchUiTableBaseCallback<Self::RefType>
|
||||
>(
|
||||
fn table(
|
||||
&mut self,
|
||||
vscroll: bool,
|
||||
columns: usize,
|
||||
header_clojure: <F::Header as _ConstMutSwitchUiTableRowCallback<Self::RefType>>::Clojure<
|
||||
'_,
|
||||
>,
|
||||
header: impl _ConstMutSwitchUiTableHeaderCallback<Self::RefType>,
|
||||
);
|
||||
}
|
||||
|
||||
pub trait _ConstMutSwitchUiCallback<RefType: super::RefType> {
|
||||
type Clojure<'a>;
|
||||
|
||||
fn render<SubCtx: ConstMutSwitchUi<RefType = RefType>>(
|
||||
ctx: &mut SubCtx,
|
||||
clojure: &mut Self::Clojure<'_>,
|
||||
);
|
||||
fn render(self, ctx: &mut impl ConstMutSwitchUi<RefType = RefType>);
|
||||
}
|
||||
|
||||
@ -10,6 +10,9 @@
|
||||
|
||||
Альтернативный вариант - параметризовать тип ссылок...
|
||||
|
||||
В данном файле приведены только краткие выдержки из рассуждений.
|
||||

|
||||
|
||||
## 1
|
||||
|
||||
Начальный интерфейс содержал ассоциированный тип с самой ссылкой и методом
|
||||
@ -684,3 +687,151 @@ trait _IteratorGetter {
|
||||
|
||||
```
|
||||
Но способ работает только для очень простых случаев, так что малопригоден.
|
||||
|
||||
## 5
|
||||
|
||||
Что бы упростить функции, отвечающие за интерфейс и абстрагироваться от формата хранения данных,
|
||||
работа с графом была переделана на контексты (почему-то всё всегда приходит к контекстам):
|
||||
```rust
|
||||
trait Ctx_Graph<G> {
|
||||
type RefType: RefType;
|
||||
|
||||
type Vertex;
|
||||
fn iter_vertices(&mut self, graph: &mut G) -> impl Iterator<Item = Self::Vertex>;
|
||||
|
||||
type Edge;
|
||||
fn iter_edges_or_nothing(
|
||||
&mut self,
|
||||
vertex: &mut Self::Vertex,
|
||||
) -> impl Iterator<Item = Option<Self::Edge>>;
|
||||
|
||||
fn get_edge_len_ptr(
|
||||
&mut self,
|
||||
edge: &mut Self::Edge,
|
||||
) -> <Self::RefType as RefType>::Ref<'_, f64>;
|
||||
|
||||
fn set_vertex_to_update_state(&self, state: &mut UpdatePending);
|
||||
}
|
||||
|
||||
fn draw_lengths_table<
|
||||
G,
|
||||
CtxUi: ConstMutSwitchUi,
|
||||
CtxGraph: Ctx_Graph<G, RefType = CtxUi::RefType>,
|
||||
>(
|
||||
ctx_ui: &mut CtxUi,
|
||||
ctx_graph: &mut CtxGraph,
|
||||
graph: G,
|
||||
update: <CtxUi::RefType as RefType>::Ref<'_, UpdatePending>,
|
||||
) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Тип графа передаётся параметром, а не ассоциированным типом, потому что в общем случае
|
||||
контекст может работать с разными представлениями одновременно.
|
||||
|
||||
## 6
|
||||
### 6.1
|
||||
В результате очередной внезапной гениальной мысли, все трейты которые должны были быть
|
||||
лямбдами, вместо ассоциированного типа для параметра теперь используют `self`. Источник
|
||||
озарения - необходимость сделать эту функцию деструктором для передаваемых данных. Не
|
||||
то что бы это нельзя было сделать с параметром, но новое способ оказался удобнее для
|
||||
имплементации:
|
||||
|
||||
```rust
|
||||
trait _ConstMutSwitchUiCallback<RefType: super::RefType> {
|
||||
fn render(self, ctx: &mut impl ConstMutSwitchUi<RefType = RefType>);
|
||||
}
|
||||
|
||||
trait ConstMutSwitchUi {
|
||||
// ...
|
||||
|
||||
fn horizontal<F>(&mut self, clojure: impl _ConstMutSwitchUiCallback<Self::RefType>);
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Такой способ более гибок корректен с точки зрения владения переданными для области данными.
|
||||
|
||||
### 6.2
|
||||
Необходимость сделать этот метод деструктором возникла во время оптимизации трейтов для таблиц:
|
||||
метод для отрисовки заголовка становится конвертером в итератор.
|
||||
|
||||
```rust
|
||||
trait _ConstMutSwitchUiTableHeaderCallback<RefType: super::RefType> {
|
||||
type RowRender: _ConstMutSwitchUiTableRowCallback<RefType=RefType>;
|
||||
|
||||
fn render_header<'a>(
|
||||
self,
|
||||
ctx: &mut impl ConstMutSwitchUiTableRow<RefType = RefType>,
|
||||
) -> impl Iterator<Item = Self::RowRender>;
|
||||
}
|
||||
```
|
||||
|
||||
### 6.3
|
||||
Итератор, который возвращает значения не может контролировать, что возвращённое значение не будет
|
||||
использоваться после выдачи следующего. Чтобы это ограничить, вместо возврата итератор будет
|
||||
передавать значение в полученную лямбду.
|
||||
|
||||
```rust
|
||||
trait LambdaIterator {
|
||||
type Item;
|
||||
|
||||
fn next<R>(&mut self, receiver: impl FnOnce(Self::Item) -> R) -> Option<R>;
|
||||
|
||||
fn consume(mut self, receiver: impl FnMut(Self::Item)) where Self: Sized {
|
||||
while self.next(|e| receiver(e)).is_some() {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Метод `consume` добавлен просто для удобства. На новый трейт были заменены все итераторы в этом
|
||||
модуле.
|
||||
|
||||
### 6.4
|
||||
Но из-за того, что значение в параметр `self` после `_ConstMutSwitchUiTableHeaderCallback::render_header`
|
||||
не существует, нельзя просто взять и вернуть что-то с ним связанное (вообще должно быть можно, но
|
||||
было много красного и лень разбираться). Решение такое же как и с итератором: "возвращать" в лямбду:
|
||||
```rust
|
||||
trait _ConstMutSwitchUiTableHeaderCallback<RefType: super::RefType> {
|
||||
type RowRender: _ConstMutSwitchUiTableRowCallback<RefType>;
|
||||
type RowsIterator: LambdaIterator<Item = Self::RowRender>;
|
||||
|
||||
fn render_header(
|
||||
self,
|
||||
ctx: &mut impl ConstMutSwitchUiTableRow<RefType = RefType>,
|
||||
render_body: impl FnOnce(Self::RowsIterator),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### 6.5
|
||||
Решение передавать в метод отрисовки заголовка лямбду выглядело прекрасно, пока не выяснилось, что
|
||||
для получения дескриптора отрисовки тела таблицы нужно выйти из этого метода, что случается после
|
||||
того как передаётся лямбда которая его использует. Пришлось всё-таки сразиться с borrow checker и найти
|
||||
ошибку, которая мешала возвращать итератор из этого метода и вернуться на этап `6.2`.
|
||||
|
||||
### 6.6
|
||||
Пользоваться контекстом графа внутри лямбды, переданной в итератор от этого контекста по очевидным
|
||||
причинам нельзя, поэтому возвращаемые итераторы были расширены:
|
||||
```rust
|
||||
|
||||
trait Ctx_Graph<G> {
|
||||
// ...
|
||||
|
||||
type Vertex;
|
||||
type VerticesIterator<'a>: LambdaIterator<Item = (&'a mut Self, Self::Vertex)>
|
||||
where
|
||||
Self: 'a;
|
||||
fn iter_vertices(&mut self, graph: &mut G) -> Self::VerticesIterator<'_>;
|
||||
|
||||
type Edge;
|
||||
fn iter_edges_or_nothing(
|
||||
&mut self,
|
||||
vertex: &mut Self::Vertex,
|
||||
) -> impl LambdaIterator<Item = (&mut Self, Option<Self::Edge>)>;
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
@ -1,173 +1,201 @@
|
||||
use crate::UpdatePending;
|
||||
use crate::graph::{CompleteGraph, Edge, EdgesVec, VerticesVec};
|
||||
use crate::gui::const_mut_switch::{_ConstMutSwitchUiCallback, _ConstMutSwitchUiTableBaseCallback, _ConstMutSwitchUiTableRowCallback, ConstMutSwitchUi, ConstMutSwitchUiTableRow, RefGetter, RefType, _IteratorGetter};
|
||||
use eframe::egui::{ScrollArea, Ui};
|
||||
use egui_extras::{Column, TableBuilder};
|
||||
use std::collections::HashSet;
|
||||
use std::ops::{Index, IndexMut};
|
||||
use crate::gui::const_mut_switch::{
|
||||
_ConstMutSwitchUiCallback, _ConstMutSwitchUiTableHeaderCallback,
|
||||
_ConstMutSwitchUiTableRowCallback, ConstMutSwitchUi, ConstMutSwitchUiTableRow, RefType,
|
||||
};
|
||||
use crate::{LambdaIterator, UpdatePending};
|
||||
use std::ptr::NonNull;
|
||||
|
||||
pub fn draw_lengths_table<'a, Ctx: ConstMutSwitchUi>(
|
||||
ctx: &mut Ctx,
|
||||
graph: <Ctx::RefType as RefType>::Ref<'a, CompleteGraph<()>>,
|
||||
update: <Ctx::RefType as RefType>::Ref<'a, UpdatePending>,
|
||||
pub trait Ctx_Graph<G> {
|
||||
type RefType: RefType;
|
||||
|
||||
type Vertex;
|
||||
type VerticesIterator<'a>: LambdaIterator<Item=(&'a mut Self, Self::Vertex)>
|
||||
where
|
||||
Self: 'a;
|
||||
fn iter_vertices(&mut self, graph: &mut G) -> Self::VerticesIterator<'_>;
|
||||
|
||||
type Edge;
|
||||
fn iter_edges_or_nothing(
|
||||
&mut self,
|
||||
vertex: &mut Self::Vertex,
|
||||
) -> impl LambdaIterator<Item=(&mut Self, Option<Self::Edge>)>;
|
||||
|
||||
fn get_edge_len_ptr(
|
||||
&mut self,
|
||||
edge: &mut Self::Edge,
|
||||
) -> <Self::RefType as RefType>::Ref<'_, f64>;
|
||||
|
||||
fn set_vertex_to_update_state(&self, vertex: &Self::Vertex, state: &mut UpdatePending);
|
||||
|
||||
fn vertices_count(&self) -> usize;
|
||||
}
|
||||
|
||||
pub fn draw_lengths_table<
|
||||
G,
|
||||
CtxUi: ConstMutSwitchUi,
|
||||
CtxGraph: Ctx_Graph<G, RefType=CtxUi::RefType>,
|
||||
>(
|
||||
ctx_ui: &mut CtxUi,
|
||||
ctx_graph: &mut CtxGraph,
|
||||
graph: G,
|
||||
update: <CtxUi::RefType as RefType>::Ref<'_, UpdatePending>,
|
||||
) {
|
||||
ctx.scroll_area_2::<ScrollArea>(&mut (graph, update));
|
||||
ctx_ui.scroll_area_2(ScrollArea {
|
||||
ctx_graph,
|
||||
graph,
|
||||
update,
|
||||
});
|
||||
}
|
||||
struct ScrollArea<'g, 'u, G, CtxGraph: Ctx_Graph<G>> {
|
||||
ctx_graph: &'g mut CtxGraph,
|
||||
graph: G,
|
||||
update: <CtxGraph::RefType as RefType>::Ref<'u, UpdatePending>,
|
||||
}
|
||||
|
||||
struct ScrollArea {}
|
||||
impl<RT: RefType> _ConstMutSwitchUiCallback<RT> for ScrollArea {
|
||||
type Clojure<'a> = (RT::Ref<'a, CompleteGraph<()>>, RT::Ref<'a, UpdatePending>);
|
||||
|
||||
fn render<SubCtx: ConstMutSwitchUi<RefType = RT>>(
|
||||
ctx: &mut SubCtx,
|
||||
mut clojure: &mut (RT::Ref<'_, CompleteGraph<()>>, RT::Ref<'_, UpdatePending>),
|
||||
) {
|
||||
ctx.table::<Table>(false, clojure.0.vertex_count(), HeaderData {
|
||||
update_state: clojure.1,
|
||||
edges: CompleteGraph::<()>,
|
||||
vertices: (),
|
||||
})
|
||||
}
|
||||
impl<G, CtxGraph: Ctx_Graph<G>> _ConstMutSwitchUiCallback<CtxGraph::RefType>
|
||||
for ScrollArea<'_, '_, G, CtxGraph>
|
||||
{
|
||||
fn render(self, ctx: &mut impl ConstMutSwitchUi<RefType=CtxGraph::RefType>) {
|
||||
ctx.table(
|
||||
false,
|
||||
self.ctx_graph.vertices_count(),
|
||||
Header {
|
||||
ctx_graph: self.ctx_graph,
|
||||
graph: self.graph,
|
||||
update: self.update,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
struct Table{}
|
||||
impl <RT: RefType>_ConstMutSwitchUiTableBaseCallback<RT> for Table {
|
||||
type Header = Header;
|
||||
type Body = Body;
|
||||
struct Header<'g, 'u, G, CtxGraph: Ctx_Graph<G>> {
|
||||
ctx_graph: &'g mut CtxGraph,
|
||||
graph: G,
|
||||
update: <CtxGraph::RefType as RefType>::Ref<'u, UpdatePending>,
|
||||
}
|
||||
|
||||
fn header_to_body<'a>(header: &mut <Self::Header as _ConstMutSwitchUiTableRowCallback<RT>>::Clojure<'a>) -> impl Iterator<Item=<Self::Body as _ConstMutSwitchUiTableRowCallback<RT>>::Clojure<'a>> {
|
||||
struct ItGet{};
|
||||
impl _IteratorGetter for ItGet {
|
||||
type C = VerticesVec;
|
||||
type T<'a> = HashSet<usize>;
|
||||
impl<'g, 'u, G, CtxGraph: Ctx_Graph<G>> _ConstMutSwitchUiTableHeaderCallback<CtxGraph::RefType>
|
||||
for Header<'g, 'u, G, CtxGraph>
|
||||
{
|
||||
type RowRender = Row<'g, 'u, G, CtxGraph>;
|
||||
|
||||
fn get_const_iterator<'a>(cont: &'a Self::C) -> impl Iterator<Item=&Self::T<'a>> {
|
||||
return cont.iter_indexed()
|
||||
type RowsIterator = RowsIterators<'g, 'u, G, CtxGraph>;
|
||||
|
||||
fn render_header(
|
||||
mut self,
|
||||
row: &mut impl ConstMutSwitchUiTableRow<RefType=CtxGraph::RefType>,
|
||||
) -> Self::RowsIterator {
|
||||
row.cell(CornerCell {});
|
||||
|
||||
let mut column_id = 0;
|
||||
self.ctx_graph
|
||||
.iter_vertices(&mut self.graph)
|
||||
.consume(|(ctx_graph, v)| {
|
||||
column_id += 1;
|
||||
// let update = &mut self.update;
|
||||
let data = TitleCell {
|
||||
ctx_graph: ctx_graph,
|
||||
row_id: column_id,
|
||||
vertex: &v,
|
||||
update: &mut self.update,
|
||||
};
|
||||
row.cell(data)
|
||||
});
|
||||
|
||||
return RowsIterators {
|
||||
vertices: self.ctx_graph.iter_vertices(&mut self.graph),
|
||||
update: self.update,
|
||||
row_id: 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
struct RowsIterators<'g, 'u, G, CtxGraph: Ctx_Graph<G> + 'g> {
|
||||
vertices: CtxGraph::VerticesIterator<'g>,
|
||||
update: <CtxGraph::RefType as RefType>::Ref<'u, UpdatePending>,
|
||||
row_id: usize,
|
||||
}
|
||||
|
||||
fn get_mut_iterator<'a>(cont: &'a mut Self::C) -> impl Iterator<Item=&mut Self::T<'a>> {
|
||||
return cont.iter_indexed_mut()
|
||||
}
|
||||
}
|
||||
impl<'g, 'u, G, CtxGraph: Ctx_Graph<G> + 'g> LambdaIterator for RowsIterators<'g, 'u, G, CtxGraph> {
|
||||
type Item = Row<'g, 'u, G, CtxGraph>;
|
||||
|
||||
return header.vertices.iter_indexed().enumerate().map(|(ri, (vi, eis))| {
|
||||
return RowData{
|
||||
update_state: header.update_state,
|
||||
edges: header.edges,
|
||||
row_id: ri,
|
||||
vertex_id: vi,
|
||||
vertex_edges_ids: eis,
|
||||
fn next<R>(&mut self, receiver: impl FnOnce(Self::Item) -> R) -> Option<R> {
|
||||
return self.vertices.next(|(ctx, v)| {
|
||||
self.row_id += 1;
|
||||
return receiver(Row {
|
||||
ctx_graph: ctx,
|
||||
vertex: v,
|
||||
row_id: self.row_id,
|
||||
update: NonNull::from_mut(&mut self.update),
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
struct Row<'g, 'u, G, CtxGraph: Ctx_Graph<G>> {
|
||||
ctx_graph: &'g mut CtxGraph,
|
||||
vertex: CtxGraph::Vertex,
|
||||
row_id: usize,
|
||||
update: NonNull<<CtxGraph::RefType as RefType>::Ref<'u, UpdatePending>>,
|
||||
}
|
||||
|
||||
impl<G, CtxGraph: Ctx_Graph<G>> _ConstMutSwitchUiTableRowCallback<CtxGraph::RefType>
|
||||
for Row<'_, '_, G, CtxGraph>
|
||||
{
|
||||
fn render_row(mut self, row: &mut impl ConstMutSwitchUiTableRow<RefType=CtxGraph::RefType>) {
|
||||
row.cell(TitleCell {
|
||||
ctx_graph: self.ctx_graph,
|
||||
row_id: self.row_id,
|
||||
vertex: &self.vertex,
|
||||
update: unsafe { self.update.as_mut() },
|
||||
});
|
||||
|
||||
self.ctx_graph
|
||||
.iter_edges_or_nothing(&mut self.vertex)
|
||||
.consume(|(ctx_graph, e)| {
|
||||
match e {
|
||||
None => row.cell(EmptyCell {}),
|
||||
Some(mut e) => row.cell(LengthCell {
|
||||
length: &mut ctx_graph.get_edge_len_ptr(&mut e),
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct HeaderData<'us, 'es, 'eis, RT: RefType> {
|
||||
update_state: RT::Ref<'us, UpdatePending>,
|
||||
edges: RT::Ref<'es, EdgesVec<()>>,
|
||||
vertices: RT::Ref<'eis, VerticesVec>,
|
||||
}
|
||||
|
||||
struct Header {}
|
||||
impl<RT: RefType> _ConstMutSwitchUiTableRowCallback<RT> for Header {
|
||||
type Clojure<'a> = HeaderData<'a, 'a, 'a, RT>;
|
||||
|
||||
fn render_row<Row: ConstMutSwitchUiTableRow<RefType = RT>>(
|
||||
row: &mut Row,
|
||||
clojure: &mut Self::Clojure<'_>,
|
||||
) {
|
||||
row.cell::<CornerCell>(&mut ());
|
||||
for i in 0..clojure.vertices.len() {
|
||||
row.cell::<Title>(&mut (i, NonNull::from_ref(&clojure.update_state)));
|
||||
}
|
||||
|
||||
struct CornerCell {}
|
||||
impl<RT: RefType> _ConstMutSwitchUiCallback<RT> for CornerCell {
|
||||
type Clojure<'a> = ();
|
||||
|
||||
fn render<SubCtx: ConstMutSwitchUi<RefType = RT>>(ctx: &mut SubCtx, _: &mut ()) {
|
||||
struct CornerCell {}
|
||||
impl<RT: RefType> _ConstMutSwitchUiCallback<RT> for CornerCell {
|
||||
fn render(self, ctx: &mut impl ConstMutSwitchUi<RefType=RT>) {
|
||||
ctx.label("#");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct RowData<'us, 'es, 'eis, RT: RefType> {
|
||||
update_state: RT::Ref<'us, UpdatePending>,
|
||||
edges: RT::Ref<'es, EdgesVec<()>>,
|
||||
struct TitleCell<'g, 'v, 'rr, 'r: 'rr, G, CtxGraph: Ctx_Graph<G>> {
|
||||
ctx_graph: &'g mut CtxGraph,
|
||||
row_id: usize,
|
||||
vertex_id: usize,
|
||||
vertex_edges_ids: RT::Ref<'eis, HashSet<usize>>,
|
||||
vertex: &'v CtxGraph::Vertex,
|
||||
update: &'rr mut <CtxGraph::RefType as RefType>::Ref<'r, UpdatePending>,
|
||||
}
|
||||
|
||||
struct Body {}
|
||||
impl<RT: RefType> _ConstMutSwitchUiTableRowCallback<RT> for Body {
|
||||
type Clojure<'a> = RowData<'a, 'a, 'a, RT>;
|
||||
|
||||
fn render_row<Row: ConstMutSwitchUiTableRow<RefType = RT>>(
|
||||
row: &mut Row,
|
||||
clojure: &mut Self::Clojure<'_>,
|
||||
) {
|
||||
row.cell::<Title>(&mut (clojure.row_id, NonNull::from_ref(&clojure.update_state)));
|
||||
|
||||
// not map because a borrow checker doesn't allow returning from lambda.
|
||||
// raw pointer because a borrow checker.
|
||||
let mut local_edges =
|
||||
Vec::<(usize, NonNull<f64>)>::with_capacity(clojure.vertex_edges_ids.len());
|
||||
for ei in clojure.vertex_edges_ids.iter() {
|
||||
let mut e = RT::_get(
|
||||
&mut clojure.edges,
|
||||
|ee| ee.index(*ei),
|
||||
|ee| ee.index_mut(*ei),
|
||||
);
|
||||
let another_vertex = e.another(clojure.vertex_id);
|
||||
let length_storage = RT::_get(&mut e, |e| &e.length, |e| &mut e.length);
|
||||
let length_storage = unsafe { RT::_to_ptr(length_storage) };
|
||||
local_edges.push((another_vertex, length_storage));
|
||||
}
|
||||
local_edges.sort_by_key(|(k, _)| *k);
|
||||
|
||||
for (column_index, cell_data) in local_edges.iter_mut().enumerate() {
|
||||
if (column_index == clojure.row_id) {
|
||||
row.cell::<EmptyCell>(&mut ());
|
||||
}
|
||||
row.cell::<Cell>(&mut unsafe { RT::_from_ptr(cell_data.1) });
|
||||
}
|
||||
|
||||
struct Cell {}
|
||||
impl<RT: RefType> _ConstMutSwitchUiCallback<RT> for Cell {
|
||||
type Clojure<'a> = RT::Ref<'a, f64>;
|
||||
|
||||
fn render<SubCtx: ConstMutSwitchUi<RefType = RT>>(
|
||||
ctx: &mut SubCtx,
|
||||
clojure: &mut Self::Clojure<'_>,
|
||||
) {
|
||||
ctx.slider(0.0..=10.0, 0.1, clojure);
|
||||
}
|
||||
}
|
||||
|
||||
struct EmptyCell {}
|
||||
impl<RT: RefType> _ConstMutSwitchUiCallback<RT> for EmptyCell {
|
||||
type Clojure<'a> = ();
|
||||
|
||||
fn render<SubCtx: ConstMutSwitchUi<RefType = RT>>(_: &mut SubCtx, _: &mut ()) {}
|
||||
}
|
||||
impl<G, CtxGraph: Ctx_Graph<G>> _ConstMutSwitchUiCallback<CtxGraph::RefType>
|
||||
for TitleCell<'_, '_, '_, '_, G, CtxGraph>
|
||||
{
|
||||
fn render(self, ctx: &mut impl ConstMutSwitchUi<RefType=CtxGraph::RefType>) {
|
||||
ctx.label(self.row_id.to_string().as_str());
|
||||
ctx.button("-", self.update, |s| {
|
||||
self.ctx_graph.set_vertex_to_update_state(self.vertex, s)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
struct Title {}
|
||||
impl<RT: RefType> _ConstMutSwitchUiCallback<RT> for Title {
|
||||
type Clojure<'a> = (usize, NonNull<RT::Ref<'a, UpdatePending>>);
|
||||
|
||||
fn render<SubCtx: ConstMutSwitchUi<RefType = RT>>(
|
||||
ctx: &mut SubCtx,
|
||||
clojure: &mut Self::Clojure<'_>,
|
||||
) {
|
||||
ctx.label(clojure.0.to_string().as_str());
|
||||
ctx.button("-", unsafe { clojure.1.as_mut() }, |s| {
|
||||
*s = UpdatePending::Remove(clojure.0)
|
||||
})
|
||||
struct LengthCell<'rr, 'r: 'rr, RT: RefType> {
|
||||
length: &'rr mut RT::Ref<'r, f64>,
|
||||
}
|
||||
impl<RT: RefType> _ConstMutSwitchUiCallback<RT> for LengthCell<'_, '_, RT> {
|
||||
fn render(self, ctx: &mut impl ConstMutSwitchUi<RefType=RT>) {
|
||||
ctx.slider(0.0..=10.0, 0.1, self.length);
|
||||
}
|
||||
}
|
||||
|
||||
struct EmptyCell {}
|
||||
impl<RT: RefType> _ConstMutSwitchUiCallback<RT> for EmptyCell {
|
||||
fn render(self, _: &mut impl ConstMutSwitchUi<RefType=RT>) {}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use crate::graph::{Edge, EdgesVec, VerticesVec};
|
||||
use eframe::egui;
|
||||
use eframe::egui::{Align2, Color32, FontDefinitions, Pos2, Rect, StrokeKind, Ui, Vec2};
|
||||
use eframe::egui::{Align2, Color32, Pos2, Rect, StrokeKind, Ui, Vec2};
|
||||
use eframe::epaint::{CornerRadius, Stroke};
|
||||
|
||||
pub fn render_graph<D>(
|
||||
|
||||
28
utility/src/lambda_iterator.rs
Normal file
28
utility/src/lambda_iterator.rs
Normal file
@ -0,0 +1,28 @@
|
||||
pub trait LambdaIterator {
|
||||
type Item;
|
||||
|
||||
fn next<R>(&mut self, receiver: impl FnOnce(Self::Item) -> R) -> Option<R>;
|
||||
|
||||
fn consume(mut self, mut receiver: impl FnMut(Self::Item))
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
while self.next(|e| receiver(e)).is_some() {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_lambda<I: Iterator>(it: I) -> impl LambdaIterator<Item = I::Item> {
|
||||
return LambdaIteratorConverter { it };
|
||||
}
|
||||
|
||||
struct LambdaIteratorConverter<I: Iterator> {
|
||||
it: I,
|
||||
}
|
||||
|
||||
impl<I: Iterator> LambdaIterator for LambdaIteratorConverter<I> {
|
||||
type Item = I::Item;
|
||||
|
||||
fn next<R>(&mut self, receiver: impl FnOnce(Self::Item) -> R) -> Option<R> {
|
||||
return self.it.next().map(receiver);
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,9 @@
|
||||
pub mod graph;
|
||||
pub mod gui;
|
||||
mod lambda_iterator;
|
||||
mod update_pending;
|
||||
mod weighted_random;
|
||||
|
||||
pub use lambda_iterator::{LambdaIterator, make_lambda};
|
||||
pub use update_pending::{UpdatePending, UpdateTarget};
|
||||
pub use weighted_random::{weighted_random, weighted_random_index};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user