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
use topcoat_core::context::Cx;
use topcoat_view::{NodeViewParts, PartsWriter, ViewHandle, identity::Identity};
use crate::{Js, ShardId};
pub struct ShardScope {
/// The identity of the shard invocation, which names the scope in its
/// markers and which the browser sends back with a re-render request so
/// the shard body derives the same identities as the inline render.
identity: Identity,
shard_id: ShardId,
exprs: Vec<Js>,
placeholder: ViewHandle,
}
impl ShardScope {
#[inline]
#[must_use]
pub fn new(
identity: Identity,
shard_id: ShardId,
exprs: Vec<Js>,
placeholder: ViewHandle,
) -> Self {
Self {
identity,
shard_id,
exprs,
placeholder,
}
}
}
impl NodeViewParts for ShardScope {
fn into_view_parts(self, cx: &Cx, parts: &mut PartsWriter<'_>) {
let shard_id = self.shard_id.as_str();
let identity = serde_json::to_string(&self.identity.to_string()).unwrap();
// <!-- ::topcoat::shard::start("<shard id>", "<identity>", ["<js>", ...]) -->
//
// The browser runtime derives the shard's route from its id. The
// identity is stable across renders, so a re-render of the enclosing
// content produces the same markers and the browser can keep them.
// Each parameter's JavaScript source is wrapped in a quoted string.
// The source parts are sealed with the comment context, so any `"`
// inside the source renders as `"` and the quotes stay
// unambiguous delimiters on the client.
parts.push_comment(|comment| {
comment
.push_promoted_str_unescaped(&"::topcoat::shard::start(")
.push_string_unescaped(serde_json::to_string(shard_id).unwrap())
.push_promoted_str_unescaped(&", ")
.push_string_unescaped(identity.clone())
.push_promoted_str_unescaped(&", [");
let last = self.exprs.len().saturating_sub(1);
for (index, expr) in self.exprs.iter().enumerate() {
comment.push_promoted_str_unescaped(&"\"");
expr.write(comment);
comment.push_promoted_str_unescaped(&"\"");
if index != last {
comment.push_promoted_str_unescaped(&", ");
}
}
comment.push_promoted_str_unescaped(&"])");
});
self.placeholder.into_view_parts(cx, parts);
parts.push_comment(|comment| {
comment
.push_promoted_str_unescaped(&"::topcoat::shard::end(")
.push_string_unescaped(identity)
.push_promoted_str_unescaped(&")");
});
}
}