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
pub struct Tag {
    pub name: &'static str,
    pub attrs: Vec<(&'static str, Box<dyn std::fmt::Display>)>,
    pub children: Vec<Box<dyn std::fmt::Display>>,
}

impl Tag {
    pub fn with_children<V: std::fmt::Display + 'static, T: IntoIterator<Item = V>>(
        mut self,
        children: T,
    ) -> Self {
        self.children.clear();
        self.add_children(children)
    }

    pub fn add_children<V: std::fmt::Display + 'static, T: IntoIterator<Item = V>>(
        mut self,
        children: T,
    ) -> Self {
        self.children.extend(
            children
                .into_iter()
                .map(|v| -> Box<dyn std::fmt::Display> { Box::new(v) }),
        );
        self
    }
}
impl std::fmt::Display for Tag {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        use std::fmt::Write;

        write!(f, "<{}", self.name)?;
        let mut buf = String::new();
        for (k, v) in self.attrs.iter() {
            buf.clear();
            write!(&mut buf, "{}", v)?;
            if buf.is_empty() {
                continue;
            }
            // TODO:
            //  escape properly
            //    - ashkan, Tue 06 Oct 2020 04:00:06 PM JST
            write!(f, " {}=\"{}\"", k, buf)?;
        }
        match &self.children[..] {
            [] if self.name == "script" => write!(f, "></{}>", self.name),
            [] => write!(f, "/>"),
            [v] => {
                buf.clear();
                write!(&mut buf, "{}", v)?;
                if buf.is_empty() {
                    return write!(f, "/>");
                }
                write!(f, ">{}</{}>", buf, self.name)
            }
            children => {
                // TODO:
                //  use itertools .format()?
                //    - ashkan, Wed 07 Oct 2020 11:24:22 AM JST
                write!(f, ">")?;
                for child in children {
                    write!(f, "{}", child)?;
                }
                write!(f, "</{}>", self.name)
            }
        }
    }
}

#[macro_export]
macro_rules! tag {
  ($name:literal[$($k:literal=$v:expr)+] $($children:expr)*) => {
      $crate::Tag {
          name: $name,
          attrs: vec![$(($k, Box::new($v))),*],
          children: vec![$(Box::new($children)),*],
      }
  };
  ($name:literal $($children:expr)*) => {
      $crate::Tag {
          name: $name,
          attrs: vec![],
          children: vec![$(Box::new($children)),*],
      }
  };
}

#[macro_export]
macro_rules! _maybe {
    ($v:literal) => {
        Some($v)
    };
    ($v:expr) => {
        $v
    };
}

#[macro_export]
macro_rules! class {
  ($($v:expr),*) => {
      (&[$($crate::_maybe!($v)),*]).into_iter().flatten().join(" ")
  };
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
        assert_eq!(2 + 2, 4);
    }
}