sim_lib_expr_tree_server/
web.rs1use std::sync::Arc;
4
5use sim_kernel::{Cx, Error, Expr, Result, Symbol};
6use sim_lib_server::ServerAddress;
7use sim_lib_view::{LensRegistry, SurfaceCaps, surface};
8use sim_lib_view_expr_tree::{
9 expression_tree_surface_codec_symbol, register_expression_tree_surface_codec,
10};
11use sim_lib_web_bridge::{RemoteTransport, SceneUpdate, Session};
12use sim_web_shell::{LiveSurface, LiveSurfaceFactory};
13
14type ContextFactory = dyn Fn() -> Result<Cx> + Send + Sync;
15
16pub struct ExpressionTreeWebSurface {
21 session: Session<RemoteTransport>,
22 registry: LensRegistry,
23 cx: Cx,
24 caps: SurfaceCaps,
25 browser_resource: String,
26 authoritative_resource: Symbol,
27}
28
29impl ExpressionTreeWebSurface {
30 fn connect(
31 endpoint: String,
32 address: ServerAddress,
33 offered_codecs: Vec<Symbol>,
34 caps: SurfaceCaps,
35 browser_resource: String,
36 authoritative_resource: Symbol,
37 mut cx: Cx,
38 ) -> Result<Self> {
39 let mut transport = RemoteTransport::local_server_address(endpoint, address)
40 .with_offered_codecs(offered_codecs);
41 transport.connect(&mut cx)?;
42 let mut registry = LensRegistry::new();
43 register_expression_tree_surface_codec(&mut registry);
44 Ok(Self {
45 session: Session::new(transport),
46 registry,
47 cx,
48 caps,
49 browser_resource,
50 authoritative_resource,
51 })
52 }
53
54 fn checked_resource(&self, requested: &str) -> Result<Symbol> {
55 if requested == self.browser_resource {
56 Ok(self.authoritative_resource.clone())
57 } else {
58 Err(Error::HostError(format!(
59 "browser resource {requested:?} is outside this expression-tree surface"
60 )))
61 }
62 }
63}
64
65impl LiveSurface for ExpressionTreeWebSurface {
66 fn open(&mut self, resource: &str, pane: &str) -> Result<Expr> {
67 let resource = self.checked_resource(resource)?;
68 self.session.open_codec(
69 &mut self.cx,
70 &self.registry,
71 Symbol::new(pane),
72 resource,
73 expression_tree_surface_codec_symbol(),
74 self.caps.clone(),
75 )
76 }
77
78 fn submit(&mut self, pane: &str, intent: &Expr) -> Result<Vec<SceneUpdate>> {
79 self.session.submit_intent_at_rendered_revision(
80 &mut self.cx,
81 &self.registry,
82 &Symbol::new(pane),
83 intent,
84 )?;
85 self.session.pump(&mut self.cx, &self.registry)
86 }
87}
88
89pub struct ExpressionTreeWebSurfaceFactory {
97 endpoint: String,
98 address: ServerAddress,
99 offered_codecs: Vec<Symbol>,
100 caps: SurfaceCaps,
101 browser_resource: String,
102 authoritative_resource: Symbol,
103 context_factory: Arc<ContextFactory>,
104}
105
106impl ExpressionTreeWebSurfaceFactory {
107 pub fn new(
109 endpoint: impl Into<String>,
110 address: ServerAddress,
111 browser_resource: impl Into<String>,
112 authoritative_resource: Symbol,
113 context_factory: impl Fn() -> Result<Cx> + Send + Sync + 'static,
114 ) -> Self {
115 Self {
116 endpoint: endpoint.into(),
117 address,
118 offered_codecs: vec![Symbol::qualified("codec", "lisp")],
119 caps: surface::preset("webui").expect("webui is a known surface preset"),
120 browser_resource: browser_resource.into(),
121 authoritative_resource,
122 context_factory: Arc::new(context_factory),
123 }
124 }
125
126 pub fn with_offered_codecs(mut self, offered_codecs: Vec<Symbol>) -> Self {
128 self.offered_codecs = offered_codecs;
129 self
130 }
131
132 pub fn with_surface_caps(mut self, caps: SurfaceCaps) -> Self {
134 self.caps = caps;
135 self
136 }
137}
138
139impl LiveSurfaceFactory for ExpressionTreeWebSurfaceFactory {
140 fn create(&self) -> Result<Box<dyn LiveSurface>> {
141 let cx = (self.context_factory)()?;
142 ExpressionTreeWebSurface::connect(
143 self.endpoint.clone(),
144 self.address.clone(),
145 self.offered_codecs.clone(),
146 self.caps.clone(),
147 self.browser_resource.clone(),
148 self.authoritative_resource.clone(),
149 cx,
150 )
151 .map(|surface| Box::new(surface) as Box<dyn LiveSurface>)
152 }
153}