Skip to main content

seam_server/
server.rs

1/* src/server/core/rust/src/server.rs */
2
3use std::collections::BTreeMap;
4use std::time::Duration;
5
6use crate::build_loader::RpcHashMap;
7use crate::channel::{ChannelDef, ChannelMeta};
8use crate::context::{ContextConfig, ContextFieldDef};
9use crate::page::{I18nConfig, PageDef};
10use crate::procedure::{ProcedureDef, StreamDef, SubscriptionDef, UploadDef};
11use crate::resolve::ResolveStrategy;
12use crate::validation::ValidationMode;
13
14/// Transport reliability configuration shared across all backends.
15pub struct TransportConfig {
16	pub heartbeat_interval: Duration,
17	pub sse_idle_timeout: Duration,
18	pub pong_timeout: Duration,
19}
20
21impl Default for TransportConfig {
22	fn default() -> Self {
23		Self {
24			heartbeat_interval: Duration::from_secs(21),
25			sse_idle_timeout: Duration::from_secs(30),
26			pong_timeout: Duration::from_secs(5),
27		}
28	}
29}
30
31/// Framework-agnostic parts extracted from `SeamServer`.
32/// Adapter crates consume this to build framework-specific routers.
33pub struct SeamParts {
34	pub procedures: Vec<ProcedureDef>,
35	pub subscriptions: Vec<SubscriptionDef>,
36	pub streams: Vec<StreamDef>,
37	pub uploads: Vec<UploadDef>,
38	pub pages: Vec<PageDef>,
39	pub rpc_hash_map: Option<RpcHashMap>,
40	pub i18n_config: Option<I18nConfig>,
41	pub strategies: Vec<Box<dyn ResolveStrategy>>,
42	pub channel_metas: BTreeMap<String, ChannelMeta>,
43	pub context_config: ContextConfig,
44	pub validation_mode: ValidationMode,
45	pub transport_config: TransportConfig,
46}
47
48impl SeamParts {
49	pub fn has_url_prefix(&self) -> bool {
50		self.strategies.iter().any(|s| s.kind() == "url_prefix")
51	}
52}
53
54pub struct SeamServer {
55	procedures: Vec<ProcedureDef>,
56	subscriptions: Vec<SubscriptionDef>,
57	streams: Vec<StreamDef>,
58	uploads: Vec<UploadDef>,
59	channels: Vec<ChannelDef>,
60	pages: Vec<PageDef>,
61	rpc_hash_map: Option<RpcHashMap>,
62	i18n_config: Option<I18nConfig>,
63	strategies: Vec<Box<dyn ResolveStrategy>>,
64	context_config: ContextConfig,
65	validation_mode: ValidationMode,
66	transport_config: TransportConfig,
67}
68
69impl SeamServer {
70	pub fn new() -> Self {
71		Self {
72			procedures: Vec::new(),
73			subscriptions: Vec::new(),
74			streams: Vec::new(),
75			uploads: Vec::new(),
76			channels: Vec::new(),
77			pages: Vec::new(),
78			rpc_hash_map: None,
79			i18n_config: None,
80			strategies: Vec::new(),
81			context_config: ContextConfig::new(),
82			validation_mode: ValidationMode::Dev,
83			transport_config: TransportConfig::default(),
84		}
85	}
86
87	pub fn procedure(mut self, proc: ProcedureDef) -> Self {
88		self.procedures.push(proc);
89		self
90	}
91
92	pub fn subscription(mut self, sub: SubscriptionDef) -> Self {
93		self.subscriptions.push(sub);
94		self
95	}
96
97	pub fn stream(mut self, stream: StreamDef) -> Self {
98		self.streams.push(stream);
99		self
100	}
101
102	pub fn upload(mut self, upload: UploadDef) -> Self {
103		self.uploads.push(upload);
104		self
105	}
106
107	pub fn channel(mut self, channel: ChannelDef) -> Self {
108		self.channels.push(channel);
109		self
110	}
111
112	pub fn page(mut self, page: PageDef) -> Self {
113		self.pages.push(page);
114		self
115	}
116
117	pub fn rpc_hash_map(mut self, map: RpcHashMap) -> Self {
118		self.rpc_hash_map = Some(map);
119		self
120	}
121
122	pub fn i18n_config(mut self, config: I18nConfig) -> Self {
123		self.i18n_config = Some(config);
124		self
125	}
126
127	pub fn resolve_strategies(mut self, strategies: Vec<Box<dyn ResolveStrategy>>) -> Self {
128		self.strategies = strategies;
129		self
130	}
131
132	pub fn context(mut self, key: &str, field: ContextFieldDef) -> Self {
133		self.context_config.insert(key.to_string(), field);
134		self
135	}
136
137	pub fn validation_mode(mut self, mode: ValidationMode) -> Self {
138		self.validation_mode = mode;
139		self
140	}
141
142	pub fn transport_config(mut self, config: TransportConfig) -> Self {
143		self.transport_config = config;
144		self
145	}
146
147	/// Consume the builder, returning framework-agnostic parts for an adapter.
148	/// Channels are expanded into their Level 0 primitives (commands + subscriptions).
149	pub fn into_parts(self) -> SeamParts {
150		let mut procedures = self.procedures;
151		let mut subscriptions = self.subscriptions;
152		let mut channel_metas = BTreeMap::new();
153
154		for channel in self.channels {
155			let name = channel.name.clone();
156			let (procs, subs, meta) = channel.expand();
157			procedures.extend(procs);
158			subscriptions.extend(subs);
159			channel_metas.insert(name, meta);
160		}
161
162		SeamParts {
163			procedures,
164			subscriptions,
165			streams: self.streams,
166			uploads: self.uploads,
167			pages: self.pages,
168			rpc_hash_map: self.rpc_hash_map,
169			i18n_config: self.i18n_config,
170			strategies: self.strategies,
171			channel_metas,
172			context_config: self.context_config,
173			validation_mode: self.validation_mode,
174			transport_config: self.transport_config,
175		}
176	}
177}
178
179impl Default for SeamServer {
180	fn default() -> Self {
181		Self::new()
182	}
183}