topcoat_runtime/
reactive_scope.rs1use serde::Serialize;
2use topcoat_core::context::Cx;
3use topcoat_view::{NodeViewParts, PartsWriter, View, ViewPart};
4use uuid::Uuid;
5
6use crate::{SHARD_ROUTE_PREFIX, ShardId};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
9#[serde(transparent)]
10pub struct ReactiveScopeId(Uuid);
11
12impl ReactiveScopeId {
13 #[inline]
14 #[must_use]
15 pub fn new() -> Self {
16 Self(Uuid::new_v4())
17 }
18}
19
20impl Default for ReactiveScopeId {
21 #[inline]
22 fn default() -> Self {
23 Self::new()
24 }
25}
26
27pub struct ReactiveScope {
28 id: ReactiveScopeId,
29 shard_id: ShardId,
30 exprs: Vec<ViewPart>,
31 placeholder: View,
32}
33
34impl ReactiveScope {
35 #[inline]
36 #[must_use]
37 pub fn new(shard_id: ShardId, exprs: Vec<ViewPart>, placeholder: View) -> Self {
38 Self {
39 id: ReactiveScopeId::new(),
40 shard_id,
41 exprs,
42 placeholder,
43 }
44 }
45}
46
47impl NodeViewParts for ReactiveScope {
48 fn into_view_parts(self, cx: &Cx, parts: &mut PartsWriter<'_>) {
49 let shard_id = self.shard_id.as_str();
50
51 parts.push_str_unescaped("<!-- ::topcoat::scope::start(");
58 parts.push_str_unescaped(serde_json::to_string(&self.id).unwrap());
59 parts.push_str_unescaped(", ");
60 parts.push_str_unescaped(
61 serde_json::to_string(&format!("{SHARD_ROUTE_PREFIX}/{shard_id}")).unwrap(),
62 );
63 parts.push_str_unescaped(", [");
64 let last = self.exprs.len().saturating_sub(1);
65 for (index, expr) in self.exprs.into_iter().enumerate() {
66 parts.push_str_unescaped("\"");
67 parts.push_part(expr);
68 parts.push_str_unescaped("\"");
69 if index != last {
70 parts.push_str_unescaped(", ");
71 }
72 }
73 parts.push_str_unescaped("]) -->");
74 self.placeholder.into_view_parts(cx, parts);
75 parts.push_str_unescaped("<!-- ::topcoat::scope::end(");
76 parts.push_str_unescaped(serde_json::to_string(&self.id).unwrap());
77 parts.push_str_unescaped(") -->");
78 }
79}