pagetop 0.5.0

Un entorno de desarrollo para crear soluciones web modulares, extensibles y configurables.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
use crate::core::component::{Component, Context};
use crate::html::{html, Markup};
use crate::{builder_fn, AutoDefault, UniqueId};

use parking_lot::Mutex;

pub use parking_lot::MutexGuard as ComponentGuard;

use std::fmt;
use std::vec::IntoIter;

/// Representa un componente hijo encapsulado para su uso en una lista [`Children`].
#[derive(AutoDefault)]
pub struct Child(Option<Mutex<Box<dyn Component>>>);

impl Clone for Child {
    fn clone(&self) -> Self {
        Child(self.0.as_ref().map(|m| Mutex::new(m.lock().clone_box())))
    }
}

impl fmt::Debug for Child {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match &self.0 {
            None => write!(f, "Child(None)"),
            Some(c) => write!(f, "Child({})", c.lock().name()),
        }
    }
}

impl Child {
    /// Crea un nuevo `Child` a partir de un componente.
    pub fn with(component: impl Component) -> Self {
        Child(Some(Mutex::new(Box::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 {
        self.0 = component.map(|c| Mutex::new(Box::new(c) as Box<dyn Component>));
        self
    }

    // **< Child GETTERS >**************************************************************************

    /// Devuelve el identificador del componente, si existe y está definido.
    #[inline]
    pub fn id(&self) -> Option<String> {
        self.0.as_ref().and_then(|c| c.lock().id())
    }

    // **< Child RENDER >***************************************************************************

    /// Renderiza el componente con el contexto proporcionado.
    pub fn render(&self, cx: &mut Context) -> Markup {
        self.0.as_ref().map_or(html! {}, |c| c.lock().render(cx))
    }

    // **< Child HELPERS >**************************************************************************

    /// Devuelve el [`UniqueId`] del tipo del componente, si existe.
    #[inline]
    fn type_id(&self) -> Option<UniqueId> {
        self.0.as_ref().map(|c| c.lock().type_id())
    }
}

impl<C: Component + 'static> From<Embed<C>> for Child {
    /// Convierte un [`Embed<C>`] en un [`Child`], consumiendo el componente tipado.
    ///
    /// Útil cuando se tiene un [`Embed`] y se necesita añadirlo a una lista [`Children`]:
    ///
    /// ```rust,ignore
    /// children.with_child(Child::from(my_embed));
    /// // o equivalentemente:
    /// children.with_child(my_embed.into());
    /// ```
    fn from(embed: Embed<C>) -> Self {
        if let Some(m) = embed.0 {
            Child(Some(Mutex::new(
                Box::new(m.into_inner()) as Box<dyn Component>
            )))
        } else {
            Child(None)
        }
    }
}

impl<T: Component + 'static> From<T> for Child {
    #[inline]
    fn from(component: T) -> Self {
        Child::with(component)
    }
}

impl<T: Component + 'static> From<T> for ChildOp {
    /// Convierte un componente en [`ChildOp::Add`], permitiendo pasar componentes directamente a
    /// métodos como [`Children::with_child`] sin envolverlos explícitamente.
    #[inline]
    fn from(component: T) -> Self {
        ChildOp::Add(Child::with(component))
    }
}

impl From<Child> for ChildOp {
    /// Convierte un [`Child`] en [`ChildOp::Add`].
    #[inline]
    fn from(child: Child) -> Self {
        ChildOp::Add(child)
    }
}

// *************************************************************************************************

/// Contenedor tipado para un *único* componente de un tipo concreto conocido.
///
/// A diferencia de [`Child`], que encapsula cualquier componente como `dyn Component`, `Embed`
/// mantiene el tipo concreto `C` y permite acceder directamente a sus métodos específicos a través
/// de [`get()`](Embed::get).
///
/// Se usa habitualmente para incrustar un componente dentro de otro cuando no se necesita una lista
/// completa de hijos ([`Children`]), sino un único componente tipado en un campo concreto.
#[derive(AutoDefault)]
pub struct Embed<C: Component>(Option<Mutex<C>>);

impl<C: Component + Clone> Clone for Embed<C> {
    fn clone(&self) -> Self {
        Embed(self.0.as_ref().map(|m| Mutex::new(m.lock().clone())))
    }
}

impl<C: Component> fmt::Debug for Embed<C> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match &self.0 {
            None => write!(f, "Embed(None)"),
            Some(c) => write!(f, "Embed({})", c.lock().name()),
        }
    }
}

impl<C: Component> Embed<C> {
    /// Crea un nuevo `Embed` a partir de un componente.
    pub fn with(component: C) -> Self {
        Embed(Some(Mutex::new(component)))
    }

    // **< Embed BUILDER >**************************************************************************

    /// Establece un componente nuevo, o lo vacía.
    ///
    /// Si se proporciona `Some(component)`, se encapsula como [`Embed`]; y si es `None`, se limpia.
    #[builder_fn]
    pub fn with_component(mut self, component: Option<C>) -> Self {
        self.0 = component.map(Mutex::new);
        self
    }

    // **< Embed GETTERS >**************************************************************************

    /// Devuelve el identificador del componente, si existe y está definido.
    #[inline]
    pub fn id(&self) -> Option<String> {
        self.0.as_ref().and_then(|c| c.lock().id())
    }

    /// Devuelve un acceso al componente incrustado.
    ///
    /// - Devuelve `Some(ComponentGuard<C>)` si existe el componente, o `None` si está vacío.
    /// - El acceso es **exclusivo**: mientras el *guard* esté activo, no habrá otros accesos.
    /// - Se recomienda mantener el *guard* **el menor tiempo posible** para evitar bloqueos
    ///   innecesarios.
    /// - Para modificar el componente, declara el *guard* como `mut`:
    ///   `if let Some(mut c) = embed.get() { c.alter_title(...); }`.
    ///
    /// # Ejemplo
    ///
    /// ```rust
    /// # use pagetop::prelude::*;
    /// let embed = Embed::with(Html::with(|_| html! { "Prueba" }));
    /// {
    ///     if let Some(component) = embed.get() {
    ///         assert_eq!(component.name(), "Html");
    ///     }
    /// }; // El *guard* se libera aquí, antes del *drop* de `embed`.
    ///
    /// let embed = Embed::with(Block::new().with_title(L10n::n("Title")));
    /// {
    ///     if let Some(mut component) = embed.get() {
    ///         component.alter_title(L10n::n("New Title"));
    ///     }
    /// }; // El *guard* se libera aquí, antes del *drop* de `embed`.
    /// ```
    pub fn get(&self) -> Option<ComponentGuard<'_, C>> {
        self.0.as_ref().map(|m| m.lock())
    }

    // **< Embed RENDER >***************************************************************************

    /// Renderiza el componente con el contexto proporcionado.
    pub fn render(&self, cx: &mut Context) -> Markup {
        self.0.as_ref().map_or(html! {}, |c| c.lock().render(cx))
    }
}

// *************************************************************************************************

/// Operaciones para componentes hijo [`Child`] en una lista [`Children`].
pub enum ChildOp {
    /// Añade un hijo al final de la lista.
    Add(Child),
    /// Añade un hijo solo si la lista está vacía.
    AddIfEmpty(Child),
    /// Añade varios hijos al final de la lista, en el orden recibido.
    AddMany(Vec<Child>),
    /// Inserta un hijo justo después del componente con el `id` dado, o al final si no existe.
    InsertAfterId(&'static str, Child),
    /// Inserta un hijo justo antes del componente con el `id` dado, o al principio si no existe.
    InsertBeforeId(&'static str, Child),
    /// Inserta un hijo al principio de la lista.
    Prepend(Child),
    /// Inserta varios hijos al principio de la lista, manteniendo el orden recibido.
    PrependMany(Vec<Child>),
    /// Elimina el primer hijo con el `id` dado.
    RemoveById(&'static str),
    /// Sustituye el primer hijo con el `id` dado por otro componente.
    ReplaceById(&'static str, Child),
    /// Vacía la lista eliminando todos los hijos.
    Reset,
}

/// Lista ordenada de componentes hijo ([`Child`]) mantenida por un componente padre.
///
/// Permite añadir, modificar, renderizar y consultar componentes hijo en orden de inserción, con
/// soporte para operaciones avanzadas como inserción relativa o reemplazo por identificador a
/// través de [`ChildOp`].
///
/// Los tipos que completan este sistema son:
///
/// - [`Child`]: representa un componente hijo encapsulado dentro de la lista. Almacena cualquier
///   componente sin necesidad de conocer su tipo concreto.
/// - [`Embed<C>`]: contenedor tipado para un *único* componente de tipo `C`. Preferible a
///   `Children` cuando el padre solo necesita un componente y quiere acceso directo a los métodos
///   de `C`.
/// - [`ChildOp`]: operaciones disponibles sobre la lista. Cuando se necesita algo más que añadir al
///   final, se construye la variante adecuada y se pasa a [`with_child`](Self::with_child).
/// - [`ComponentGuard`]: devuelto por [`Embed::get`] para garantizar acceso exclusivo al componente
///   tipado. Mientras está activo bloquea cualquier otro acceso por lo que conviene liberarlo
///   cuanto antes.
///
/// # Conversiones implícitas
///
/// Cualquier componente implementa `Into<ChildOp>` (equivalente a `ChildOp::Add`) e `Into<Child>`.
/// Gracias a esto, [`with_child`](Self::with_child) acepta un componente directamente o cualquier
/// variante de [`ChildOp`]:
///
/// ```rust,ignore
/// // Añadir al final de la lista (implícito):
/// children.with_child(MiComponente::new());
///
/// // Operación explícita:
/// children.with_child(ChildOp::Prepend(MiComponente::new().into()));
/// ```
#[derive(AutoDefault, Clone, Debug)]
pub struct Children(Vec<Child>);

impl Children {
    /// Crea una lista vacía.
    pub fn new() -> Self {
        Self::default()
    }

    /// Crea una lista con un componente hijo inicial.
    pub fn with(child: Child) -> Self {
        Self::default().with_child(child)
    }

    // **< Children BUILDER >***********************************************************************

    /// Añade un componente hijo o aplica una operación [`ChildOp`] sobre la lista.
    #[builder_fn]
    pub fn with_child(mut self, op: impl Into<ChildOp>) -> Self {
        match op.into() {
            ChildOp::Add(any) => self.add(any),
            ChildOp::AddIfEmpty(any) => self.add_if_empty(any),
            ChildOp::AddMany(many) => self.add_many(many),
            ChildOp::InsertAfterId(id, any) => self.insert_after_id(id, any),
            ChildOp::InsertBeforeId(id, any) => self.insert_before_id(id, any),
            ChildOp::Prepend(any) => self.prepend(any),
            ChildOp::PrependMany(many) => self.prepend_many(many),
            ChildOp::RemoveById(id) => self.remove_by_id(id),
            ChildOp::ReplaceById(id, any) => self.replace_by_id(id, any),
            ChildOp::Reset => self.reset(),
        }
    }

    /// Añade un componente hijo al final de la lista.
    #[inline]
    pub(crate) fn add(&mut self, child: Child) -> &mut Self {
        self.0.push(child);
        self
    }

    /// Añade un componente hijo en la lista sólo si está vacía.
    #[inline]
    pub(crate) fn add_if_empty(&mut self, child: Child) -> &mut Self {
        if self.0.is_empty() {
            self.0.push(child);
        }
        self
    }

    // **< Children GETTERS >***********************************************************************

    /// Devuelve el número de componentes hijo de la lista.
    pub fn len(&self) -> usize {
        self.0.len()
    }

    /// Indica si la lista está vacía.
    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }

    /// Devuelve el primer componente hijo con el identificador indicado, si existe.
    pub fn get_by_id(&self, id: impl AsRef<str>) -> Option<&Child> {
        let id = Some(id.as_ref());
        self.0.iter().find(|c| c.id().as_deref() == id)
    }

    /// Devuelve un iterador sobre los componentes hijo con el identificador indicado.
    pub fn iter_by_id<'a>(&'a self, id: &'a str) -> impl Iterator<Item = &'a Child> + 'a {
        self.0.iter().filter(move |c| c.id().as_deref() == Some(id))
    }

    /// Devuelve un iterador sobre los componentes hijo con el identificador de tipo ([`UniqueId`])
    /// indicado.
    pub fn iter_by_type_id(&self, type_id: UniqueId) -> impl Iterator<Item = &Child> {
        self.0.iter().filter(move |c| c.type_id() == Some(type_id))
    }

    // **< Children RENDER >************************************************************************

    /// Renderiza todos los componentes hijo, en orden.
    pub fn render(&self, cx: &mut Context) -> Markup {
        html! {
            @for c in &self.0 {
                (c.render(cx))
            }
        }
    }

    // **< Children HELPERS >***********************************************************************

    /// Añade más de un componente hijo al final de la lista (en el orden recibido).
    #[inline]
    fn add_many<I>(&mut self, iter: I) -> &mut Self
    where
        I: IntoIterator<Item = Child>,
    {
        self.0.extend(iter);
        self
    }

    /// Inserta un hijo después del componente con el `id` dado, o al final si no se encuentra.
    #[inline]
    fn insert_after_id(&mut self, id: impl AsRef<str>, child: Child) -> &mut Self {
        let id = Some(id.as_ref());
        match self.0.iter().position(|c| c.id().as_deref() == id) {
            Some(index) => self.0.insert(index + 1, child),
            _ => self.0.push(child),
        };
        self
    }

    /// Inserta un hijo antes del componente con el `id` dado, o al principio si no se encuentra.
    #[inline]
    fn insert_before_id(&mut self, id: impl AsRef<str>, child: Child) -> &mut Self {
        let id = Some(id.as_ref());
        match self.0.iter().position(|c| c.id().as_deref() == id) {
            Some(index) => self.0.insert(index, child),
            _ => self.0.insert(0, child),
        };
        self
    }

    /// Inserta un hijo al principio de la lista.
    #[inline]
    fn prepend(&mut self, child: Child) -> &mut Self {
        self.0.insert(0, child);
        self
    }

    /// Inserta más de un componente hijo al principio de la lista (manteniendo el orden recibido).
    #[inline]
    fn prepend_many<I>(&mut self, iter: I) -> &mut Self
    where
        I: IntoIterator<Item = Child>,
    {
        let buf: Vec<Child> = iter.into_iter().collect();
        self.0.splice(0..0, buf);
        self
    }

    /// Elimina el primer hijo con el `id` dado.
    #[inline]
    fn remove_by_id(&mut self, id: impl AsRef<str>) -> &mut Self {
        let id = Some(id.as_ref());
        if let Some(index) = self.0.iter().position(|c| c.id().as_deref() == id) {
            self.0.remove(index);
        }
        self
    }

    /// Sustituye el primer hijo con el `id` dado por otro componente.
    #[inline]
    fn replace_by_id(&mut self, id: impl AsRef<str>, child: Child) -> &mut Self {
        let id = Some(id.as_ref());
        for c in &mut self.0 {
            if c.id().as_deref() == id {
                *c = child;
                break;
            }
        }
        self
    }

    /// Elimina todos los componentes hijo de la lista.
    #[inline]
    fn reset(&mut self) -> &mut Self {
        self.0.clear();
        self
    }
}

impl IntoIterator for Children {
    type Item = Child;
    type IntoIter = IntoIter<Child>;

    /// Consume la estructura `Children`, devolviendo un iterador que consume los elementos.
    ///
    /// # Ejemplo
    ///
    /// ```rust,ignore
    /// let children = Children::new().with(child1).with(child2);
    /// for child in children {
    ///     println!("{:?}", child.id());
    /// }
    /// ```
    fn into_iter(self) -> Self::IntoIter {
        self.0.into_iter()
    }
}

impl<'a> IntoIterator for &'a Children {
    type Item = &'a Child;
    type IntoIter = std::slice::Iter<'a, Child>;

    /// Itera sobre una referencia inmutable de `Children`, devolviendo un iterador de referencia.
    ///
    /// # Ejemplo
    ///
    /// ```rust,ignore
    /// let children = Children::new().with(child1).with(child2);
    /// for child in &children {
    ///     println!("{:?}", child.id());
    /// }
    /// ```
    fn into_iter(self) -> Self::IntoIter {
        self.0.iter()
    }
}

impl<'a> IntoIterator for &'a mut Children {
    type Item = &'a mut Child;
    type IntoIter = std::slice::IterMut<'a, Child>;

    /// Itera sobre una referencia mutable de `Children`, devolviendo un iterador mutable.
    ///
    /// # Ejemplo
    ///
    /// ```rust,ignore
    /// let mut children = Children::new().with(child1).with(child2);
    /// for child in &mut children {
    ///     child.render(&mut context);
    /// }
    /// ```
    fn into_iter(self) -> Self::IntoIter {
        self.0.iter_mut()
    }
}