Skip to content

Commit

Permalink
fix: reimplement Oco cloning (#1749)
Browse files Browse the repository at this point in the history
  • Loading branch information
DanikVitek authored Sep 22, 2023
1 parent d651400 commit baa5ea8
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 101 deletions.
4 changes: 2 additions & 2 deletions leptos_dom/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -929,15 +929,15 @@ thread_local! {
/// This is cached as a thread-local variable, so calling `window()` multiple times
/// requires only one call out to JavaScript.
pub fn window() -> web_sys::Window {
WINDOW.with(|window| window.clone())
WINDOW.with(Clone::clone)
}

/// Returns the [`Document`](https://developer.mozilla.org/en-US/docs/Web/API/Document).
///
/// This is cached as a thread-local variable, so calling `document()` multiple times
/// requires only one call out to JavaScript.
pub fn document() -> web_sys::Document {
DOCUMENT.with(|document| document.clone())
DOCUMENT.with(Clone::clone)
}

/// Returns true if running on the server (SSR).
Expand Down
2 changes: 1 addition & 1 deletion leptos_dom/src/ssr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ pub fn render_to_stream_with_prefix_undisposed_with_context_and_block_replacemen
.push(async move { (fragment_id, data.out_of_order.await) });
} else {
fragments.push(Box::pin(async move {
(fragment_id.clone(), data.out_of_order.await)
(fragment_id, data.out_of_order.await)
})
as Pin<Box<dyn Future<Output = (String, String)>>>);
}
Expand Down
146 changes: 56 additions & 90 deletions leptos_reactive/src/oco.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ pub enum Oco<'a, T: ?Sized + ToOwned + 'a> {
Owned(<T as ToOwned>::Owned),
}

impl<T: ?Sized + ToOwned> Oco<'_, T> {
impl<'a, T: ?Sized + ToOwned> Oco<'a, T> {
/// Converts the value into an owned value.
pub fn into_owned(self) -> <T as ToOwned>::Owned {
match self {
Expand Down Expand Up @@ -211,122 +211,65 @@ where
}
}

// ------------------------------------------------------------------------------------------------------
// Cloning (has to be implemented manually because of the `Rc<T>: From<&<T as ToOwned>::Owned>` bound)
// ------------------------------------------------------------------------------------------------------

impl Clone for Oco<'_, str> {
impl<'a, T> Clone for Oco<'a, T>
where
T: ?Sized + ToOwned + 'a,
for<'b> Rc<T>: From<&'b T>,
{
/// Returns a new [`Oco`] with the same value as this one.
/// If the value is [`Oco::Owned`], this will convert it into
/// [`Oco::Counted`], so that the next clone will be O(1).
/// # Examples
/// [`String`] :
/// ```
/// # use leptos_reactive::oco::Oco;
/// let oco = Oco::<str>::Owned("Hello".to_string());
/// let oco2 = oco.clone();
/// assert_eq!(oco, oco2);
/// assert!(oco2.is_counted());
/// ```
fn clone(&self) -> Self {
match self {
Oco::Borrowed(v) => Oco::Borrowed(v),
Oco::Counted(v) => Oco::Counted(v.clone()),
Oco::Owned(v) => Oco::Counted(Rc::<str>::from(v.as_str())),
}
}
}

impl Clone for Oco<'_, CStr> {
/// Returns a new [`Oco`] with the same value as this one.
/// If the value is [`Oco::Owned`], this will convert it into
/// [`Oco::Counted`], so that the next clone will be O(1).
/// # Examples
/// ```
/// # use leptos_reactive::oco::Oco;
/// use std::ffi::CStr;
///
/// let oco = Oco::<CStr>::Owned(
/// CStr::from_bytes_with_nul(b"Hello\0").unwrap().to_owned(),
/// );
/// let oco2 = oco.clone();
/// assert_eq!(oco, oco2);
/// assert!(oco2.is_counted());
/// ```
fn clone(&self) -> Self {
match self {
Oco::Borrowed(v) => Oco::Borrowed(v),
Oco::Counted(v) => Oco::Counted(v.clone()),
Oco::Owned(v) => Oco::Counted(Rc::<CStr>::from(v.as_c_str())),
}
}
}

impl Clone for Oco<'_, OsStr> {
/// Returns a new [`Oco`] with the same value as this one.
/// If the value is [`Oco::Owned`], this will convert it into
/// [`Oco::Counted`], so that the next clone will be O(1).
/// # Examples
/// ```
/// # use leptos_reactive::oco::Oco;
/// use std::ffi::OsStr;
///
/// let oco = Oco::<OsStr>::Owned(OsStr::new("Hello").to_owned());
/// let oco2 = oco.clone();
/// assert_eq!(oco, oco2);
/// assert!(oco2.is_counted());
/// ```
fn clone(&self) -> Self {
match self {
Oco::Borrowed(v) => Oco::Borrowed(v),
Oco::Counted(v) => Oco::Counted(v.clone()),
Oco::Owned(v) => Oco::Counted(Rc::<OsStr>::from(v.as_os_str())),
}
}
}

