working on examples

This commit is contained in:
Greg Johnston 2024-02-11 19:24:36 -05:00
parent d726b56b71
commit eea971b9fe
16 changed files with 460 additions and 97 deletions

View File

@ -1,4 +1,4 @@
use leptos::*;
use leptos::prelude::*;
const MANY_COUNTERS: usize = 1000;
@ -77,39 +77,23 @@ pub fn Counters() -> impl IntoView {
}
#[component]
fn Counter(
id: usize,
value: ReadSignal<i32>,
set_value: WriteSignal<i32>,
) -> impl IntoView {
fn Counter(id: usize, value: ArcRwSignal<i32>) -> impl IntoView {
let value = RwSignal::from(value);
let CounterUpdater { set_counters } = use_context().unwrap();
let input = move |ev| {
set_value
.set(event_target_value(&ev).parse::<i32>().unwrap_or_default())
value.set(event_target_value(&ev).parse::<i32>().unwrap_or_default())
};
// this will run when the scope is disposed, i.e., when this row is deleted
// because the signal was created in the parent scope, it won't be disposed
// of until the parent scope is. but we no longer need it, so we'll dispose of
// it when this row is deleted, instead. if we don't dispose of it here,
// this memory will "leak," i.e., the signal will continue to exist until the
// parent component is removed. in the case of this component, where it's the
// root, that's the lifetime of the program.
on_cleanup(move || {
log::debug!("deleted a row");
value.dispose();
});
view! {
<li>
<button on:click=move |_| set_value.update(move |value| *value -= 1)>"-1"</button>
<button on:click=move |_| value.update(move |value| *value -= 1)>"-1"</button>
<input type="text"
prop:value={value}
on:input=input
/>
<span>{value}</span>
<button on:click=move |_| set_value.update(move |value| *value += 1)>"+1"</button>
<button on:click=move |_| value.update(move |value| *value += 1)>"+1"</button>
<button on:click=move |_| set_counters.update(move |counters| counters.retain(|(counter_id, _)| counter_id != &id))>"x"</button>
</li>
}

View File

@ -5,7 +5,7 @@ use crate::{
ToAnySource, ToAnySubscriber,
},
signal::MappedSignalReadGuard,
traits::{DefinedAt, Readable},
traits::{DefinedAt, ReadUntracked},
};
use core::fmt::Debug;
use or_poisoned::OrPoisoned;
@ -158,10 +158,10 @@ impl<T: Send + Sync + 'static> Subscriber for ArcMemo<T> {
}
}
impl<T: Send + Sync + 'static> Readable for ArcMemo<T> {
impl<T: Send + Sync + 'static> ReadUntracked for ArcMemo<T> {
type Value = MappedSignalReadGuard<MemoInner<T>, T>;
fn try_read(&self) -> Option<Self::Value> {
fn try_read_untracked(&self) -> Option<Self::Value> {
self.update_if_necessary();
MappedSignalReadGuard::try_new(Arc::clone(&self.inner), |t| {

View File

@ -11,7 +11,7 @@ use crate::{
},
owner::Owner,
signal::SignalReadGuard,
traits::{DefinedAt, Readable},
traits::{DefinedAt, ReadUntracked},
};
use core::fmt::Debug;
use futures::{FutureExt, StreamExt};
@ -221,10 +221,10 @@ impl<T: 'static> ArcAsyncDerived<T> {
}
}
impl<T: 'static> Readable for ArcAsyncDerived<T> {
impl<T: 'static> ReadUntracked for ArcAsyncDerived<T> {
type Value = SignalReadGuard<AsyncState<T>>;
fn try_read(&self) -> Option<Self::Value> {
fn try_read_untracked(&self) -> Option<Self::Value> {
SignalReadGuard::try_new(Arc::clone(&self.value))
}
}

View File

@ -8,7 +8,7 @@ use crate::{
},
owner::{Stored, StoredData},
signal::{MappedSignalReadGuard, SignalReadGuard},
traits::{DefinedAt, Readable},
traits::{DefinedAt, ReadUntracked},
unwrap_signal,
};
use core::fmt::Debug;
@ -146,11 +146,11 @@ impl<T: Send + Sync + Clone + 'static> IntoFuture for AsyncDerived<T> {
}
}
impl<T: Send + Sync + 'static> Readable for AsyncDerived<T> {
impl<T: Send + Sync + 'static> ReadUntracked for AsyncDerived<T> {
type Value = SignalReadGuard<AsyncState<T>>;
fn try_read(&self) -> Option<Self::Value> {
self.get_value().map(|inner| inner.read())
fn try_read_untracked(&self) -> Option<Self::Value> {
self.get_value().map(|inner| inner.read_untracked())
}
}

