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