Skip to content

Commit 0580917

Browse files
committed
feat!: compiles but panics
1 parent 71cdf46 commit 0580917

File tree

35 files changed

+2133
-534
lines changed

35 files changed

+2133
-534
lines changed

examples/keyed-elements/Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@ edition = "2021"
66
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
77

88
[dependencies]
9-
frender = { path = "../../packages/frender", features = ["web", "spawn", "either"] }
9+
frender = { path = "../../packages/frender", features = [
10+
"web",
11+
"spawn",
12+
"KeyedElements",
13+
] }
1014
hooks = "3.0.0-alpha"
1115
gloo = "0.8.0"
1216
either = "1.8.1"

examples/synced/src/main.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use either::Either;
2-
use frender::{prelude::*, synced_vec_to_elements, SyncedVec};
2+
use frender::{prelude::*, SyncedVec};
33
use hooks::{ShareValue, Signal, SignalHook};
44

55
struct Item {
@@ -169,7 +169,7 @@ fn Main() {
169169
component_fn!(move || {
170170
h![data.use_signal()];
171171

172-
data.to_element_with_fn(synced_vec_to_elements(
172+
data.to_element_with_fn(SyncedVec::make_fn_mut(
173173
move |Data {
174174
items,
175175
// selected_index,

packages/frender-attr-value/src/kinds/str.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ mod csr {
4747

4848
state.to_as_ref_str().as_ref()
4949
},
50-
eq = |this, cache| <S::StaticStrCache as PartialEq<S>>::eq(cache, this),
50+
eq = |this, cache| this.match_static_str_cache(cache),
5151
);
5252
}
5353
}
Lines changed: 3 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,4 @@
1-
use frender_common::{
2-
strings::{CsrStr, SsrStr},
3-
IntoStaticStr, IntoStaticStrCache, TempStr,
4-
};
5-
6-
trait KnownStaticStr: 'static + AsRef<str> + SsrStr + CsrStr {}
7-
8-
frender_common::impl_many!(
9-
impl<__> KnownStaticStr
10-
for each_of![
11-
&'static str,
12-
String,
13-
std::borrow::Cow<'static, str>,
14-
std::rc::Rc<str>,
15-
std::sync::Arc<str>,
16-
]
17-
{
18-
}
1+
frender_common::define_trait_known_str!(
2+
pub(crate) trait Ssr = KnownSsrStr;
3+
pub(crate) trait Csr = KnownCsrStr;
194
);
20-
21-
pub(crate) trait KnownSsrStr: SsrStr {}
22-
23-
impl<S: KnownStaticStr> KnownSsrStr for S {}
24-
impl<S: IntoStaticStr> KnownSsrStr for TempStr<S> {}
25-
26-
pub(crate) trait KnownCsrStr: CsrStr {}
27-
28-
impl<S: KnownStaticStr> KnownCsrStr for S {}
29-
impl<S: IntoStaticStrCache> KnownCsrStr for TempStr<S> {}

packages/frender-common/src/csr.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
use std::pin::Pin;
2+
3+
pub trait StateUnmount {
4+
fn state_unmount(self: Pin<&mut Self>);
5+
}
6+
7+
impl<T: StateUnmount> StateUnmount for Option<T> {
8+
fn state_unmount(self: Pin<&mut Self>) {
9+
if let Some(this) = self.as_pin_mut() {
10+
this.state_unmount()
11+
}
12+
}
13+
}
14+
15+
impl StateUnmount for () {
16+
fn state_unmount(self: Pin<&mut Self>) {}
17+
}
18+
19+
macro_rules! impl_render_for_tuple {
20+
($($name:ident ($($field_var:ident as $field:ident),+) ,)+) => {
21+
$(
22+
impl<$($field: StateUnmount),+> StateUnmount for ($($field,)+) {
23+
fn state_unmount(self: Pin<&mut Self>) {
24+
let ($($field_var,)+) = crate::utils::pin_project::$name(self);
25+
$( $field_var.state_unmount(); )+
26+
}
27+
}
28+
)+
29+
};
30+
}
31+
32+
impl_render_for_tuple! {
33+
tuple_2 (r0 as R0, r1 as R1),
34+
tuple_3 (r0 as R0, r1 as R1, r2 as R2),
35+
tuple_4 (r0 as R0, r1 as R1, r2 as R2, r3 as R3),
36+
tuple_5 (r0 as R0, r1 as R1, r2 as R2, r3 as R3, r4 as R4),
37+
tuple_6 (r0 as R0, r1 as R1, r2 as R2, r3 as R3, r4 as R4, r5 as R5),
38+
tuple_7 (r0 as R0, r1 as R1, r2 as R2, r3 as R3, r4 as R4, r5 as R5, r6 as R6),
39+
tuple_8 (r0 as R0, r1 as R1, r2 as R2, r3 as R3, r4 as R4, r5 as R5, r6 as R6, r7 as R7),
40+
tuple_9 (r0 as R0, r1 as R1, r2 as R2, r3 as R3, r4 as R4, r5 as R5, r6 as R6, r7 as R7, r8 as R8),
41+
tuple_10(r0 as R0, r1 as R1, r2 as R2, r3 as R3, r4 as R4, r5 as R5, r6 as R6, r7 as R7, r8 as R8, r9 as R9),
42+
tuple_11(r0 as R0, r1 as R1, r2 as R2, r3 as R3, r4 as R4, r5 as R5, r6 as R6, r7 as R7, r8 as R8, r9 as R9, r10 as R10),
43+
tuple_12(r0 as R0, r1 as R1, r2 as R2, r3 as R3, r4 as R4, r5 as R5, r6 as R6, r7 as R7, r8 as R8, r9 as R9, r10 as R10, r11 as R11),
44+
// tuple_13(r0 as R0, r1 as R1, r2 as R2, r3 as R3, r4 as R4, r5 as R5, r6 as R6, r7 as R7, r8 as R8, r9 as R9, r10 as R10, r11 as R11, r12 as R12),
45+
}

packages/frender-common/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,12 @@ pub mod either;
1919
mod empty;
2020
pub use empty::Empty;
2121

22+
pub mod csr;
23+
2224
pub mod strings;
2325

26+
pub mod reactive_value;
27+
2428
pub mod utils {
2529
pub use frender_pin_utils::*;
2630
}
Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
pub use self::render_value::{
2+
RenderValueMut, RenderValueOnce, RenderValueWithFn, RenderValueWithFnMutAndData,
3+
RenderValueWithFnOnceAndData,
4+
};
5+
6+
use std::{pin::Pin, task::Poll};
7+
8+
use crate::csr::StateUnmount;
9+
10+
mod render_value;
11+
12+
mod option;
13+
pub mod str;
14+
15+
pub trait ReactiveValueKind: 'static {
16+
type Value<'a>;
17+
}
18+
19+
impl ReactiveValueKind for str {
20+
type Value<'a> = &'a str;
21+
}
22+
23+
pub trait ReactiveValueState: StateUnmount {
24+
type ReactiveValueKind: ?Sized + ReactiveValueKind;
25+
26+
fn reactive_value_state_poll_render(
27+
self: Pin<&mut Self>,
28+
renderer: &mut impl RenderValueMut<Self::ReactiveValueKind, RenderOutput = ()>,
29+
cx: &mut std::task::Context<'_>,
30+
) -> Poll<()>;
31+
}
32+
33+
impl<T: ReactiveValueState> ReactiveValueState for Option<T> {
34+
type ReactiveValueKind = T::ReactiveValueKind;
35+
36+
fn reactive_value_state_poll_render(
37+
self: Pin<&mut Self>,
38+
renderer: &mut impl RenderValueMut<Self::ReactiveValueKind, RenderOutput = ()>,
39+
cx: &mut std::task::Context<'_>,
40+
) -> Poll<()> {
41+
if let Some(this) = self.as_pin_mut() {
42+
this.reactive_value_state_poll_render(renderer, cx)
43+
} else {
44+
Poll::Ready(())
45+
}
46+
}
47+
}
48+
49+
mod sealed {
50+
pub trait AsOptionMut<T: ?Sized> {}
51+
}
52+
53+
/// This trait is sealed to ensure correct implementation:
54+
/// - `as_option_mut()` returning `None` indicates [`<Self as StateUnmount>::state_unmount()`](StateUnmount::state_unmount) is noop.
55+
/// - `as_option_mut()` returning `None` indicates the renderer has been called with `renderer.*_remove()`
56+
///
57+
/// (Search the code for *Correct AsOptionMut Implementation* to find the code relying on this behavior.)
58+
pub trait AsOptionMut<T: ?Sized>: sealed::AsOptionMut<T> {
59+
// Not using the receiver `&mut self` so that calling this method must be qualified.
60+
fn as_option_mut(this: &mut Self) -> Option<&mut T>;
61+
62+
fn as_option_pin_mut(this: Pin<&mut Self>) -> Option<Pin<&mut T>>;
63+
}
64+
65+
impl<T: ?Sized> sealed::AsOptionMut<T> for T {}
66+
impl<T: ?Sized> AsOptionMut<T> for T {
67+
fn as_option_mut(this: &mut Self) -> Option<&mut T> {
68+
Some(this)
69+
}
70+
71+
fn as_option_pin_mut(this: Pin<&mut Self>) -> Option<Pin<&mut T>> {
72+
Some(this)
73+
}
74+
}
75+
76+
impl<T> sealed::AsOptionMut<T> for Option<T> {}
77+
impl<T> AsOptionMut<T> for Option<T> {
78+
fn as_option_mut(this: &mut Self) -> Option<&mut T> {
79+
this.as_mut()
80+
}
81+
82+
fn as_option_pin_mut(this: Pin<&mut Self>) -> Option<Pin<&mut T>> {
83+
this.as_pin_mut()
84+
}
85+
}
86+
87+
/// The value is optional. That's why [`RenderValueOnce`] and [`RenderValueMut`] have a `remove*` method.
88+
pub trait ReactiveValue<VK: ?Sized + ReactiveValueKind> {
89+
type PinnedState;
90+
91+
/// Requires [`Default`] so that it can be initialized and pinned safely.
92+
type PinnedStateDefault: Default
93+
+ From<Self::PinnedState>
94+
+ AsOptionMut<Self::PinnedState>
95+
+ ReactiveValueState<ReactiveValueKind = VK>;
96+
97+
/// Requires [`Unpin`] so that [`CsrReactiveStrStateUnmount`] can be reused without defining another trait taking `&mut self`.
98+
type UnpinnedState: Unpin + ReactiveValueState<ReactiveValueKind = VK>;
99+
100+
/// Requires `Unpin + ReactiveValueState` so that implementations for `Option` like types doesn't need to
101+
/// introduce a wrapper type to implement `type StateUnpinned`.
102+
/// `Unpin + ReactiveValueState` is usually already implemented because `Option<_>` derives them.
103+
type UnpinnedStateDefault: Default
104+
+ From<Self::UnpinnedState>
105+
+ AsOptionMut<Self::UnpinnedState>
106+
+ Unpin
107+
+ ReactiveValueState<ReactiveValueKind = VK>;
108+
109+
/// `state` is `Default::default()` at a pinned place.
110+
fn reactive_value_render_init_pinned<Out>(
111+
self,
112+
renderer: impl RenderValueOnce<VK, RenderOutput = Out>,
113+
state: Pin<&mut Self::PinnedStateDefault>,
114+
) -> Out;
115+
116+
/// `old_state` has been [unmounted](ReactiveStrStateUnmount::reactive_value_state_unmount) but not necessarily set to `Default::default()`.
117+
fn reactive_value_render_init_with_old_state_pinned<Out>(
118+
self,
119+
renderer: impl RenderValueOnce<VK, RenderOutput = Out>,
120+
old_state: Pin<&mut Self::PinnedStateDefault>,
121+
) -> Out;
122+
123+
/// `renderer` doesn't need to be updated if unnecessary.
124+
fn reactive_value_render_update_pinned<Out>(
125+
self,
126+
renderer: impl RenderValueOnce<VK, RenderOutput = Out>,
127+
state: Pin<&mut Self::PinnedStateDefault>,
128+
) -> Option<Out>;
129+
130+
fn reactive_value_render_init_unpinned<Out>(
131+
self,
132+
renderer: impl RenderValueOnce<VK, RenderOutput = Out>,
133+
) -> (Self::UnpinnedState, Out);
134+
135+
fn reactive_value_render_init_with_old_state_unpinned<Out>(
136+
self,
137+
renderer: impl RenderValueOnce<VK, RenderOutput = Out>,
138+
old_state: &mut Self::UnpinnedState,
139+
) -> Out;
140+
141+
/// `renderer` doesn't need to be updated if unnecessary.
142+
fn reactive_value_render_update_unpinned<Out>(
143+
self,
144+
renderer: impl RenderValueOnce<VK, RenderOutput = Out>,
145+
state: &mut Self::UnpinnedState,
146+
) -> Option<Out>;
147+
}
148+
149+
pub trait ReactiveValueExt<VK: ?Sized + ReactiveValueKind>: ReactiveValue<VK> + Sized {
150+
fn reactive_value_render_init_unpinned_default<Out>(
151+
self,
152+
renderer: impl RenderValueOnce<VK, RenderOutput = Out>,
153+
) -> (Self::UnpinnedStateDefault, Out) {
154+
let (state, out) = self.reactive_value_render_init_unpinned(renderer);
155+
(state.into(), out)
156+
}
157+
158+
fn reactive_value_render_init_with_old_state_unpinned_default<Out>(
159+
self,
160+
renderer: impl RenderValueOnce<VK, RenderOutput = Out>,
161+
old_state: &mut Self::UnpinnedStateDefault,
162+
) -> Out {
163+
if let Some(old_state) = AsOptionMut::<Self::UnpinnedState>::as_option_mut(old_state) {
164+
self.reactive_value_render_init_with_old_state_unpinned(renderer, old_state)
165+
} else {
166+
// I pray for the compiler to optimize out this branch when StateUnpinnedDefault::as_option_mut() always returns Some(_)
167+
let out;
168+
(*old_state, out) = self.reactive_value_render_init_unpinned_default(renderer);
169+
out
170+
}
171+
}
172+
173+
/// Note that this method requires `state` to be Some or will panic.
174+
///
175+
/// `renderer` doesn't need to be updated if unnecessary.
176+
fn reactive_value_render_update_unpinned_default<Out>(
177+
self,
178+
renderer: impl RenderValueOnce<VK, RenderOutput = Out>,
179+
state: &mut Self::UnpinnedStateDefault,
180+
) -> Option<Out> {
181+
if let Some(state) = AsOptionMut::<Self::UnpinnedState>::as_option_mut(state) {
182+
self.reactive_value_render_update_unpinned(renderer, state)
183+
} else {
184+
::core::panic!("state should have been initialized before render_update")
185+
}
186+
}
187+
}
188+
impl<T: ReactiveValue<VK>, VK: ?Sized + ReactiveValueKind> ReactiveValueExt<VK> for T {}
189+
190+
macro_rules! impl_reactive_value_pinned_with_unpinned {
191+
(
192+
type ReactiveValueKind = $ReactiveValueKind:ty;
193+
) => {
194+
type PinnedState = Self::UnpinnedState;
195+
type PinnedStateDefault = Self::UnpinnedStateDefault;
196+
197+
fn reactive_value_render_init_pinned<Out>(
198+
self,
199+
renderer: impl $crate::reactive_value::RenderValueOnce<
200+
$ReactiveValueKind,
201+
RenderOutput = Out,
202+
>,
203+
default_state: ::core::pin::Pin<&mut Self::PinnedStateDefault>,
204+
) -> Out {
205+
let default_state = default_state.get_mut();
206+
207+
::core::debug_assert!(
208+
<Self::PinnedStateDefault as $crate::reactive_value::AsOptionMut<
209+
Self::PinnedState,
210+
>>::as_option_mut(default_state)
211+
.is_none(),
212+
"state must be set to default before render_init"
213+
);
214+
215+
let out;
216+
(*default_state, out) = <Self as $crate::reactive_value::ReactiveValueExt<
217+
$ReactiveValueKind,
218+
>>::reactive_value_render_init_unpinned_default(
219+
self, renderer
220+
);
221+
222+
out
223+
}
224+
225+
fn reactive_value_render_init_with_old_state_pinned<Out>(
226+
self,
227+
renderer: impl $crate::reactive_value::RenderValueOnce<
228+
$ReactiveValueKind,
229+
RenderOutput = Out,
230+
>,
231+
old_state: ::core::pin::Pin<&mut Self::PinnedStateDefault>,
232+
) -> Out {
233+
#[rustfmt::skip]
234+
let out = <Self as $crate::reactive_value::ReactiveValueExt<
235+
$ReactiveValueKind,
236+
>>::reactive_value_render_init_with_old_state_unpinned_default(
237+
self, renderer, old_state.get_mut()
238+
);
239+
out
240+
}
241+
242+
fn reactive_value_render_update_pinned<Out>(
243+
self,
244+
renderer: impl $crate::reactive_value::RenderValueOnce<str, RenderOutput = Out>,
245+
state: ::core::pin::Pin<&mut Self::PinnedStateDefault>,
246+
) -> ::core::option::Option<Out> {
247+
#[rustfmt::skip]
248+
let out = <Self as $crate::reactive_value::ReactiveValueExt<
249+
$ReactiveValueKind,
250+
>>::reactive_value_render_update_unpinned_default(
251+
self, renderer, state.get_mut()
252+
);
253+
out
254+
}
255+
};
256+
}
257+
258+
use impl_reactive_value_pinned_with_unpinned;

0 commit comments

Comments
 (0)