mirror of
https://github.com/leptos-rs/leptos
synced 2024-09-20 06:21:57 +00:00
oco merge issues
This commit is contained in:
parent
846ff2fefb
commit
de3dd3c296
2 changed files with 93 additions and 39 deletions
|
@ -1,6 +1,5 @@
|
||||||
[package]
|
[package]
|
||||||
name = "oco_ref"
|
name = "oco_ref"
|
||||||
name = "oco_ref"
|
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
authors = ["Danik Vitek", "Greg Johnston"]
|
authors = ["Danik Vitek", "Greg Johnston"]
|
||||||
|
|
131
oco/src/lib.rs
131
oco/src/lib.rs
|
@ -1,6 +1,36 @@
|
||||||
//! This module contains the `Oco` (Owned Clones Once) smart pointer,
|
|
||||||
//! which is used to store immutable references to values.
|
//! which is used to store immutable references to values.
|
||||||
//! This is useful for storing, for example, strings.
|
//! This is useful for storing, for example, strings.
|
||||||
|
//!
|
||||||
|
//! Imagine this as an alternative to [`Cow`] with an additional, reference-counted
|
||||||
|
//! branch.
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! use oco_ref::Oco;
|
||||||
|
//! use std::sync::Arc;
|
||||||
|
//!
|
||||||
|
//! let static_str = "foo";
|
||||||
|
//! let rc_str: Arc<str> = "bar".into();
|
||||||
|
//! let owned_str: String = "baz".into();
|
||||||
|
//!
|
||||||
|
//! fn uses_oco(value: impl Into<Oco<'static, str>>) {
|
||||||
|
//! let mut value = value.into();
|
||||||
|
//!
|
||||||
|
//! // ensures that the value is either a reference, or reference-counted
|
||||||
|
//! // O(n) at worst
|
||||||
|
//! let clone1 = value.clone_inplace();
|
||||||
|
//!
|
||||||
|
//! // these subsequent clones are O(1)
|
||||||
|
//! let clone2 = value.clone();
|
||||||
|
//! let clone3 = value.clone();
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! uses_oco(static_str);
|
||||||
|
//! uses_oco(rc_str);
|
||||||
|
//! uses_oco(owned_str);
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
#![forbid(unsafe_code)]
|
||||||
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -10,24 +40,32 @@ use std::{
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
ops::{Add, Deref},
|
ops::{Add, Deref},
|
||||||
path::Path,
|
path::Path,
|
||||||
rc::Rc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// "Owned Clones Once" - a smart pointer that can be either a reference,
|
/// "Owned Clones Once": a smart pointer that can be either a reference,
|
||||||
/// an owned value, or a reference counted pointer. This is useful for
|
/// an owned value, or a reference-counted pointer. This is useful for
|
||||||
/// storing immutable values, such as strings, in a way that is cheap to
|
/// storing immutable values, such as strings, in a way that is cheap to
|
||||||
/// clone and pass around.
|
/// clone and pass around.
|
||||||
///
|
///
|
||||||
/// The `Clone` implementation is amortized `O(1)`. Cloning the [`Oco::Borrowed`]
|
/// The cost of the `Clone` implementation depends on the branch. Cloning the [`Oco::Borrowed`]
|
||||||
/// variant simply copies the references (`O(1)`). Cloning the [`Oco::Counted`]
|
/// variant simply copies the references (`O(1)`). Cloning the [`Oco::Counted`]
|
||||||
/// variant increments a reference count (`O(1)`). Cloning the [`Oco::Owned`]
|
/// variant increments a reference count (`O(1)`). Cloning the [`Oco::Owned`]
|
||||||
/// variant upgrades it to [`Oco::Counted`], which requires an `O(n)` clone of the
|
/// variant requires an `O(n)` clone of the data.
|
||||||
/// data, but all subsequent clones will be `O(1)`.
|
///
|
||||||
|
/// For an amortized `O(1)` clone, you can use [`Oco::clone_inplace()`]. Using this method,
|
||||||
|
/// [`Oco::Borrowed`] and [`Oco::Counted`] are still `O(1)`. [`Oco::Owned`] does a single `O(n)`
|
||||||
|
/// clone, but converts the object to the [`Oco::Counted`] branch, which means future clones will
|
||||||
|
/// be `O(1)`.
|
||||||
|
///
|
||||||
|
/// In general, you'll either want to call `clone_inplace()` once, before sharing the `Oco` with
|
||||||
|
/// other parts of your application (so that all future clones are `O(1)`), or simply use this as
|
||||||
|
/// if it is a [`Cow`] with an additional branch for reference-counted values.
|
||||||
pub enum Oco<'a, T: ?Sized + ToOwned + 'a> {
|
pub enum Oco<'a, T: ?Sized + ToOwned + 'a> {
|
||||||
/// A static reference to a value.
|
/// A static reference to a value.
|
||||||
Borrowed(&'a T),
|
Borrowed(&'a T),
|
||||||
/// A reference counted pointer to a value.
|
/// A reference counted pointer to a value.
|
||||||
Counted(Rc<T>),
|
Counted(Arc<T>),
|
||||||
/// An owned value.
|
/// An owned value.
|
||||||
Owned(<T as ToOwned>::Owned),
|
Owned(<T as ToOwned>::Owned),
|
||||||
}
|
}
|
||||||
|
@ -45,10 +83,10 @@ impl<'a, T: ?Sized + ToOwned> Oco<'a, T> {
|
||||||
/// Checks if the value is [`Oco::Borrowed`].
|
/// Checks if the value is [`Oco::Borrowed`].
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
/// # use std::rc::Rc;
|
/// # use std::sync::Arc;
|
||||||
/// # use leptos_reactive::oco::Oco;
|
/// # use oco_ref::Oco;
|
||||||
/// assert!(Oco::<str>::Borrowed("Hello").is_borrowed());
|
/// assert!(Oco::<str>::Borrowed("Hello").is_borrowed());
|
||||||
/// assert!(!Oco::<str>::Counted(Rc::from("Hello")).is_borrowed());
|
/// assert!(!Oco::<str>::Counted(Arc::from("Hello")).is_borrowed());
|
||||||
/// assert!(!Oco::<str>::Owned("Hello".to_string()).is_borrowed());
|
/// assert!(!Oco::<str>::Owned("Hello".to_string()).is_borrowed());
|
||||||
/// ```
|
/// ```
|
||||||
pub const fn is_borrowed(&self) -> bool {
|
pub const fn is_borrowed(&self) -> bool {
|
||||||
|
@ -58,9 +96,9 @@ impl<'a, T: ?Sized + ToOwned> Oco<'a, T> {
|
||||||
/// Checks if the value is [`Oco::Counted`].
|
/// Checks if the value is [`Oco::Counted`].
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
/// # use std::rc::Rc;
|
/// # use std::sync::Arc;
|
||||||
/// # use leptos_reactive::oco::Oco;
|
/// # use oco_ref::Oco;
|
||||||
/// assert!(Oco::<str>::Counted(Rc::from("Hello")).is_counted());
|
/// assert!(Oco::<str>::Counted(Arc::from("Hello")).is_counted());
|
||||||
/// assert!(!Oco::<str>::Borrowed("Hello").is_counted());
|
/// assert!(!Oco::<str>::Borrowed("Hello").is_counted());
|
||||||
/// assert!(!Oco::<str>::Owned("Hello".to_string()).is_counted());
|
/// assert!(!Oco::<str>::Owned("Hello".to_string()).is_counted());
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -71,11 +109,11 @@ impl<'a, T: ?Sized + ToOwned> Oco<'a, T> {
|
||||||
/// Checks if the value is [`Oco::Owned`].
|
/// Checks if the value is [`Oco::Owned`].
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
/// # use std::rc::Rc;
|
/// # use std::sync::Arc;
|
||||||
/// # use leptos_reactive::oco::Oco;
|
/// # use oco_ref::Oco;
|
||||||
/// assert!(Oco::<str>::Owned("Hello".to_string()).is_owned());
|
/// assert!(Oco::<str>::Owned("Hello".to_string()).is_owned());
|
||||||
/// assert!(!Oco::<str>::Borrowed("Hello").is_owned());
|
/// assert!(!Oco::<str>::Borrowed("Hello").is_owned());
|
||||||
/// assert!(!Oco::<str>::Counted(Rc::from("Hello")).is_owned());
|
/// assert!(!Oco::<str>::Counted(Arc::from("Hello")).is_owned());
|
||||||
/// ```
|
/// ```
|
||||||
pub const fn is_owned(&self) -> bool {
|
pub const fn is_owned(&self) -> bool {
|
||||||
matches!(self, Oco::Owned(_))
|
matches!(self, Oco::Owned(_))
|
||||||
|
@ -130,7 +168,7 @@ impl Oco<'_, str> {
|
||||||
/// Returns a `&str` slice of this [`Oco`].
|
/// Returns a `&str` slice of this [`Oco`].
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
/// # use leptos_reactive::oco::Oco;
|
/// # use oco_ref::Oco;
|
||||||
/// let oco = Oco::<str>::Borrowed("Hello");
|
/// let oco = Oco::<str>::Borrowed("Hello");
|
||||||
/// let s: &str = oco.as_str();
|
/// let s: &str = oco.as_str();
|
||||||
/// assert_eq!(s, "Hello");
|
/// assert_eq!(s, "Hello");
|
||||||
|
@ -145,7 +183,7 @@ impl Oco<'_, CStr> {
|
||||||
/// Returns a `&CStr` slice of this [`Oco`].
|
/// Returns a `&CStr` slice of this [`Oco`].
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
/// # use leptos_reactive::oco::Oco;
|
/// # use oco_ref::Oco;
|
||||||
/// use std::ffi::CStr;
|
/// use std::ffi::CStr;
|
||||||
///
|
///
|
||||||
/// let oco =
|
/// let oco =
|
||||||
|
@ -163,7 +201,7 @@ impl Oco<'_, OsStr> {
|
||||||
/// Returns a `&OsStr` slice of this [`Oco`].
|
/// Returns a `&OsStr` slice of this [`Oco`].
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
/// # use leptos_reactive::oco::Oco;
|
/// # use oco_ref::Oco;
|
||||||
/// use std::ffi::OsStr;
|
/// use std::ffi::OsStr;
|
||||||
///
|
///
|
||||||
/// let oco = Oco::<OsStr>::Borrowed(OsStr::new("Hello"));
|
/// let oco = Oco::<OsStr>::Borrowed(OsStr::new("Hello"));
|
||||||
|
@ -180,7 +218,7 @@ impl Oco<'_, Path> {
|
||||||
/// Returns a `&Path` slice of this [`Oco`].
|
/// Returns a `&Path` slice of this [`Oco`].
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
/// # use leptos_reactive::oco::Oco;
|
/// # use oco_ref::Oco;
|
||||||
/// use std::path::Path;
|
/// use std::path::Path;
|
||||||
///
|
///
|
||||||
/// let oco = Oco::<Path>::Borrowed(Path::new("Hello"));
|
/// let oco = Oco::<Path>::Borrowed(Path::new("Hello"));
|
||||||
|
@ -200,7 +238,7 @@ where
|
||||||
/// Returns a `&[T]` slice of this [`Oco`].
|
/// Returns a `&[T]` slice of this [`Oco`].
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
/// # use leptos_reactive::oco::Oco;
|
/// # use oco_ref::Oco;
|
||||||
/// let oco = Oco::<[u8]>::Borrowed(b"Hello");
|
/// let oco = Oco::<[u8]>::Borrowed(b"Hello");
|
||||||
/// let s: &[u8] = oco.as_slice();
|
/// let s: &[u8] = oco.as_slice();
|
||||||
/// assert_eq!(s, b"Hello");
|
/// assert_eq!(s, b"Hello");
|
||||||
|
@ -214,7 +252,7 @@ where
|
||||||
impl<'a, T> Clone for Oco<'a, T>
|
impl<'a, T> Clone for Oco<'a, T>
|
||||||
where
|
where
|
||||||
T: ?Sized + ToOwned + 'a,
|
T: ?Sized + ToOwned + 'a,
|
||||||
for<'b> Rc<T>: From<&'b T>,
|
for<'b> Arc<T>: From<&'b T>,
|
||||||
{
|
{
|
||||||
/// Returns a new [`Oco`] with the same value as this one.
|
/// Returns a new [`Oco`] with the same value as this one.
|
||||||
/// If the value is [`Oco::Owned`], this will convert it into
|
/// If the value is [`Oco::Owned`], this will convert it into
|
||||||
|
@ -222,7 +260,7 @@ where
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// [`String`] :
|
/// [`String`] :
|
||||||
/// ```
|
/// ```
|
||||||
/// # use leptos_reactive::oco::Oco;
|
/// # use oco_ref::Oco;
|
||||||
/// let oco = Oco::<str>::Owned("Hello".to_string());
|
/// let oco = Oco::<str>::Owned("Hello".to_string());
|
||||||
/// let oco2 = oco.clone();
|
/// let oco2 = oco.clone();
|
||||||
/// assert_eq!(oco, oco2);
|
/// assert_eq!(oco, oco2);
|
||||||
|
@ -230,7 +268,7 @@ where
|
||||||
/// ```
|
/// ```
|
||||||
/// [`Vec`] :
|
/// [`Vec`] :
|
||||||
/// ```
|
/// ```
|
||||||
/// # use leptos_reactive::oco::Oco;
|
/// # use oco_ref::Oco;
|
||||||
/// let oco = Oco::<[u8]>::Owned(b"Hello".to_vec());
|
/// let oco = Oco::<[u8]>::Owned(b"Hello".to_vec());
|
||||||
/// let oco2 = oco.clone();
|
/// let oco2 = oco.clone();
|
||||||
/// assert_eq!(oco, oco2);
|
/// assert_eq!(oco, oco2);
|
||||||
|
@ -239,8 +277,8 @@ where
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
match self {
|
match self {
|
||||||
Self::Borrowed(v) => Self::Borrowed(v),
|
Self::Borrowed(v) => Self::Borrowed(v),
|
||||||
Self::Counted(v) => Self::Counted(Rc::clone(v)),
|
Self::Counted(v) => Self::Counted(Arc::clone(v)),
|
||||||
Self::Owned(v) => Self::Counted(Rc::from(v.borrow())),
|
Self::Owned(v) => Self::Counted(Arc::from(v.borrow())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -248,13 +286,30 @@ where
|
||||||
impl<'a, T> Oco<'a, T>
|
impl<'a, T> Oco<'a, T>
|
||||||
where
|
where
|
||||||
T: ?Sized + ToOwned + 'a,
|
T: ?Sized + ToOwned + 'a,
|
||||||
for<'b> Rc<T>: From<&'b T>,
|
for<'b> Arc<T>: From<&'b T>,
|
||||||
{
|
{
|
||||||
|
/// Upgrades the value in place, by converting into [`Oco::Counted`] if it
|
||||||
|
/// was previously [`Oco::Owned`].
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// # use oco_ref::Oco;
|
||||||
|
/// let mut oco1 = Oco::<str>::Owned("Hello".to_string());
|
||||||
|
/// assert!(oco1.is_owned());
|
||||||
|
/// oco1.upgrade_inplace();
|
||||||
|
/// assert!(oco1.is_counted());
|
||||||
|
/// ```
|
||||||
|
pub fn upgrade_inplace(&mut self) {
|
||||||
|
if let Self::Owned(v) = &*self {
|
||||||
|
let rc = Arc::from(v.borrow());
|
||||||
|
*self = Self::Counted(rc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Clones the value with inplace conversion into [`Oco::Counted`] if it
|
/// Clones the value with inplace conversion into [`Oco::Counted`] if it
|
||||||
/// was previously [`Oco::Owned`].
|
/// was previously [`Oco::Owned`].
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
/// # use leptos_reactive::oco::Oco;
|
/// # use oco_ref::Oco;
|
||||||
/// let mut oco1 = Oco::<str>::Owned("Hello".to_string());
|
/// let mut oco1 = Oco::<str>::Owned("Hello".to_string());
|
||||||
/// let oco2 = oco1.clone_inplace();
|
/// let oco2 = oco1.clone_inplace();
|
||||||
/// assert_eq!(oco1, oco2);
|
/// assert_eq!(oco1, oco2);
|
||||||
|
@ -264,9 +319,9 @@ where
|
||||||
pub fn clone_inplace(&mut self) -> Self {
|
pub fn clone_inplace(&mut self) -> Self {
|
||||||
match &*self {
|
match &*self {
|
||||||
Self::Borrowed(v) => Self::Borrowed(v),
|
Self::Borrowed(v) => Self::Borrowed(v),
|
||||||
Self::Counted(v) => Self::Counted(Rc::clone(v)),
|
Self::Counted(v) => Self::Counted(Arc::clone(v)),
|
||||||
Self::Owned(v) => {
|
Self::Owned(v) => {
|
||||||
let rc = Rc::from(v.borrow());
|
let rc = Arc::from(v.borrow());
|
||||||
*self = Self::Counted(rc.clone());
|
*self = Self::Counted(rc.clone());
|
||||||
Self::Counted(rc)
|
Self::Counted(rc)
|
||||||
}
|
}
|
||||||
|
@ -378,11 +433,11 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized> From<Rc<T>> for Oco<'_, T>
|
impl<T: ?Sized> From<Arc<T>> for Oco<'_, T>
|
||||||
where
|
where
|
||||||
T: ToOwned,
|
T: ToOwned,
|
||||||
{
|
{
|
||||||
fn from(v: Rc<T>) -> Self {
|
fn from(v: Arc<T>) -> Self {
|
||||||
Oco::Counted(v)
|
Oco::Counted(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -563,7 +618,7 @@ mod tests {
|
||||||
fn debug_fmt_should_display_quotes_for_strings() {
|
fn debug_fmt_should_display_quotes_for_strings() {
|
||||||
let s: Oco<str> = Oco::Borrowed("hello");
|
let s: Oco<str> = Oco::Borrowed("hello");
|
||||||
assert_eq!(format!("{s:?}"), "\"hello\"");
|
assert_eq!(format!("{s:?}"), "\"hello\"");
|
||||||
let s: Oco<str> = Oco::Counted(Rc::from("hello"));
|
let s: Oco<str> = Oco::Counted(Arc::from("hello"));
|
||||||
assert_eq!(format!("{s:?}"), "\"hello\"");
|
assert_eq!(format!("{s:?}"), "\"hello\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -601,7 +656,7 @@ mod tests {
|
||||||
fn as_str_should_return_a_str() {
|
fn as_str_should_return_a_str() {
|
||||||
let s: Oco<str> = Oco::Borrowed("hello");
|
let s: Oco<str> = Oco::Borrowed("hello");
|
||||||
assert_eq!(s.as_str(), "hello");
|
assert_eq!(s.as_str(), "hello");
|
||||||
let s: Oco<str> = Oco::Counted(Rc::from("hello"));
|
let s: Oco<str> = Oco::Counted(Arc::from("hello"));
|
||||||
assert_eq!(s.as_str(), "hello");
|
assert_eq!(s.as_str(), "hello");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -609,7 +664,7 @@ mod tests {
|
||||||
fn as_slice_should_return_a_slice() {
|
fn as_slice_should_return_a_slice() {
|
||||||
let s: Oco<[i32]> = Oco::Borrowed([1, 2, 3].as_slice());
|
let s: Oco<[i32]> = Oco::Borrowed([1, 2, 3].as_slice());
|
||||||
assert_eq!(s.as_slice(), [1, 2, 3].as_slice());
|
assert_eq!(s.as_slice(), [1, 2, 3].as_slice());
|
||||||
let s: Oco<[i32]> = Oco::Counted(Rc::from([1, 2, 3]));
|
let s: Oco<[i32]> = Oco::Counted(Arc::from([1, 2, 3]));
|
||||||
assert_eq!(s.as_slice(), [1, 2, 3].as_slice());
|
assert_eq!(s.as_slice(), [1, 2, 3].as_slice());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -645,7 +700,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn cloned_counted_str_should_make_counted_str() {
|
fn cloned_counted_str_should_make_counted_str() {
|
||||||
let s: Oco<str> = Oco::Counted(Rc::from("hello"));
|
let s: Oco<str> = Oco::Counted(Arc::from("hello"));
|
||||||
assert!(s.clone().is_counted());
|
assert!(s.clone().is_counted());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -667,7 +722,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn cloned_inplace_counted_str_should_make_counted_str_and_remain_counted() {
|
fn cloned_inplace_counted_str_should_make_counted_str_and_remain_counted() {
|
||||||
let mut s: Oco<str> = Oco::Counted(Rc::from("hello"));
|
let mut s: Oco<str> = Oco::Counted(Arc::from("hello"));
|
||||||
assert!(s.clone_inplace().is_counted());
|
assert!(s.clone_inplace().is_counted());
|
||||||
assert!(s.is_counted());
|
assert!(s.is_counted());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue