use std::fmt;
use std::rc::Rc;
use crate::html::{Html, ImplicitClone};
use crate::utils::RcExt;
use crate::virtual_dom::{VChild, VComp, VList, VNode};
use crate::{BaseComponent, Properties};
pub type Children = ChildrenRenderer<Html>;
pub type ChildrenWithProps<CHILD> = ChildrenRenderer<VChild<CHILD>>;
pub struct ChildrenRenderer<T> {
pub(crate) children: Option<Rc<Vec<T>>>,
}
impl<T> Clone for ChildrenRenderer<T> {
fn clone(&self) -> Self {
Self {
children: self.children.clone(),
}
}
}
impl<T> ImplicitClone for ChildrenRenderer<T> {}
impl<T: PartialEq> PartialEq for ChildrenRenderer<T> {
fn eq(&self, other: &Self) -> bool {
match (self.children.as_ref(), other.children.as_ref()) {
(Some(a), Some(b)) => a == b,
(Some(a), None) => a.is_empty(),
(None, Some(b)) => b.is_empty(),
(None, None) => true,
}
}
}
impl<T> ChildrenRenderer<T>
where
T: Clone,
{
pub fn new(children: Vec<T>) -> Self {
if children.is_empty() {
Self { children: None }
} else {
Self {
children: Some(Rc::new(children)),
}
}
}
pub fn is_empty(&self) -> bool {
self.children.as_ref().map(|x| x.is_empty()).unwrap_or(true)
}
pub fn len(&self) -> usize {
self.children.as_ref().map(|x| x.len()).unwrap_or(0)
}
pub fn iter(&self) -> impl Iterator<Item = T> + '_ {
self.children.iter().flat_map(|x| x.iter()).cloned()
}
pub fn map<OUT: Default>(&self, closure: impl FnOnce(&Self) -> OUT) -> OUT {
if self.is_empty() {
Default::default()
} else {
closure(self)
}
}
}
impl<T> Default for ChildrenRenderer<T> {
fn default() -> Self {
Self {
children: Default::default(),
}
}
}
impl<T> fmt::Debug for ChildrenRenderer<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("ChildrenRenderer<_>")
}
}
impl<T: Clone> IntoIterator for ChildrenRenderer<T> {
type IntoIter = std::vec::IntoIter<Self::Item>;
type Item = T;
fn into_iter(self) -> Self::IntoIter {
if let Some(children) = self.children {
let children = RcExt::unwrap_or_clone(children);
children.into_iter()
} else {
Vec::new().into_iter()
}
}
}
impl From<ChildrenRenderer<Html>> for Html {
fn from(mut val: ChildrenRenderer<Html>) -> Self {
if let Some(children) = val.children.as_mut() {
if children.len() == 1 {
let children = Rc::make_mut(children);
if let Some(m) = children.pop() {
return m;
}
}
}
Html::VList(Rc::new(val.into()))
}
}
impl From<ChildrenRenderer<Html>> for VList {
fn from(val: ChildrenRenderer<Html>) -> Self {
VList::from(val.children)
}
}
impl<COMP> From<ChildrenRenderer<VChild<COMP>>> for ChildrenRenderer<Html>
where
COMP: BaseComponent,
{
fn from(value: ChildrenRenderer<VChild<COMP>>) -> Self {
Self::new(
value
.into_iter()
.map(VComp::from)
.map(VNode::from)
.collect::<Vec<_>>(),
)
}
}
#[derive(Debug, Properties, PartialEq)]
pub struct ChildrenProps {
#[prop_or_default]
pub children: Html,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn children_map() {
let children = Children::new(vec![]);
let res = children.map(|children| Some(children.clone()));
assert!(res.is_none());
let children = Children::new(vec![Default::default()]);
let res = children.map(|children| Some(children.clone()));
assert!(res.is_some());
}
}