From d68fa762965971b53e037efcf8e8feb5393788ba Mon Sep 17 00:00:00 2001 From: Ken Micklas Date: Tue, 25 Jun 2024 19:35:30 +0100 Subject: [PATCH] Generalize slice view to any iterator --- examples/tutorial/src/main.rs | 8 +- .../src/collections/{slice.rs => iter.rs} | 76 +++++++++---------- ravel-web/src/collections/mod.rs | 4 +- 3 files changed, 42 insertions(+), 46 deletions(-) rename ravel-web/src/collections/{slice.rs => iter.rs} (56%) diff --git a/examples/tutorial/src/main.rs b/examples/tutorial/src/main.rs index 5cc0178..264a73d 100644 --- a/examples/tutorial/src/main.rs +++ b/examples/tutorial/src/main.rs @@ -3,7 +3,7 @@ use std::collections::BTreeMap; use ravel::{adapt_ref, with, with_local}; use ravel_web::{ any, attr, - collections::{btree_map, slice}, + collections::{btree_map, iter}, el, event::{self, on, on_}, format_text, @@ -193,7 +193,7 @@ fn dynamic_view(model: &Model) -> View!(Model, '_) { /// at any position. /// /// If the data is just an array which grows or shrinks at the end, we can use -/// [`slice()`] to generate a [`trait@View`] over a [`Vec`]/slice. +/// [`iter()`] to generate a [`trait@View`] over any iterator. fn lists(model: &Model) -> View!(Model, '_) { ( el::h2("Map view"), @@ -215,10 +215,10 @@ fn lists(model: &Model) -> View!(Model, '_) { ))) })), ))), - el::h2("Slice view"), + el::h2("Iterator view"), el::p(el::table(( el::thead(el::tr((el::td(()), el::td("Id"), el::td("Message")))), - el::tbody(slice(&model.item_vec, |cx, i, (key, value)| { + el::tbody(iter(&model.item_vec, |cx, i, (key, value)| { let key = *key; cx.build(el::tr(( el::td(el::button(( diff --git a/ravel-web/src/collections/slice.rs b/ravel-web/src/collections/iter.rs similarity index 56% rename from ravel-web/src/collections/slice.rs rename to ravel-web/src/collections/iter.rs index 68fddae..df8f96a 100644 --- a/ravel-web/src/collections/slice.rs +++ b/ravel-web/src/collections/iter.rs @@ -1,4 +1,4 @@ -use std::{cmp::Ordering, marker::PhantomData}; +use std::{iter::once, marker::PhantomData}; use ravel::{with, State, Token}; use web_sys::wasm_bindgen::UnwrapThrowExt; @@ -8,23 +8,22 @@ use crate::{ BuildCx, Builder, Cx, RebuildCx, Web, }; -pub struct SliceBuilder<'data, T, RenderItem, S> { - data: &'data [T], +pub struct IterBuilder { + iter: I, render_item: RenderItem, phantom: PhantomData, } -impl<'data, T, RenderItem, S: 'static> Builder - for SliceBuilder<'data, T, RenderItem, S> +impl Builder + for IterBuilder where - RenderItem: Fn(Cx, usize, &T) -> Token, + RenderItem: Fn(Cx, usize, I::Item) -> Token, { - type State = SliceState; + type State = IterState; fn build(self, cx: BuildCx) -> Self::State { let data = self - .data - .iter() + .iter .enumerate() .map(|(i, v)| { let header = @@ -41,22 +40,22 @@ where let footer = web_sys::Comment::new_with_data("|").unwrap_throw(); cx.position.insert(&footer); - SliceState { data, footer } + IterState { data, footer } } - fn rebuild(self, cx: RebuildCx, state: &mut Self::State) { - for (i, (v, entry)) in - self.data.iter().zip(state.data.iter_mut()).enumerate() - { - with(|cx| (self.render_item)(cx, i, v)) - .rebuild(cx, &mut entry.state) - } + fn rebuild(mut self, cx: RebuildCx, state: &mut Self::State) { + let mut data = state.data.iter_mut(); - match self.data.len().cmp(&state.data.len()) { - Ordering::Equal => {} - Ordering::Greater => state.data.extend( - self.data.iter().enumerate().skip(state.data.len()).map( - |(i, v)| { + for i in 0.. { + match (self.iter.next(), data.next()) { + (None, None) => break, + (None, Some(entry)) => { + clear(cx.parent, &entry.header, &state.footer); + state.data.truncate(i); + break; + } + (Some(v), None) => { + state.data.extend(once(v).chain(self.iter).map(|v| { let position = Position { parent: cx.parent, insert_before: &state.footer, @@ -72,27 +71,24 @@ where state: with(|cx| (self.render_item)(cx, i, v)) .build(BuildCx { position }), } - }, - ), - ), - Ordering::Less => { - clear( - cx.parent, - &state.data[self.data.len()].header, - &state.footer, - ); - state.data.truncate(self.data.len()); + })); + break; + } + (Some(v), Some(entry)) => { + with(|cx| (self.render_item)(cx, i, v)) + .rebuild(cx, &mut entry.state) + } } } } } -pub struct SliceState { +pub struct IterState { data: Vec>, footer: web_sys::Comment, } -impl State for SliceState +impl State for IterState where S: State, { @@ -108,16 +104,16 @@ struct Entry { state: S, } -pub fn slice( - data: &[T], +pub fn iter( + iter: I, render_item: RenderItem, -) -> SliceBuilder +) -> IterBuilder where - RenderItem: Fn(Cx, usize, &T) -> Token, + RenderItem: Fn(Cx, usize, I::Item) -> Token, { - SliceBuilder { + IterBuilder { render_item, - data, + iter: iter.into_iter(), phantom: PhantomData, } } diff --git a/ravel-web/src/collections/mod.rs b/ravel-web/src/collections/mod.rs index 593024c..ee199fa 100644 --- a/ravel-web/src/collections/mod.rs +++ b/ravel-web/src/collections/mod.rs @@ -1,7 +1,7 @@ //! Views over dynamically sized collections. pub mod btree_map; -pub mod slice; +pub mod iter; pub use btree_map::btree_map; -pub use slice::slice; +pub use iter::iter;