ordinary_config/app/template/
mod.rs1use crate::{Check, HttpCache, HttpCors, HttpCsp, StoredCache};
6use ordinary_types::Kind;
7use serde::{Deserialize, Serialize};
8
9#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
11#[cfg_attr(feature = "docs", derive(schemars::JsonSchema))]
12#[derive(Deserialize, Serialize, Debug, Clone)]
13pub struct TemplateField {
14 pub name: String,
16 pub kind: Kind,
18 pub value: serde_json::Value,
20}
21
22#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
24#[cfg_attr(feature = "docs", derive(schemars::JsonSchema))]
25#[derive(Deserialize, Serialize, Debug, Clone)]
26pub enum QueryExpression {
27 Gte,
28 Gt,
29 Lte,
30 Lt,
31 Eq,
32 BeginsWith,
33}
34
35#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
37#[cfg_attr(feature = "docs", derive(schemars::JsonSchema))]
38#[derive(Deserialize, Serialize, Debug, Clone)]
39pub enum TemplateRefFieldBind {
40 Token {
42 field: String,
49 #[serde(skip_serializing_if = "Option::is_none")]
51 #[serde(default)]
52 expression: Option<QueryExpression>,
53 },
54 Segment {
56 name: String,
58 #[serde(skip_serializing_if = "Option::is_none")]
60 #[serde(default)]
61 expression: Option<QueryExpression>,
62 },
63 Param {
65 key: String,
67 #[serde(skip_serializing_if = "Option::is_none")]
69 #[serde(default)]
70 expression: Option<QueryExpression>,
71 },
72}
73
74#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
77#[cfg_attr(feature = "docs", derive(schemars::JsonSchema))]
78#[derive(Deserialize, Serialize, Debug, Clone, Default)]
79pub struct TemplateRefField {
80 pub idx: u8,
83 pub name: String,
85 #[serde(skip_serializing_if = "Option::is_none")]
92 #[serde(default)]
93 pub bind: Option<TemplateRefFieldBind>,
94 #[cfg_attr(feature = "utoipa", schema(no_recursion))]
96 #[serde(skip_serializing_if = "Option::is_none")]
97 #[serde(default)]
98 pub fields: Option<Vec<TemplateRefField>>,
99}
100
101#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
105#[cfg_attr(feature = "docs", derive(schemars::JsonSchema))]
106#[derive(Deserialize, Serialize, Debug, Clone, Default)]
107pub struct TemplateRef {
108 pub idx: u8,
112 pub name: String,
114 #[serde(skip_serializing_if = "Option::is_none")]
118 #[serde(default)]
119 pub all: Option<String>,
120 #[serde(skip_serializing_if = "Option::is_none")]
132 #[serde(default)]
133 pub predicate: Option<Vec<(String, TemplateRefFieldBind)>>,
134 #[serde(skip_serializing_if = "Option::is_none")]
138 #[serde(default)]
139 pub fields: Option<Vec<TemplateRefField>>,
140}
141
142#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
145#[cfg_attr(feature = "docs", derive(schemars::JsonSchema))]
146#[derive(Deserialize, Serialize, Debug, Clone)]
147pub struct TemplateFlagRef {
148 pub idx: u8,
152 pub name: String,
154}
155
156#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
157#[cfg_attr(feature = "docs", derive(schemars::JsonSchema))]
158#[derive(Deserialize, Serialize, Debug, Clone)]
159pub struct TemplateCache {
160 #[serde(skip_serializing_if = "Option::is_none")]
161 #[serde(default)]
162 pub stored: Option<StoredCache>,
163
164 #[serde(skip_serializing_if = "Option::is_none")]
165 #[serde(default)]
166 pub http: Option<HttpCache>,
167}
168
169#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
170#[cfg_attr(feature = "docs", derive(schemars::JsonSchema))]
171#[derive(Deserialize, Serialize, Debug, Clone, Default)]
172pub enum TemplateFfiVersion {
173 V1,
174 #[default]
175 V2,
176}
177
178#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
180#[cfg_attr(feature = "docs", derive(schemars::JsonSchema))]
181#[derive(Deserialize, Serialize, Debug, Clone, Default)]
182pub enum TemplateFfiSerialization {
183 #[default]
184 FlexBufferVector,
185 Json,
187}
188
189#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
190#[cfg_attr(feature = "docs", derive(schemars::JsonSchema))]
191#[derive(Deserialize, Serialize, Debug, Clone, Default)]
192pub struct TemplateFfi {
193 pub version: TemplateFfiVersion,
194 pub serialization: TemplateFfiSerialization,
195}
196
197#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
198#[cfg_attr(feature = "docs", derive(schemars::JsonSchema))]
199#[derive(Deserialize, Serialize, Debug, Clone, Default)]
200pub enum BindgenLang {
201 #[default]
202 #[serde(rename = "rust")]
203 Rust,
204}
205
206#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
207#[cfg_attr(feature = "docs", derive(schemars::JsonSchema))]
208#[derive(Deserialize, Serialize, Debug, Clone, Default)]
209pub enum ManagedTemplate {
210 #[default]
213 #[serde(rename = "v2-rust-askama")]
214 V2RustAskama,
215}
216
217impl ManagedTemplate {
218 #[must_use]
219 pub fn as_str(&self) -> &'static str {
220 match self {
221 Self::V2RustAskama => "v2-rust-askama",
222 }
223 }
224}
225
226#[cfg(feature = "cli")]
227impl clap::ValueEnum for ManagedTemplate {
228 fn value_variants<'a>() -> &'a [Self] {
229 &[Self::V2RustAskama]
230 }
231
232 fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
233 match self {
234 Self::V2RustAskama => Some(clap::builder::PossibleValue::new("v2-rust-askama")),
235 }
236 }
237}
238
239#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
241#[cfg_attr(feature = "docs", derive(schemars::JsonSchema))]
242#[derive(Deserialize, Serialize, Debug, Clone, Default)]
243pub struct TemplateConfig {
244 #[serde(skip_serializing_if = "Option::is_none")]
246 #[serde(default)]
247 pub ffi: Option<TemplateFfi>,
248 #[serde(skip_serializing_if = "Option::is_none")]
250 #[serde(default, rename = "ref")]
251 pub r#ref: Option<String>,
252 #[serde(skip_serializing_if = "Option::is_none")]
254 #[serde(default)]
255 pub idx: Option<u8>,
256 #[serde(skip_serializing_if = "Option::is_none")]
258 #[serde(default)]
259 pub name: Option<String>,
260 #[serde(skip_serializing_if = "Option::is_none")]
265 #[serde(default)]
266 pub managed: Option<ManagedTemplate>,
267 #[serde(skip_serializing_if = "Option::is_none")]
271 #[serde(default)]
272 pub mime: Option<String>,
273 #[serde(skip_serializing_if = "Option::is_none")]
276 #[serde(default)]
277 pub minify: Option<bool>,
278
279 #[serde(skip_serializing_if = "Option::is_none")]
282 #[serde(default)]
283 pub build: Option<Vec<Vec<String>>>,
284
285 #[serde(skip_serializing_if = "Option::is_none")]
287 #[serde(default)]
288 pub bin: Option<String>,
289
290 #[serde(skip_serializing_if = "Option::is_none")]
307 #[serde(default)]
308 pub bindgen: Option<BindgenLang>,
309
310 #[serde(skip_serializing_if = "Option::is_none")]
312 #[serde(default)]
313 pub path: Option<String>,
314 #[serde(skip_serializing_if = "Option::is_none")]
317 #[serde(default)]
318 pub route: Option<String>,
319 #[serde(skip_serializing_if = "Option::is_none")]
321 #[serde(default)]
322 pub protected: Option<Check>,
323 #[serde(skip_serializing_if = "Option::is_none")]
325 #[serde(default)]
326 pub cache: Option<TemplateCache>,
327
328 #[serde(skip_serializing_if = "Option::is_none")]
338 #[serde(default)]
339 pub csp: Option<HttpCsp>,
340
341 #[serde(skip_serializing_if = "Option::is_none")]
342 #[serde(default)]
343 pub cors: Option<HttpCors>,
344
345 #[serde(skip_serializing_if = "Option::is_none")]
349 #[serde(default)]
350 pub timeout: Option<u16>,
351
352 #[serde(skip_serializing_if = "Option::is_none")]
356 #[serde(default)]
357 pub fields: Option<Vec<TemplateField>>,
358 #[serde(skip_serializing_if = "Option::is_none")]
362 #[serde(default)]
363 pub globals: Option<Vec<String>>,
364 #[serde(skip_serializing_if = "Option::is_none")]
366 #[serde(default)]
367 pub flags: Option<Vec<TemplateFlagRef>>,
368 #[serde(skip_serializing_if = "Option::is_none")]
371 #[serde(default)]
372 pub models: Option<Vec<TemplateRef>>,
373 #[serde(skip_serializing_if = "Option::is_none")]
376 #[serde(default)]
377 pub content: Option<Vec<TemplateRef>>,
378
379 #[serde(skip_serializing_if = "Option::is_none")]
383 #[serde(default)]
384 pub actions: Option<Vec<String>>,
385
386 #[serde(skip_serializing_if = "Option::is_none")]
391 #[serde(default)]
392 pub variables: Option<Vec<String>>,
393
394 #[serde(skip_serializing_if = "Option::is_none")]
396 #[serde(default)]
397 pub middlewares: Option<Vec<String>>,
398}
399
400impl TemplateConfig {
401 pub fn load_managed(&mut self) {
402 let template_name = self.name_validated().to_owned();
403
404 if let Some(managed) = &self.managed {
405 match managed {
406 ManagedTemplate::V2RustAskama => {
407 self.ffi = Some(TemplateFfi {
408 version: TemplateFfiVersion::V2,
409 serialization: TemplateFfiSerialization::FlexBufferVector,
410 });
411
412 self.bindgen = Some(BindgenLang::Rust);
413 self.build = Some(vec![vec![
414 "sh".into(),
415 format!(".ordinary/{template_name}/build.sh"),
416 ]]);
417 self.bin = Some(format!(".ordinary/{template_name}/component.wasm"));
418 }
419 }
420 }
421 }
422
423 #[must_use]
427 pub fn idx_validated(&self) -> u8 {
428 self.idx.expect("idx should not be 'None' after validation")
429 }
430
431 #[must_use]
435 pub fn name_validated(&self) -> &str {
436 self.name
437 .as_ref()
438 .expect("name should not be 'None' after validation")
439 }
440
441 #[must_use]
445 pub fn route_validated(&self) -> &str {
446 self.route
447 .as_ref()
448 .expect("route should not be 'None' after validation")
449 }
450
451 #[must_use]
455 pub fn mime_validated(&self) -> &str {
456 self.mime
457 .as_ref()
458 .expect("route should not be 'None' after validation")
459 }
460
461 #[must_use]
465 pub fn ffi_validated(&self) -> &TemplateFfi {
466 self.ffi
467 .as_ref()
468 .expect("ffi should not be 'None' after validation")
469 }
470}