1use std::collections::BTreeMap;
2pub mod power;
3pub mod stream;
4const STATIC_TAGS: &str = "<turbo-stream><template></template></turbo-stream>";
5#[inline]
6pub(crate) fn turbo_stream_action<'a, S: AsRef<str>>(
7 action: &'a str,
8 attributes: &mut BTreeMap<&str, &'a str>,
9 content: S,
10) -> String {
11 attributes.insert("action", action);
12 turbo_stream_tag(attributes, content)
13}
14
15#[inline]
16pub(crate) fn turbo_stream_target<'a, S: AsRef<str>>(
17 action: &'a str,
18 target: &'a str,
19 attributes: &mut BTreeMap<&str, &'a str>,
20 content: S,
21) -> String {
22 attributes.insert("target", target);
23 turbo_stream_action(action, attributes, content)
24}
25
26#[inline]
27pub(crate) fn turbo_stream_target_all<'a, S: AsRef<str>>(
28 action: &'a str,
29 targets: &'a str,
30 attributes: &mut BTreeMap<&str, &'a str>,
31 content: S,
32) -> String {
33 attributes.insert("targets", targets);
34 turbo_stream_action(action, attributes, content)
35}
36
37#[inline]
38pub(crate) fn turbo_stream_tag<S: AsRef<str>>(
39 attributes: &mut BTreeMap<&str, &str>,
40 content: S,
41) -> String {
42 let content = content.as_ref();
43 let attributes: BTreeMap<&str, String> = attributes
44 .iter_mut()
45 .map(|(key, value)| {
46 let value = html_escape::encode_double_quoted_attribute(*value);
47 (*key, value.to_string())
48 })
49 .collect();
50 let attr_len: usize = attributes
51 .iter()
52 .map(|(key, value)| 4 + key.len() + value.len())
53 .sum();
54 let capacity = STATIC_TAGS.len() + attr_len + content.len();
55 let mut output = String::with_capacity(capacity);
56 output.push_str(r#"<turbo-stream"#);
57 if attributes.is_empty() {
58 output.push('>');
59 } else {
60 for (key, value) in attributes {
61 output.push(' ');
62 output.push_str(key);
63 output.push_str(r#"=""#);
64 output.push_str(value.as_str());
65 output.push('"');
66 }
67 output.push('>');
68 }
69 output.push_str("<template>");
70 output.push_str(content);
71 output.push_str("</template></turbo-stream>");
72 output
73}
74
75#[cfg(test)]
76mod tests {
77 use std::collections::BTreeMap;
78
79 #[test]
80 fn custom() {
81 let expected =
82 r#"<turbo-stream action="custom" target="target"><template></template></turbo-stream>"#;
83 assert_eq!(
84 expected,
85 super::turbo_stream_target("custom", "target", &mut Default::default(), "")
86 );
87 }
88
89 #[test]
90 fn additional_attributes() {
91 let expected = r#"<turbo-stream action="custom" foo="bar" hello="world" target="target"><template></template></turbo-stream>"#;
92 let mut attributes = BTreeMap::from([("hello", "world"), ("foo", "bar")]);
93 assert_eq!(
94 expected,
95 super::turbo_stream_target("custom", "target", &mut attributes, "")
96 );
97 }
98}