View File

@ -2,7 +2,7 @@ use super::{inner::MemoInner, ArcMemo};
use crate::{
owner::{Stored, StoredData},
signal::MappedSignalReadGuard,
traits::{DefinedAt, Readable, Track},
traits::{DefinedAt, ReadUntracked, Track},
};
use std::{fmt::Debug, panic::Location};
@ -80,10 +80,10 @@ impl<T: Send + Sync + 'static> Track for Memo<T> {
}
}
impl<T: Send + Sync + 'static> Readable for Memo<T> {
impl<T: Send + Sync + 'static> ReadUntracked for Memo<T> {
type Value = MappedSignalReadGuard<MemoInner<T>, T>;
fn try_read(&self) -> Option<Self::Value> {
self.get_value().map(|inner| inner.read())
fn try_read_untracked(&self) -> Option<Self::Value> {
self.get_value().map(|inner| inner.read_untracked())
}
}

View File

@ -4,7 +4,7 @@ use crate::{
ArcReadSignal, ArcRwSignal, ArcWriteSignal, ReadSignal, RwSignal,
WriteSignal,
},
traits::{Readable, Set},
traits::{Read, Set},
};
macro_rules! impl_get_fn_traits {
@ -12,7 +12,7 @@ macro_rules! impl_get_fn_traits {
$(
#[cfg(feature = "nightly")]
impl<T: 'static> FnOnce<()> for $ty<T> {
type Output = <Self as Readable>::Value;
type Output = <Self as Read>::Value;
#[inline(always)]
extern "rust-call" fn call_once(self, _args: ()) -> Self::Output {
@ -88,7 +88,7 @@ macro_rules! impl_get_fn_traits_send {
$(
#[cfg(feature = "nightly")]
impl<T: Send + Sync + 'static> FnOnce<()> for $ty<T> {
type Output = <Self as Readable>::Value;
type Output = <Self as Read>::Value;
#[inline(always)]
extern "rust-call" fn call_once(self, _args: ()) -> Self::Output {

View File

@ -1,7 +1,7 @@
use super::{subscriber_traits::AsSubscriberSet, SignalReadGuard};
use crate::{
graph::SubscriberSet,
traits::{DefinedAt, IsDisposed, Readable},
traits::{DefinedAt, IsDisposed, ReadUntracked},
};
use core::fmt::{Debug, Formatter, Result};
use std::{
@ -82,10 +82,10 @@ impl<T> AsSubscriberSet for ArcReadSignal<T> {
}
}
impl<T: 'static> Readable for ArcReadSignal<T> {
impl<T: 'static> ReadUntracked for ArcReadSignal<T> {
type Value = SignalReadGuard<T>;
fn try_read(&self) -> Option<Self::Value> {
fn try_read_untracked(&self) -> Option<Self::Value> {
SignalReadGuard::try_new(Arc::clone(&self.value))
}
}

View File

@ -5,7 +5,7 @@ use super::{
use crate::{
graph::{ReactiveNode, SubscriberSet},
prelude::{IsDisposed, Trigger},
traits::{DefinedAt, Readable, Writeable},
traits::{DefinedAt, ReadUntracked, Writeable},
};
use core::fmt::{Debug, Formatter, Result};
use std::{
@ -112,10 +112,10 @@ impl<T> AsSubscriberSet for ArcRwSignal<T> {
}
}
impl<T: 'static> Readable for ArcRwSignal<T> {
impl<T: 'static> ReadUntracked for ArcRwSignal<T> {
type Value = SignalReadGuard<T>;
fn try_read(&self) -> Option<Self::Value> {
fn try_read_untracked(&self) -> Option<Self::Value> {
SignalReadGuard::try_new(Arc::clone(&self.value))
}
}

View File

@ -4,7 +4,8 @@ use super::{
use crate::{
graph::SubscriberSet,
owner::{Stored, StoredData},
traits::{DefinedAt, IsDisposed, Readable},
traits::{DefinedAt, IsDisposed, ReadUntracked},
unwrap_signal,
};
use core::fmt::Debug;
use std::{
@ -76,10 +77,28 @@ impl<T: Send + Sync + 'static> AsSubscriberSet for ReadSignal<T> {
}
}
impl<T: Send + Sync + 'static> Readable for ReadSignal<T> {
impl<T: Send + Sync + 'static> ReadUntracked for ReadSignal<T> {
type Value = SignalReadGuard<T>;
fn try_read(&self) -> Option<Self::Value> {
self.get_value().map(|inner| inner.read())
fn try_read_untracked(&self) -> Option<Self::Value> {
self.get_value().map(|inner| inner.read_untracked())
}
}
impl<T: Send + Sync + 'static> From<ArcReadSignal<T>> for ReadSignal<T> {
#[track_caller]
fn from(value: ArcReadSignal<T>) -> Self {
ReadSignal {
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner: Stored::new(value),
}
}
}
impl<T: Send + Sync + 'static> From<ReadSignal<T>> for ArcReadSignal<T> {
#[track_caller]
fn from(value: ReadSignal<T>) -> Self {
value.get_value().unwrap_or_else(unwrap_signal!(value))
}
}

View File

@ -5,7 +5,7 @@ use super::{
use crate::{
graph::{ReactiveNode, SubscriberSet},
owner::{Stored, StoredData},
traits::{DefinedAt, IsDisposed, Readable, Trigger, UpdateUntracked},
traits::{DefinedAt, IsDisposed, ReadUntracked, Trigger, UpdateUntracked},
unwrap_signal,
};
use core::fmt::Debug;
@ -146,11 +146,11 @@ impl<T: Send + Sync + 'static> AsSubscriberSet for RwSignal<T> {
}
}
impl<T: Send + Sync + 'static> Readable for RwSignal<T> {
impl<T: Send + Sync + 'static> ReadUntracked for RwSignal<T> {
type Value = SignalReadGuard<T>;
fn try_read(&self) -> Option<Self::Value> {
self.get_value().map(|inner| inner.read())
fn try_read_untracked(&self) -> Option<Self::Value> {
self.get_value().map(|inner| inner.read_untracked())
}
}
@ -170,3 +170,21 @@ impl<T: Send + Sync + 'static> UpdateUntracked for RwSignal<T> {
self.get_value().and_then(|n| n.try_update_untracked(fun))
}
}
impl<T: Send + Sync + 'static> From<ArcRwSignal<T>> for RwSignal<T> {
#[track_caller]
fn from(value: ArcRwSignal<T>) -> Self {
RwSignal {
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner: Stored::new(value),
}
}
}
impl<T: Send + Sync + 'static> From<RwSignal<T>> for ArcRwSignal<T> {
#[track_caller]
fn from(value: RwSignal<T>) -> Self {
value.get_value().unwrap_or_else(unwrap_signal!(value))
}
}

View File

@ -13,20 +13,20 @@
//! - [`IsDisposed`] checks whether a signal is currently accessible.
//!
//! ## Base Traits
//! | Trait | Mode | Description |
//! |--------------|-------|---------------------------------------------------------------------------------------|
//! | [`Track`] | — | Tracks changes to this value, adding it as a source of the current reactive observer. |
//! | [`Trigger`] | — | Notifies subscribers that this value has changed. |
//! | [`Readable`] | Guard | Gives immutable access to the value of this signal. |
//! | [`Writeable`]| Guard | Gives mutable access to the value of this signal.
//! | Trait | Mode | Description |
//! |-------------------|-------|---------------------------------------------------------------------------------------|
//! | [`Track`] | — | Tracks changes to this value, adding it as a source of the current reactive observer. |
//! | [`Trigger`] | — | Notifies subscribers that this value has changed. |
//! | [`ReadUntracked`] | Guard | Gives immutable access to the value of this signal. |
//! | [`Writeable`] | Guard | Gives mutable access to the value of this signal.
//!
//! ## Derived Traits
//!
//! ### Access
//! | Trait | Mode | Composition | Description
//! |-------------------|---------------|-------------------------------|------------
//! | [`WithUntracked`] | `fn(&T) -> U` | [`Readable`] | Applies closure to the current value of the signal and returns result.
//! | [`With`] | `fn(&T) -> U` | [`Readable`] + [`Track`] | Applies closure to the current value of the signal and returns result, with reactive tracking.
//! | [`WithUntracked`] | `fn(&T) -> U` | [`ReadUntracked`] | Applies closure to the current value of the signal and returns result.
//! | [`With`] | `fn(&T) -> U` | [`ReadUntracked`] + [`Track`] | Applies closure to the current value of the signal and returns result, with reactive tracking.
//! | [`GetUntracked`] | `T` | [`WithUntracked`] + [`Clone`] | Clones the current value of the signal.
//! | [`Get`] | `T` | [`GetUntracked`] + [`Track`] | Clones the current value of the signal, with reactive tracking.
//!
@ -42,9 +42,9 @@
//! These traits are designed so that you can implement as few as possible, and the rest will be
//! implemented automatically.
//!
//! For example, if you have a struct for which you can implement [`Readable`] and [`Track`], then
//! For example, if you have a struct for which you can implement [`ReadUntracked`] and [`Track`], then
//! [`WithUntracked`] and [`With`] will be implemented automatically (as will [`GetUntracked`] and
//! [`Get`] for `Clone` types). But if you cannot implement [`Readable`] (because, for example,
//! [`Get`] for `Clone` types). But if you cannot implement [`ReadUntracked`] (because, for example,
//! there isn't an `RwLock` you can wrap in a [`SignalReadGuard`](crate::signal::SignalReadGuard),
//! but you can still implement [`WithUntracked`] and [`Track`], the same traits will still be implemented.
@ -95,15 +95,43 @@ impl<T: Source + ToAnySource> Track for T {
}
}
pub trait Readable: Sized + DefinedAt {
pub trait ReadUntracked: Sized + DefinedAt {
type Value: Deref;
#[track_caller]
fn try_read_untracked(&self) -> Option<Self::Value>;
#[track_caller]
fn read_untracked(&self) -> Self::Value {
self.try_read_untracked()
.unwrap_or_else(unwrap_signal!(self))
}
}
pub trait Read {
type Value: Deref;
#[track_caller]
fn try_read(&self) -> Option<Self::Value>;
#[track_caller]
fn read(&self) -> Self::Value;
}
impl<T> Read for T
where
T: Track + ReadUntracked,
{
type Value = T::Value;
fn try_read(&self) -> Option<Self::Value> {
self.track();
self.try_read_untracked()
}
fn read(&self) -> Self::Value {
self.try_read().unwrap_or_else(unwrap_signal!(self))
self.track();
self.read_untracked()
}
}
@ -144,15 +172,15 @@ pub trait WithUntracked: DefinedAt {
impl<T> WithUntracked for T
where
T: DefinedAt + Readable,
T: DefinedAt + ReadUntracked,
{
type Value = <<Self as Readable>::Value as Deref>::Target;
type Value = <<Self as ReadUntracked>::Value as Deref>::Target;
fn try_with_untracked<U>(
&self,
fun: impl FnOnce(&Self::Value) -> U,
) -> Option<U> {
self.try_read().map(|value| fun(&value))
self.try_read_untracked().map(|value| fun(&value))
}
}

View File

@ -2,7 +2,7 @@ use reactive_graph::{
computed::{ArcAsyncDerived, AsyncDerived, AsyncState},
executor::Executor,
signal::RwSignal,
traits::{Get, Readable, Set, With, WithUntracked},
traits::{Get, Read, Set, With, WithUntracked},
};
#[cfg(feature = "tokio")]

View File

@ -1,7 +1,7 @@
use reactive_graph::{
signal::{arc_signal, signal, ArcRwSignal, RwSignal},
traits::{
Get, GetUntracked, Readable, Set, Update, UpdateUntracked, With,
Get, GetUntracked, Read, Set, Update, UpdateUntracked, With,
WithUntracked, Writeable,
},
};

View File

@ -1,46 +1,271 @@
//! Implements the [`Render`] and [`RenderHtml`] traits for signal guard types.
use crate::{prelude::RenderHtml, renderer::Renderer, view::Render};
use crate::{
hydration::Cursor,
prelude::RenderHtml,
renderer::{CastFrom, Renderer},
view::{
strings::StrState, InfallibleRender, Mountable, Position,
PositionState, Render, ToTemplate,
},
};
use reactive_graph::signal::SignalReadGuard;
use std::{
fmt::Write,
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6},
num::{
NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8,
NonZeroIsize, NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64,
NonZeroU8, NonZeroUsize,
},
};
impl<T, Rndr> Render<Rndr> for SignalReadGuard<T>
where
T: PartialEq + Clone + Render<Rndr>,
Rndr: Renderer,
{
type State = T::State;
// any changes here should also be made in src/view/primitives.rs
macro_rules! render_primitive {
($($child_type:ty),* $(,)?) => {
$(
paste::paste! {
pub struct [<SignalReadGuard $child_type:camel State>]<R>(R::Text, $child_type) where R: Renderer;
impl<'a, R: Renderer> Mountable<R> for [<SignalReadGuard $child_type:camel State>]<R> {
fn unmount(&mut self) {
self.0.unmount()
}
fn mount(
&mut self,
parent: &<R as Renderer>::Element,
marker: Option<&<R as Renderer>::Node>,
) {
R::insert_node(parent, self.0.as_ref(), marker);
}
fn insert_before_this(
&self,
parent: &<R as Renderer>::Element,
child: &mut dyn Mountable<R>,
) -> bool {
child.mount(parent, Some(self.0.as_ref()));
true
}
}
impl<'a, R: Renderer> Render<R> for SignalReadGuard<$child_type> {
type State = [<SignalReadGuard $child_type:camel State>]<R>;
fn build(self) -> Self::State {
let node = R::create_text_node(&self.to_string());
[<SignalReadGuard $child_type:camel State>](node, *self)
}
fn rebuild(self, state: &mut Self::State) {
let [<SignalReadGuard $child_type:camel State>](node, this) = state;
if &self != this {
crate::log(&format!("not equal"));
R::set_text(node, &self.to_string());
*this = *self;
}
}
}
impl<'a> InfallibleRender for SignalReadGuard<$child_type> {}
impl<'a, R> RenderHtml<R> for SignalReadGuard<$child_type>
where
R: Renderer,
R::Node: Clone,
R::Element: Clone,
{
const MIN_LENGTH: usize = 0;
fn to_html_with_buf(self, buf: &mut String, position: &mut Position) {
// add a comment node to separate from previous sibling, if any
if matches!(position, Position::NextChildAfterText) {
buf.push_str("<!>")
}
write!(buf, "{}", self);
*position = Position::NextChildAfterText;
}
fn hydrate<const FROM_SERVER: bool>(
self,
cursor: &Cursor<R>,
position: &PositionState,
) -> Self::State {
if position.get() == Position::FirstChild {
cursor.child();
} else {
cursor.sibling();
}
// separating placeholder marker comes before text node
if matches!(position.get(), Position::NextChildAfterText) {
cursor.sibling();
}
let node = cursor.current();
let node = R::Text::cast_from(node)
.expect("couldn't cast text node from node");
if !FROM_SERVER {
R::set_text(&node, &self.to_string());
}
position.set(Position::NextChildAfterText);
[<SignalReadGuard $child_type:camel State>](node, *self)
}
}
impl<'a> ToTemplate for SignalReadGuard<$child_type> {
const TEMPLATE: &'static str = " <!>";
fn to_template(
buf: &mut String,
_class: &mut String,
_style: &mut String,
_inner_html: &mut String,
position: &mut Position,
) {
if matches!(*position, Position::NextChildAfterText) {
buf.push_str("<!>")
}
buf.push(' ');
*position = Position::NextChildAfterText;
}
}
}
)*
};
}
render_primitive![
usize,
u8,
u16,
u32,
u64,
u128,
isize,
i8,
i16,
i32,
i64,
i128,
f32,
f64,
char,
bool,
IpAddr,
SocketAddr,
SocketAddrV4,
SocketAddrV6,
Ipv4Addr,
Ipv6Addr,
NonZeroI8,
NonZeroU8,
NonZeroI16,
NonZeroU16,
NonZeroI32,
NonZeroU32,
NonZeroI64,
NonZeroU64,
NonZeroI128,
NonZeroU128,
NonZeroIsize,
NonZeroUsize,
];
// strings
pub struct SignalReadGuardStringState<R: Renderer> {
node: R::Text,
str: String,
}
impl<R: Renderer> Render<R> for SignalReadGuard<String> {
type State = SignalReadGuardStringState<R>;
fn build(self) -> Self::State {
todo!()
let node = R::create_text_node(&self);
SignalReadGuardStringState {
node,
str: self.to_string(),
}
}
fn rebuild(self, state: &mut Self::State) {
todo!()
let SignalReadGuardStringState { node, str } = state;
if *self != *str {
R::set_text(node, &self);
str.clear();
str.push_str(&self);
}
}
}
impl<T, Rndr> RenderHtml<Rndr> for SignalReadGuard<T>
where
T: PartialEq + Clone + RenderHtml<Rndr>,
Rndr: Renderer,
Rndr::Element: Clone,
Rndr::Node: Clone,
{
const MIN_LENGTH: usize = T::MIN_LENGTH;
impl InfallibleRender for SignalReadGuard<String> {}
fn to_html_with_buf(
self,
buf: &mut String,
position: &mut crate::view::Position,
) {
todo!()
impl<R> RenderHtml<R> for SignalReadGuard<String>
where
R: Renderer,
R::Node: Clone,
R::Element: Clone,
{
const MIN_LENGTH: usize = 0;
fn to_html_with_buf(self, buf: &mut String, position: &mut Position) {
<&str as RenderHtml<R>>::to_html_with_buf(&self, buf, position)
}
fn hydrate<const FROM_SERVER: bool>(
self,
cursor: &crate::hydration::Cursor<Rndr>,
position: &crate::view::PositionState,
cursor: &Cursor<R>,
position: &PositionState,
) -> Self::State {
todo!()
let this: &str = self.as_ref();
let StrState { node, .. } =
this.hydrate::<FROM_SERVER>(cursor, position);
SignalReadGuardStringState {
node,
str: self.to_string(),
}
}
}
impl ToTemplate for SignalReadGuard<String> {
const TEMPLATE: &'static str = <&str as ToTemplate>::TEMPLATE;
fn to_template(
buf: &mut String,
class: &mut String,
style: &mut String,
inner_html: &mut String,
position: &mut Position,
) {
<&str as ToTemplate>::to_template(
buf, class, style, inner_html, position,
)
}
}
impl<R: Renderer> Mountable<R> for SignalReadGuardStringState<R> {
fn unmount(&mut self) {
self.node.unmount()
}
fn mount(
&mut self,
parent: &<R as Renderer>::Element,
marker: Option<&<R as Renderer>::Node>,
) {
R::insert_node(parent, self.node.as_ref(), marker);
}
fn insert_before_this(
&self,
parent: &<R as Renderer>::Element,
child: &mut dyn Mountable<R>,
) -> bool {
child.mount(parent, Some(self.node.as_ref()));
true
}
}

View File

@ -16,6 +16,7 @@ use std::{
},
};
// any changes here should also be made in src/reactive_graph/guards.rs
macro_rules! render_primitive {
($($child_type:ty),* $(,)?) => {
$(

View File

@ -6,7 +6,7 @@ use crate::{
hydration::Cursor,
renderer::{CastFrom, Renderer},
};
use std::{rc::Rc, sync::Arc};
use std::{borrow::Cow, rc::Rc, sync::Arc};
pub struct StrState<'a, R: Renderer> {
pub node: R::Text,
@ -121,6 +121,7 @@ where
true
}
}
pub struct StringState<R: Renderer> {
node: R::Text,
str: String,
@ -380,3 +381,90 @@ impl<R: Renderer> Mountable<R> for ArcStrState<R> {
true
}
}
pub struct CowStrState<'a, R: Renderer> {
node: R::Text,
str: Cow<'a, str>,
}
impl<'a, R: Renderer> Render<R> for Cow<'a, str> {
type State = CowStrState<'a, R>;
fn build(self) -> Self::State {
let node = R::create_text_node(&self);
CowStrState { node, str: self }
}
fn rebuild(self, state: &mut Self::State) {
let CowStrState { node, str } = state;
if self != *str {
R::set_text(node, &self);
*str = self;
}
}
}
impl<'a> InfallibleRender for Cow<'a, str> {}
impl<'a, R> RenderHtml<R> for Cow<'a, str>
where
R: Renderer,
R::Node: Clone,
R::Element: Clone,
{
const MIN_LENGTH: usize = 0;
fn to_html_with_buf(self, buf: &mut String, position: &mut Position) {
<&str as RenderHtml<R>>::to_html_with_buf(&self, buf, position)
}
fn hydrate<const FROM_SERVER: bool>(
self,
cursor: &Cursor<R>,
position: &PositionState,
) -> Self::State {
let this: &str = self.as_ref();
let StrState { node, .. } =
this.hydrate::<FROM_SERVER>(cursor, position);
CowStrState { node, str: self }
}
}
impl<'a> ToTemplate for Cow<'a, str> {
const TEMPLATE: &'static str = <&str as ToTemplate>::TEMPLATE;
fn to_template(
buf: &mut String,
class: &mut String,
style: &mut String,
inner_html: &mut String,
position: &mut Position,
) {
<&str as ToTemplate>::to_template(
buf, class, style, inner_html, position,
)
}
}
impl<'a, R: Renderer> Mountable<R> for CowStrState<'a, R> {
fn unmount(&mut self) {
self.node.unmount()
}
fn mount(
&mut self,
parent: &<R as Renderer>::Element,
marker: Option<&<R as Renderer>::Node>,
) {
R::insert_node(parent, self.node.as_ref(), marker);
}
fn insert_before_this(
&self,
parent: &<R as Renderer>::Element,
child: &mut dyn Mountable<R>,
) -> bool {
child.mount(parent, Some(self.node.as_ref()));
true
}
}