Skip to content

Commit

Permalink
feat: Oco (Owned Clones Once) smart pointer (#1480)
Browse files Browse the repository at this point in the history
  • Loading branch information
DanikVitek authored Aug 26, 2023
1 parent 6c3e2fe commit 793c191
Show file tree
Hide file tree
Showing 31 changed files with 1,020 additions and 228 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ Cargo.lock
.idea
.direnv
.envrc

.vscode
11 changes: 9 additions & 2 deletions leptos/src/additional_attributes.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use crate::TextProp;
use std::rc::Rc;

/// A collection of additional HTML attributes to be applied to an element,
/// each of which may or may not be reactive.
#[derive(Default, Clone)]
#[derive(Clone)]
#[repr(transparent)]
pub struct AdditionalAttributes(pub(crate) Vec<(String, TextProp)>);
pub struct AdditionalAttributes(pub(crate) Rc<[(String, TextProp)]>);

impl<I, T, U> From<I> for AdditionalAttributes
where
Expand All @@ -22,6 +23,12 @@ where
}
}

impl Default for AdditionalAttributes {
fn default() -> Self {
Self([].into_iter().collect())
}
}

/// Iterator over additional HTML attributes.
#[repr(transparent)]
pub struct AdditionalAttributesIter<'a>(
Expand Down
32 changes: 24 additions & 8 deletions leptos/src/text_prop.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
use leptos_reactive::Oco;
use std::{fmt::Debug, rc::Rc};