impl Clone for Oco<'_, Path> {
/// Returns a new [`Oco`] with the same value as this one.
/// If the value is [`Oco::Owned`], this will convert it into
/// [`Oco::Counted`], so that the next clone will be O(1).
/// # Examples
/// [`Vec`] :
/// ```
/// # use leptos_reactive::oco::Oco;
/// use std::path::Path;
///
/// let oco = Oco::<Path>::Owned(Path::new("Hello").to_owned());
/// let oco = Oco::<[u8]>::Owned(b"Hello".to_vec());
/// let oco2 = oco.clone();
/// assert_eq!(oco, oco2);
/// assert!(oco2.is_counted());
/// ```
fn clone(&self) -> Self {
match self {
Oco::Borrowed(v) => Oco::Borrowed(v),
Oco::Counted(v) => Oco::Counted(v.clone()),
Oco::Owned(v) => Oco::Counted(Rc::<Path>::from(v.as_path())),
Self::Borrowed(v) => Self::Borrowed(v),
Self::Counted(v) => Self::Counted(Rc::clone(v)),
Self::Owned(v) => Self::Counted(Rc::from(v.borrow())),
}
}
}

impl<T: Clone> Clone for Oco<'_, [T]>
impl<'a, T> Oco<'a, T>
where
[T]: ToOwned<Owned = Vec<T>>,
T: ?Sized + ToOwned + 'a,
for<'b> Rc<T>: From<&'b T>,
{
/// Returns a new [`Oco`] with the same value as this one.
/// If the value is [`Oco::Owned`], this will convert it into
/// [`Oco::Counted`], so that the next clone will be O(1).
/// Clones the value with inplace conversion into [`Oco::Counted`] if it
/// was previously [`Oco::Owned`].
/// # Examples
/// ```
/// # use leptos_reactive::oco::Oco;
/// let oco = Oco::<[i32]>::Owned(vec![1, 2, 3]);
/// let oco2 = oco.clone();
/// assert_eq!(oco, oco2);
/// let mut oco1 = Oco::<str>::Owned("Hello".to_string());
/// let oco2 = oco1.clone_inplace();
/// assert_eq!(oco1, oco2);
/// assert!(oco1.is_counted());
/// assert!(oco2.is_counted());
/// ```
fn clone(&self) -> Self {
match self {
Oco::Borrowed(v) => Oco::Borrowed(v),
Oco::Counted(v) => Oco::Counted(v.clone()),
Oco::Owned(v) => Oco::Counted(Rc::<[T]>::from(v.as_slice())),
pub fn clone_inplace(&mut self) -> Self {
match &*self {
Self::Borrowed(v) => Self::Borrowed(v),
Self::Counted(v) => Self::Counted(Rc::clone(v)),
Self::Owned(v) => {
let rc = Rc::from(v.borrow());
*self = Self::Counted(rc.clone());
Self::Counted(rc)
}
}
}
}
Expand Down Expand Up @@ -689,23 +632,46 @@ mod tests {
}

#[test]
fn cloned_owned_string_should_become_counted_str() {
fn cloned_owned_string_should_make_counted_str() {
let s: Oco<str> = Oco::Owned(String::from("hello"));
assert!(s.clone().is_counted());
}

#[test]
fn cloned_borrowed_str_should_remain_borrowed_str() {
fn cloned_borrowed_str_should_make_borrowed_str() {
let s: Oco<str> = Oco::Borrowed("hello");
assert!(s.clone().is_borrowed());
}

#[test]
fn cloned_counted_str_should_remain_counted_str() {
fn cloned_counted_str_should_make_counted_str() {
let s: Oco<str> = Oco::Counted(Rc::from("hello"));
assert!(s.clone().is_counted());
}

#[test]
fn cloned_inplace_owned_string_should_make_counted_str_and_become_counted()
{
let mut s: Oco<str> = Oco::Owned(String::from("hello"));
assert!(s.clone_inplace().is_counted());
assert!(s.is_counted());
}

#[test]
fn cloned_inplace_borrowed_str_should_make_borrowed_str_and_remain_borrowed(
) {
let mut s: Oco<str> = Oco::Borrowed("hello");
assert!(s.clone_inplace().is_borrowed());
assert!(s.is_borrowed());
}

#[test]
fn cloned_inplace_counted_str_should_make_counted_str_and_remain_counted() {
let mut s: Oco<str> = Oco::Counted(Rc::from("hello"));
assert!(s.clone_inplace().is_counted());
assert!(s.is_counted());
}

#[test]
fn serialization_works() {
let s = serde_json::to_string(&Oco::Borrowed("foo"))
Expand Down
4 changes: 2 additions & 2 deletions meta/src/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,11 @@ pub fn Link(
) -> impl IntoView {
let meta = use_head();
let next_id = meta.tags.get_next_id();
let id: Oco<'static, str> =
let mut id: Oco<'static, str> =
id.unwrap_or_else(|| format!("leptos-link-{}", next_id.0).into());

let builder_el = leptos::leptos_dom::html::as_meta_tag({
let id = id.clone();
let id = id.clone_inplace();
move || {
leptos::leptos_dom::html::link()
.attr("id", id)
Expand Down
4 changes: 2 additions & 2 deletions meta/src/script.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,11 @@ pub fn Script(
) -> impl IntoView {
let meta = use_head();
let next_id = meta.tags.get_next_id();
let id: Oco<'static, str> =
let mut id: Oco<'static, str> =
id.unwrap_or_else(|| format!("leptos-link-{}", next_id.0).into());

let builder_el = leptos::leptos_dom::html::as_meta_tag({
let id = id.clone();
let id = id.clone_inplace();
move || {
leptos::leptos_dom::html::script()
.attr("id", id)
Expand Down
4 changes: 2 additions & 2 deletions meta/src/style.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ pub fn Style(
) -> impl IntoView {
let meta = use_head();
let next_id = meta.tags.get_next_id();
let id: Oco<'static, str> =
let mut id: Oco<'static, str> =
id.unwrap_or_else(|| format!("leptos-link-{}", next_id.0).into());

let builder_el = leptos::leptos_dom::html::as_meta_tag({
let id = id.clone();
let id = id.clone_inplace();
move || {
leptos::leptos_dom::html::style()
.attr("id", id)
Expand Down
4 changes: 2 additions & 2 deletions router/src/hooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,13 @@ pub fn create_query_signal<T>(
where
T: FromStr + ToString + PartialEq,
{
let key = key.into();
let mut key: Oco<'static, str> = key.into();
let query_map = use_query_map();
let navigate = use_navigate();
let location = use_location();

let get = create_memo({
let key = key.clone();
let key = key.clone_inplace();
move |_| {
query_map
.with(|map| map.get(&key).and_then(|value| value.parse().ok()))
Expand Down

0 comments on commit baa5ea8

Please sign in to comment.