Skip to main content

seam_server/
lib.rs

1/* src/server/core/rust/src/lib.rs */
2#![cfg_attr(test, allow(clippy::unwrap_used))]
3
4pub mod build_loader;
5pub mod channel;
6pub mod errors;
7pub mod escape;
8pub mod manifest;
9pub mod page;
10pub mod procedure;
11pub mod resolve;
12pub mod server;
13
14// Re-exports for ergonomic use
15pub use build_loader::{RpcHashMap, load_build_output, load_i18n_config, load_rpc_hash_map};
16pub use channel::{ChannelDef, ChannelMeta, IncomingDef, IncomingMeta};
17pub use errors::SeamError;
18pub use escape::ascii_escape_json;
19pub use page::I18nConfig;
20pub use procedure::{BoxFuture, BoxStream, ProcedureDef, ProcedureType, SubscriptionDef};
21pub use resolve::{
22  ResolveData, ResolveStrategy, default_strategies, from_accept_language, from_cookie,
23  from_url_prefix, from_url_query, resolve_chain,
24};
25pub use seam_macros::{SeamType, seam_command, seam_procedure, seam_subscription};
26pub use server::{SeamParts, SeamServer};
27
28/// Trait for types that can describe themselves as a JTD schema.
29/// Derive with `#[derive(SeamType)]` or implement manually.
30pub trait SeamType {
31  fn jtd_schema() -> serde_json::Value;
32}
33
34// -- Primitive SeamType impls --
35
36macro_rules! impl_seam_type_primitive {
37  ($rust_ty:ty, $jtd:expr_2021) => {
38    impl SeamType for $rust_ty {
39      fn jtd_schema() -> serde_json::Value {
40        serde_json::json!({ "type": $jtd })
41      }
42    }
43  };
44}
45
46impl_seam_type_primitive!(String, "string");
47impl_seam_type_primitive!(bool, "boolean");
48impl_seam_type_primitive!(i8, "int8");
49impl_seam_type_primitive!(i16, "int16");
50impl_seam_type_primitive!(i32, "int32");
51impl_seam_type_primitive!(u8, "uint8");
52impl_seam_type_primitive!(u16, "uint16");
53impl_seam_type_primitive!(u32, "uint32");
54impl_seam_type_primitive!(f32, "float32");
55impl_seam_type_primitive!(f64, "float64");
56
57impl<T: SeamType> SeamType for Vec<T> {
58  fn jtd_schema() -> serde_json::Value {
59    serde_json::json!({ "elements": T::jtd_schema() })
60  }
61}
62
63impl<T: SeamType> SeamType for Option<T> {
64  fn jtd_schema() -> serde_json::Value {
65    let mut schema = T::jtd_schema();
66    if let Some(obj) = schema.as_object_mut() {
67      obj.insert("nullable".to_string(), serde_json::Value::Bool(true));
68    }
69    schema
70  }
71}
72
73impl<T: SeamType> SeamType for std::collections::HashMap<String, T> {
74  fn jtd_schema() -> serde_json::Value {
75    serde_json::json!({ "values": T::jtd_schema() })
76  }
77}
78
79impl<T: SeamType> SeamType for std::collections::BTreeMap<String, T> {
80  fn jtd_schema() -> serde_json::Value {
81    serde_json::json!({ "values": T::jtd_schema() })
82  }
83}
84
85#[cfg(test)]
86extern crate self as seam_server;
87
88#[cfg(test)]
89mod tests {
90  use super::*;
91
92  #[test]
93  fn primitive_schemas() {
94    assert_eq!(String::jtd_schema(), serde_json::json!({"type": "string"}));
95    assert_eq!(bool::jtd_schema(), serde_json::json!({"type": "boolean"}));
96    assert_eq!(i32::jtd_schema(), serde_json::json!({"type": "int32"}));
97    assert_eq!(u32::jtd_schema(), serde_json::json!({"type": "uint32"}));
98    assert_eq!(f64::jtd_schema(), serde_json::json!({"type": "float64"}));
99  }
100
101  #[test]
102  fn vec_schema() {
103    assert_eq!(Vec::<String>::jtd_schema(), serde_json::json!({"elements": {"type": "string"}}),);
104  }
105
106  #[test]
107  fn option_schema() {
108    assert_eq!(
109      Option::<String>::jtd_schema(),
110      serde_json::json!({"type": "string", "nullable": true}),
111    );
112  }
113
114  #[test]
115  fn hashmap_schema() {
116    assert_eq!(
117      std::collections::HashMap::<String, f64>::jtd_schema(),
118      serde_json::json!({"values": {"type": "float64"}}),
119    );
120  }
121
122  #[derive(SeamType)]
123  #[allow(dead_code)]
124  enum Role {
125    Admin,
126    Member,
127    Guest,
128  }
129
130  #[test]
131  fn enum_schema() {
132    assert_eq!(Role::jtd_schema(), serde_json::json!({"enum": ["admin", "member", "guest"]}),);
133  }
134}