/// Describes a value that is either a static or a reactive string, i.e.,
/// a [`String`], a [`&str`], or a reactive `Fn() -> String`.
#[derive(Clone)]
pub struct TextProp(Rc<dyn Fn() -> String>);
pub struct TextProp(Rc<dyn Fn() -> Oco<'static, str>>);

impl TextProp {
/// Accesses the current value of the property.
#[inline(always)]
pub fn get(&self) -> String {
pub fn get(&self) -> Oco<'static, str> {
(self.0)()
}
}
Expand All @@ -21,23 +22,38 @@ impl Debug for TextProp {

impl From<String> for TextProp {
fn from(s: String) -> Self {
let s: Oco<'_, str> = Oco::Counted(Rc::from(s));
TextProp(Rc::new(move || s.clone()))
}
}

impl From<&str> for TextProp {
fn from(s: &str) -> Self {
let s = s.to_string();
impl From<&'static str> for TextProp {
fn from(s: &'static str) -> Self {
let s: Oco<'_, str> = s.into();
TextProp(Rc::new(move || s.clone()))
}
}

impl<F> From<F> for TextProp
impl From<Rc<str>> for TextProp {
fn from(s: Rc<str>) -> Self {
let s: Oco<'_, str> = s.into();
TextProp(Rc::new(move || s.clone()))
}
}

impl From<Oco<'static, str>> for TextProp {
fn from(s: Oco<'static, str>) -> Self {
TextProp(Rc::new(move || s.clone()))
}
}

impl<F, S> From<F> for TextProp
where
F: Fn() -> String + 'static,
F: Fn() -> S + 'static,
S: Into<Oco<'static, str>>,
{
#[inline(always)]
fn from(s: F) -> Self {
TextProp(Rc::new(s))
TextProp(Rc::new(move || s().into()))
}
}
20 changes: 10 additions & 10 deletions leptos_dom/src/components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ pub use dyn_child::*;
pub use each::*;
pub use errors::*;
pub use fragment::*;
use leptos_reactive::untrack_with_diagnostics;
use leptos_reactive::{untrack_with_diagnostics, Oco};
#[cfg(all(target_arch = "wasm32", feature = "web"))]
use once_cell::unsync::OnceCell;
use std::fmt;
#[cfg(all(target_arch = "wasm32", feature = "web"))]
use std::rc::Rc;
use std::{borrow::Cow, fmt};
pub use unit::*;
#[cfg(all(target_arch = "wasm32", feature = "web"))]
use wasm_bindgen::JsCast;
Expand Down Expand Up @@ -55,7 +55,7 @@ pub struct ComponentRepr {
#[cfg(all(target_arch = "wasm32", feature = "web"))]
mounted: Rc<OnceCell<()>>,
#[cfg(any(debug_assertions, feature = "ssr"))]
pub(crate) name: Cow<'static, str>,
pub(crate) name: Oco<'static, str>,
#[cfg(debug_assertions)]
_opening: Comment,
/// The children of the component.
Expand Down Expand Up @@ -163,24 +163,24 @@ impl IntoView for ComponentRepr {
impl ComponentRepr {
/// Creates a new [`Component`].
#[inline(always)]
pub fn new(name: impl Into<Cow<'static, str>>) -> Self {
pub fn new(name: impl Into<Oco<'static, str>>) -> Self {
Self::new_with_id_concrete(name.into(), HydrationCtx::id())
}

/// Creates a new [`Component`] with the given hydration ID.
#[inline(always)]
pub fn new_with_id(
name: impl Into<Cow<'static, str>>,
name: impl Into<Oco<'static, str>>,
id: HydrationKey,
) -> Self {
Self::new_with_id_concrete(name.into(), id)
}

fn new_with_id_concrete(name: Cow<'static, str>, id: HydrationKey) -> Self {
fn new_with_id_concrete(name: Oco<'static, str>, id: HydrationKey) -> Self {
let markers = (
Comment::new(Cow::Owned(format!("</{name}>")), &id, true),
Comment::new(format!("</{name}>"), &id, true),
#[cfg(debug_assertions)]
Comment::new(Cow::Owned(format!("<{name}>")), &id, false),
Comment::new(format!("<{name}>"), &id, false),
);

#[cfg(all(target_arch = "wasm32", feature = "web"))]
Expand Down Expand Up @@ -236,7 +236,7 @@ where
V: IntoView,
{
id: HydrationKey,
name: Cow<'static, str>,
name: Oco<'static, str>,
children_fn: F,
}

Expand All @@ -246,7 +246,7 @@ where
V: IntoView,
{
/// Creates a new component.
pub fn new(name: impl Into<Cow<'static, str>>, f: F) -> Self {
pub fn new(name: impl Into<Oco<'static, str>>, f: F) -> Self {
Self {
id: HydrationCtx::id(),
name: name.into(),
Expand Down
6 changes: 3 additions & 3 deletions leptos_dom/src/components/dyn_child.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::{
Comment, IntoView, View,
};
use cfg_if::cfg_if;
use std::{borrow::Cow, cell::RefCell, fmt, ops::Deref, rc::Rc};
use std::{cell::RefCell, fmt, ops::Deref, rc::Rc};
cfg_if! {
if #[cfg(all(target_arch = "wasm32", feature = "web"))] {
use crate::{mount_child, prepare_to_move, unmount_child, MountKind, Mountable, Text};
Expand Down Expand Up @@ -83,9 +83,9 @@ impl Mountable for DynChildRepr {
impl DynChildRepr {
fn new_with_id(id: HydrationKey) -> Self {
let markers = (
Comment::new(Cow::Borrowed("</DynChild>"), &id, true),
Comment::new("</DynChild>", &id, true),
#[cfg(debug_assertions)]
Comment::new(Cow::Borrowed("<DynChild>"), &id, false),
Comment::new("<DynChild>", &id, false),
);

#[cfg(all(target_arch = "wasm32", feature = "web"))]
Expand Down
10 changes: 5 additions & 5 deletions leptos_dom/src/components/each.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
use crate::hydration::HydrationKey;
use crate::{hydration::HydrationCtx, Comment, CoreComponent, IntoView, View};
use leptos_reactive::{as_child_of_current_owner, Disposer};
use std::{borrow::Cow, cell::RefCell, fmt, hash::Hash, ops::Deref, rc::Rc};
use std::{cell::RefCell, fmt, hash::Hash, ops::Deref, rc::Rc};
#[cfg(all(target_arch = "wasm32", feature = "web"))]
use web::*;

Expand Down Expand Up @@ -79,9 +79,9 @@ impl Default for EachRepr {
let id = HydrationCtx::id();

let markers = (
Comment::new(Cow::Borrowed("</Each>"), &id, true),
Comment::new("</Each>", &id, true),
#[cfg(debug_assertions)]
Comment::new(Cow::Borrowed("<Each>"), &id, false),
Comment::new("<Each>", &id, false),
);

#[cfg(all(target_arch = "wasm32", feature = "web"))]
Expand Down Expand Up @@ -224,13 +224,13 @@ impl EachItem {

let markers = (
if needs_closing {
Some(Comment::new(Cow::Borrowed("</EachItem>"), &id, true))
Some(Comment::new("</EachItem>", &id, true))
} else {
None
},
#[cfg(debug_assertions)]
if needs_closing {
Some(Comment::new(Cow::Borrowed("<EachItem>"), &id, false))
Some(Comment::new("<EachItem>", &id, false))
} else {
None
},
Expand Down
11 changes: 6 additions & 5 deletions leptos_dom/src/events.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
pub mod typed;

use std::{borrow::Cow, cell::RefCell, collections::HashSet};
use leptos_reactive::Oco;
use std::{cell::RefCell, collections::HashSet};
#[cfg(all(target_arch = "wasm32", feature = "web"))]
use wasm_bindgen::{
convert::FromWasmAbi, intern, prelude::Closure, JsCast, JsValue,
UnwrapThrowExt,
};

thread_local! {
pub(crate) static GLOBAL_EVENTS: RefCell<HashSet<Cow<'static, str>>> = RefCell::new(HashSet::new());
pub(crate) static GLOBAL_EVENTS: RefCell<HashSet<Oco<'static, str>>> = RefCell::new(HashSet::new());
}

// Used in template macro
Expand Down Expand Up @@ -47,8 +48,8 @@ pub fn add_event_helper<E: crate::ev::EventDescriptor + 'static>(
#[cfg(all(target_arch = "wasm32", feature = "web"))]
pub fn add_event_listener<E>(
target: &web_sys::Element,
key: Cow<'static, str>,
event_name: Cow<'static, str>,
key: Oco<'static, str>,
event_name: Oco<'static, str>,
#[cfg(debug_assertions)] mut cb: Box<dyn FnMut(E)>,
#[cfg(not(debug_assertions))] cb: Box<dyn FnMut(E)>,
options: &Option<web_sys::AddEventListenerOptions>,
Expand Down Expand Up @@ -115,7 +116,7 @@ pub(crate) fn add_event_listener_undelegated<E>(
#[cfg(all(target_arch = "wasm32", feature = "web"))]
pub(crate) fn add_delegated_event_listener(
key: &str,
event_name: Cow<'static, str>,
event_name: Oco<'static, str>,
options: &Option<web_sys::AddEventListenerOptions>,
) {
GLOBAL_EVENTS.with(|global_events| {
Expand Down
23 changes: 12 additions & 11 deletions leptos_dom/src/events/typed.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Types for all DOM events.
use std::{borrow::Cow, marker::PhantomData};
use leptos_reactive::Oco;
use std::marker::PhantomData;
use wasm_bindgen::convert::FromWasmAbi;

/// A trait for converting types into [web_sys events](web_sys).
Expand All @@ -16,10 +17,10 @@ pub trait EventDescriptor: Clone {
const BUBBLES: bool;

/// The name of the event, such as `click` or `mouseover`.
fn name(&self) -> Cow<'static, str>;
fn name(&self) -> Oco<'static, str>;

/// The key used for event delegation.
fn event_delegation_key(&self) -> Cow<'static, str>;
fn event_delegation_key(&self) -> Oco<'static, str>;

/// Return the options for this type. This is only used when you create a [`Custom`] event
/// handler.
Expand All @@ -39,12 +40,12 @@ impl<Ev: EventDescriptor> EventDescriptor for undelegated<Ev> {
type EventType = Ev::EventType;

#[inline(always)]
fn name(&self) -> Cow<'static, str> {
fn name(&self) -> Oco<'static, str> {
self.0.name()
}

#[inline(always)]
fn event_delegation_key(&self) -> Cow<'static, str> {
fn event_delegation_key(&self) -> Oco<'static, str> {
self.0.event_delegation_key()
}

Expand All @@ -54,7 +55,7 @@ impl<Ev: EventDescriptor> EventDescriptor for undelegated<Ev> {
/// A custom event.
#[derive(Debug)]
pub struct Custom<E: FromWasmAbi = web_sys::Event> {
name: Cow<'static, str>,
name: Oco<'static, str>,
options: Option<web_sys::AddEventListenerOptions>,
_event_type: PhantomData<E>,
}
Expand All @@ -72,11 +73,11 @@ impl<E: FromWasmAbi> Clone for Custom<E> {
impl<E: FromWasmAbi> EventDescriptor for Custom<E> {
type EventType = E;

fn name(&self) -> Cow<'static, str> {
fn name(&self) -> Oco<'static, str> {
self.name.clone()
}

fn event_delegation_key(&self) -> Cow<'static, str> {
fn event_delegation_key(&self) -> Oco<'static, str> {
format!("$$${}", self.name).into()
}

Expand All @@ -92,7 +93,7 @@ impl<E: FromWasmAbi> Custom<E> {
/// Creates a custom event type that can be used within
/// [`HtmlElement::on`](crate::HtmlElement::on), for events
/// which are not covered in the [`ev`](crate::ev) module.
pub fn new(name: impl Into<Cow<'static, str>>) -> Self {
pub fn new(name: impl Into<Oco<'static, str>>) -> Self {
Self {
name: name.into(),
options: None,
Expand Down Expand Up @@ -299,12 +300,12 @@ macro_rules! generate_event_types {
type EventType = web_sys::$web_event;

#[inline(always)]
fn name(&self) -> Cow<'static, str> {
fn name(&self) -> Oco<'static, str> {
stringify!([< $($event)+ >]).into()
}

#[inline(always)]
fn event_delegation_key(&self) -> Cow<'static, str> {
fn event_delegation_key(&self) -> Oco<'static, str> {
concat!("$$$", stringify!([< $($event)+ >])).into()
}

Expand Down
Loading

0 comments on commit 793c191

Please sign in to comment.