🔥 Elimina definitivamente TypedOpt por Typed

This commit is contained in:
Manuel Cillero 2025-09-27 21:18:54 +02:00
parent 476ea7de7f
commit f5290b477f
4 changed files with 56 additions and 90 deletions

View file

@ -7,6 +7,3 @@ mod children;
pub use children::Children; pub use children::Children;
pub use children::{Child, ChildOp}; pub use children::{Child, ChildOp};
pub use children::{Typed, TypedOp}; pub use children::{Typed, TypedOp};
mod optional;
pub use optional::TypedOpt;

View file

@ -1,6 +1,6 @@
use crate::core::component::Component; use crate::core::component::Component;
use crate::html::{html, Context, Markup}; use crate::html::{html, Context, Markup};
use crate::{builder_fn, UniqueId}; use crate::{builder_fn, AutoDefault, UniqueId};
use parking_lot::RwLock; use parking_lot::RwLock;
@ -11,76 +11,105 @@ use std::vec::IntoIter;
/// ///
/// Esta estructura permite manipular y renderizar un componente que implemente [`Component`], y /// Esta estructura permite manipular y renderizar un componente que implemente [`Component`], y
/// habilita acceso concurrente mediante [`Arc<RwLock<_>>`]. /// habilita acceso concurrente mediante [`Arc<RwLock<_>>`].
#[derive(Clone)] #[derive(AutoDefault, Clone)]
pub struct Child(Arc<RwLock<dyn Component>>); pub struct Child(Option<Arc<RwLock<dyn Component>>>);
impl Child { impl Child {
/// Crea un nuevo `Child` a partir de un componente. /// Crea un nuevo `Child` a partir de un componente.
pub fn with(component: impl Component) -> Self { pub fn with(component: impl Component) -> Self {
Child(Arc::new(RwLock::new(component))) Child(Some(Arc::new(RwLock::new(component))))
}
// Child BUILDER *******************************************************************************
/// Establece un componente nuevo, o lo vacía.
///
/// Si se proporciona `Some(component)`, se encapsula como [`Child`]; y si es `None`, se limpia.
#[builder_fn]
pub fn with_component<C: Component>(mut self, component: Option<C>) -> Self {
if let Some(c) = component {
self.0 = Some(Arc::new(RwLock::new(c)));
} else {
self.0 = None;
}
self
} }
// Child GETTERS ******************************************************************************* // Child GETTERS *******************************************************************************
/// Devuelve el identificador del componente, si está definido. /// Devuelve el identificador del componente, si existe y está definido.
#[inline]
pub fn id(&self) -> Option<String> { pub fn id(&self) -> Option<String> {
self.0.read().id() self.0.as_ref().and_then(|c| c.read().id())
} }
// Child RENDER ******************************************************************************** // Child RENDER ********************************************************************************
/// Renderiza el componente con el contexto proporcionado. /// Renderiza el componente con el contexto proporcionado.
pub fn render(&self, cx: &mut Context) -> Markup { pub fn render(&self, cx: &mut Context) -> Markup {
self.0.write().render(cx) self.0.as_ref().map_or(html! {}, |c| c.write().render(cx))
} }
// Child HELPERS ******************************************************************************* // Child HELPERS *******************************************************************************
// Devuelve el [`UniqueId`] del tipo del componente. // Devuelve el [`UniqueId`] del tipo del componente, si existe.
fn type_id(&self) -> UniqueId { #[inline]
self.0.read().type_id() fn type_id(&self) -> Option<UniqueId> {
self.0.as_ref().map(|c| c.read().type_id())
} }
} }
// ************************************************************************************************* // *************************************************************************************************
/// Variante tipada de [`Child`] para evitar conversiones durante el uso. /// Variante tipada de [`Child`] para evitar conversiones de tipo durante el uso.
/// ///
/// Esta estructura permite manipular y renderizar un componente concreto que implemente /// Esta estructura permite manipular y renderizar un componente concreto que implemente
/// [`Component`], y habilita acceso concurrente mediante [`Arc<RwLock<_>>`]. /// [`Component`], y habilita acceso concurrente mediante [`Arc<RwLock<_>>`].
pub struct Typed<C: Component>(Arc<RwLock<C>>); #[derive(AutoDefault, Clone)]
pub struct Typed<C: Component>(Option<Arc<RwLock<C>>>);
impl<C: Component> Clone for Typed<C> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<C: Component> Typed<C> { impl<C: Component> Typed<C> {
/// Crea un nuevo `Typed` a partir de un componente. /// Crea un nuevo `Typed` a partir de un componente.
pub fn with(component: C) -> Self { pub fn with(component: C) -> Self {
Typed(Arc::new(RwLock::new(component))) Typed(Some(Arc::new(RwLock::new(component))))
}
// Typed BUILDER *******************************************************************************
/// Establece un componente nuevo, o lo vacía.
///
/// Si se proporciona `Some(component)`, se encapsula como [`Typed`]; y si es `None`, se limpia.
#[builder_fn]
pub fn with_component(mut self, component: Option<C>) -> Self {
self.0 = component.map(|c| Arc::new(RwLock::new(c)));
self
} }
// Typed GETTERS ******************************************************************************* // Typed GETTERS *******************************************************************************
/// Devuelve el identificador del componente, si está definido. /// Devuelve el identificador del componente, si existe y está definido.
#[inline]
pub fn id(&self) -> Option<String> { pub fn id(&self) -> Option<String> {
self.0.read().id() self.0.as_ref().and_then(|c| c.read().id())
} }
// Typed RENDER ******************************************************************************** // Typed RENDER ********************************************************************************
/// Renderiza el componente con el contexto proporcionado. /// Renderiza el componente con el contexto proporcionado.
pub fn render(&self, cx: &mut Context) -> Markup { pub fn render(&self, cx: &mut Context) -> Markup {
self.0.write().render(cx) self.0.as_ref().map_or(html! {}, |c| c.write().render(cx))
} }
// Typed HELPERS ******************************************************************************* // Typed HELPERS *******************************************************************************
// Convierte el componente tipado en un [`Child`]. // Convierte el componente tipado en un [`Child`].
#[inline]
fn into_child(self) -> Child { fn into_child(self) -> Child {
Child(self.0.clone()) if let Some(c) = &self.0 {
Child(Some(c.clone()))
} else {
Child(None)
}
} }
} }
@ -201,7 +230,7 @@ impl Children {
/// Devuelve un iterador sobre los componentes hijo con el identificador de tipo ([`UniqueId`]) /// Devuelve un iterador sobre los componentes hijo con el identificador de tipo ([`UniqueId`])
/// indicado. /// indicado.
pub fn iter_by_type_id(&self, type_id: UniqueId) -> impl Iterator<Item = &Child> { pub fn iter_by_type_id(&self, type_id: UniqueId) -> impl Iterator<Item = &Child> {
self.0.iter().filter(move |&c| c.type_id() == type_id) self.0.iter().filter(move |&c| c.type_id() == Some(type_id))
} }
// Children RENDER ***************************************************************************** // Children RENDER *****************************************************************************

View file

@ -1,59 +0,0 @@
use crate::core::component::{Component, Typed};
use crate::html::{html, Context, Markup};
use crate::{builder_fn, AutoDefault};
/// Contenedor **opcional** para un componente [`Typed`].
///
/// Un `TypedOpt` actúa como un contenedor para incluir o no un subcomponente tipado. Internamente
/// encapsula `Option<Typed<C>>`, pero ofrece una API más sencilla para construir estructuras
/// jerárquicas o contenidas de componentes.
///
/// # Ejemplo
///
/// ```rust
/// use pagetop::prelude::*;
///
/// let icon = Icon::default();
/// let icon = TypedOpt::new(icon);
/// assert!(icon.get().is_some());
/// ```
#[derive(AutoDefault)]
pub struct TypedOpt<C: Component>(Option<Typed<C>>);
impl<C: Component> TypedOpt<C> {
/// Crea un nuevo [`TypedOpt`].
///
/// El componente se envuelve automáticamente en un [`Typed`] y se almacena.
pub fn new(component: C) -> Self {
TypedOpt(Some(Typed::with(component)))
}
// TypedOpt BUILDER ****************************************************************************
/// Establece un componente nuevo, o lo vacía.
///
/// Si se proporciona `Some(component)`, se guarda como [`Typed`]; y si es `None`, se limpia.
#[builder_fn]
pub fn with_component(mut self, component: Option<C>) -> Self {
self.0 = component.map(Typed::with);
self
}
// TypedOpt GETTERS ****************************************************************************
/// Devuelve un clon (incrementa el contador `Arc`) de [`Typed<C>`], si existe.
pub fn get(&self) -> Option<Typed<C>> {
self.0.clone()
}
// TypedOpt RENDER *****************************************************************************
/// Renderiza el componente, si existe.
pub fn render(&self, cx: &mut Context) -> Markup {
if let Some(component) = &self.0 {
component.render(cx)
} else {
html! {}
}
}
}

View file

@ -50,14 +50,13 @@ pub type OptionClasses = AttrClasses;
use crate::{core, AutoDefault}; use crate::{core, AutoDefault};
/// **Obsoleto desde la versión 0.4.0**: usar [`TypedOpt`](crate::core::component::TypedOpt) en su /// **Obsoleto desde la versión 0.4.0**: usar [`Typed`](crate::core::component::Typed) en su lugar.
/// lugar.
#[deprecated( #[deprecated(
since = "0.4.0", since = "0.4.0",
note = "Use `pagetop::core::component::TypedOpt` instead" note = "Use `pagetop::core::component::Typed` instead"
)] )]
#[allow(type_alias_bounds)] #[allow(type_alias_bounds)]
pub type OptionComponent<C: core::component::Component> = core::component::TypedOpt<C>; pub type OptionComponent<C: core::component::Component> = core::component::Typed<C>;
/// Prepara contenido HTML para su conversión a [`Markup`]. /// Prepara contenido HTML para su conversión a [`Markup`].
/// ///