1use crate::prelude::*;
6
7#[derive(Clone,Serialize,Default,Deserialize,Hash,Eq,Ord,PartialEq,PartialOrd)]
8#[serde(transparent)]
9pub struct Html(String);
10
11impl Debug for HtmlStr {
12 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
13 const MAX: usize = 23;
14 if self.len() < MAX {
15 write!(f, "<{}>", &self.0)
16 } else {
17 let lim = (MAX-3 ..).into_iter()
18 .find(|&i| self.0.is_char_boundary(i)).unwrap();
19 write!(f, "<{}>...", &self.0[0..lim])
20 }
21 }
22}
23
24impl AsRef<HtmlStr> for Html {
25 fn as_ref(&self) -> &HtmlStr { HtmlStr::from_html_str(&self.0) }
26}
27impl AsRef<HtmlStr> for HtmlStr {
28 fn as_ref(&self) -> &HtmlStr { self }
29}
30impl AsRef<HtmlStr> for HtmlLit {
31 fn as_ref(&self) -> &HtmlStr { HtmlStr::from_html_str(self.0) }
32}
33impl Deref for Html {
34 type Target = HtmlStr;
35 fn deref(&self) -> &HtmlStr { HtmlStr::from_html_str(&self.0) }
36}
37impl Deref for HtmlLit {
38 type Target = HtmlStr;
39 fn deref(&self) -> &HtmlStr { HtmlStr::from_html_str(self.0) }
40}
41
42impl Debug for Html {
43 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
44 Debug::fmt(self.as_ref(), f)
45 }
46}
47
48#[derive(Hash,Eq,Ord,PartialEq,PartialOrd)]
49#[repr(transparent)]
50pub struct HtmlStr(str);
51
52#[derive(Hash,Eq,Ord,PartialEq,PartialOrd)]
53#[derive(Serialize,Deserialize)]
54#[serde(transparent)]
55pub struct HtmlLit(&'static str);
56
57impl Default for &'static HtmlStr {
58 fn default() -> Self { HtmlStr::from_html_str("") }
59}
60
61impl From<HtmlLit> for &'static HtmlStr {
62 fn from(l: HtmlLit) -> &'static HtmlStr { HtmlStr::from_html_str(l.0) }
63}
64
65pub trait HtmlFormat<'e> {
66 type Encoded: Display;
67 fn html_format<'f: 'e>(&'f self) -> Self::Encoded;
68}
69
70impl From<HtmlLit> for Html {
71 fn from(l: HtmlLit) -> Html { Html(l.0.into()) }
72}
73impl From<&HtmlStr> for Html {
74 fn from(l: &HtmlStr) -> Html { Html(l.0.into()) }
75}
76
77impl<'e, T> HtmlFormat<'e> for &'e T where T: HtmlFormat<'e> + ?Sized {
78 type Encoded = T::Encoded;
79 fn html_format<'f: 'e>(&'f self) -> Self::Encoded {
80 <T as HtmlFormat>::html_format(self)
81 }
82}
83
84#[derive(Debug,Copy,Clone,Ord,PartialOrd,Eq,PartialEq,Hash)]
85pub struct IsHtmlFormatted<T:Display>(pub T);
86impl<'e, T:Display+'e> HtmlFormat<'e> for IsHtmlFormatted<T> {
87 type Encoded = &'e T;
88 fn html_format<'f: 'e>(&'f self) -> Self::Encoded { &self.0 }
89}
90
91impl Html {
92 pub fn new() -> Self { default() }
93 pub fn from_txt(s: &str) -> Self {
94 Html(htmlescape::encode_minimal(s))
95 }
96
97 pub fn from_html_string(s: String) -> Self { Html(s) }
98 pub fn as_html_string_mut(&mut self) -> &mut String { &mut self.0 }
99 pub fn into_html_string(self) -> String { self.0 }
100
101 pub const fn lit(s: &'static str) -> HtmlLit { HtmlLit(s) }
102}
103
104impl HtmlStr {
105 pub fn from_html_str<'s>(s: &'s str) -> &'s Self {
106 let s = unsafe { mem::transmute::<&'s str, &'s HtmlStr>(s) };
107 s
108 }
109 pub fn len(&self) -> usize { self.0.len() }
110 pub fn as_html_str(&self) -> &str { &self.0 }
111}
112
113#[ext(pub, name=HtmlIteratorExt, supertraits=Iterator)]
114impl<T:Iterator> T {
115 fn hjoin<'i,'j, I,J>(self, j: &'j J) -> Html
116 where
117 Self: Iterator<Item=&'i I>,
118 I: AsRef<HtmlStr> + 'i,
119 J: AsRef<HtmlStr>,
120 {
121 let j: &HtmlStr = j.as_ref();
122 Html::from_html_string(
123 izip!(
124 iter::once("").chain(iter::repeat(j.as_html_str())),
125 self.map(|h| h.as_ref().as_html_str()),
126 )
127 .map(|(a,b)| iter::once(a).chain(iter::once(b)))
128 .flatten()
129 .collect::<String>()
130 )
131 }
132}
133
134impl Borrow<HtmlStr> for Html {
135 fn borrow<'b>(&'b self) -> &'b HtmlStr {
136 HtmlStr::from_html_str(&self.0)
137 }
138}
139impl Borrow<HtmlStr> for HtmlLit {
140 fn borrow<'b>(&'b self) -> &'static HtmlStr {
141 HtmlStr::from_html_str(self.0)
142 }
143}
144
145impl ToOwned for HtmlStr {
146 type Owned = Html;
147 fn to_owned(&self) -> Html { Html(self.0.to_owned()) }
148}
149
150#[ext(pub, name=HtmlFormatRef)]
151impl<'e, T: HtmlFormat<'e>> T {
152 fn to_html(&'e self) -> Html { hformat!("{}", *self) }
153}
154
155impl<'e> HtmlFormat<'e> for Html {
156 type Encoded = &'e str;
157 fn html_format<'f: 'e>(&'f self) -> Self::Encoded { &self.0 }
158}
159impl<'e> HtmlFormat<'e> for HtmlStr {
160 type Encoded = &'e str;
161 fn html_format<'f: 'e>(&'f self) -> Self::Encoded { &self.0 }
162}
163impl<'e> HtmlFormat<'e> for HtmlLit {
164 type Encoded = &'static str;
165 fn html_format<'f: 'e>(&'f self) -> Self::Encoded { self.0 }
166}
167impl<'e> HtmlFormat<'e> for str {
168 type Encoded = String;
169 fn html_format<'f: 'e>(&'f self) -> Self::Encoded {
170 htmlescape::encode_minimal(self)
171 }
172}
173impl<'e> HtmlFormat<'e> for String {
174 type Encoded = String;
175 fn html_format<'f: 'e>(&'f self) -> Self::Encoded {
176 htmlescape::encode_minimal(self)
177 }
178}
179
180hformat_as_display!{ usize u8 u16 u32 u64
181 isize i8 i16 i32 i64
182 f32 f64 }
183
184#[macro_export]
185macro_rules! hformat_as_display {
186 ( $( $t:ty )* ) => {
187 $(
188 impl<'e> $crate::html::HtmlFormat<'e> for $t {
189 type Encoded = &'e $t;
190 fn html_format<'f: 'e>(&'f self) -> Self::Encoded { self }
191 }
192 )*
193 }
194}
195
196#[macro_export]
197macro_rules! hformat {
198 ( $f:tt $(,$( $v:expr )?)* ) => {
199 Html::from_html_string(
200 format!(
201 $f $(,$(
202 $crate::html::HtmlFormat::html_format(&$v)
203 )?)*
204 )
205 )
206 }
207}
208
209#[macro_export]
210macro_rules! hwrite {
211 ( $o:expr, $f:tt $(,$( $v:expr )?)* ) => {
212 write!(
213 $crate::html::Html::as_html_string_mut($o),
214 $f $(,$(
215 $crate::html::HtmlFormat::html_format(&$v)
216 )?)*
217 )
218 }
219}