1use std::collections::{BTreeMap, HashMap};
2use std::fmt::Write;
3
4use crate::NamespaceMemberDefinition;
5
6use crate::{
7 rpc::codegen::{self, RpcMember},
8 rpc::Request,
9 rpc::ResponseSender,
10};
11
12pub type StreamHandle = tokio::task::JoinHandle<()>;
13
14#[derive(Debug, Default)]
15pub struct SubscriberMap {
16 inner: HashMap<usize, StreamHandle>,
17}
18
19impl std::ops::Deref for SubscriberMap {
20 type Target = HashMap<usize, StreamHandle>;
21
22 fn deref(&self) -> &Self::Target {
23 &self.inner
24 }
25}
26
27impl std::ops::DerefMut for SubscriberMap {
28 fn deref_mut(&mut self) -> &mut Self::Target {
29 &mut self.inner
30 }
31}
32
33impl Drop for SubscriberMap {
34 fn drop(&mut self) {
35 for (_, jh) in self.inner.drain() {
36 jh.abort();
37 }
38 }
39}
40
41#[derive(Clone, Debug, Default)]
42pub struct CodegenOptions {
43 prefix_schema: String,
44 suffix_schema: String,
45
46 prefix_type: String,
47 suffix_type: String,
48
49 prefix_interface: String,
50 suffix_interface: String,
51}
52
53#[async_trait::async_trait]
54pub trait Backend {
55 const NS_NAMES: &'static [&'static str];
56
57 fn rpc_members() -> HashMap<&'static str, Vec<String>> {
58 let mut out = HashMap::<&'static str, Vec<String>>::new();
59 let members = inventory::iter::<RpcMember>()
60 .filter(|member| Self::NS_NAMES.contains(&member.ns_name()));
61
62 for member in members {
63 out.entry(member.ns_name()).or_default().push(member.decl());
64 }
65 out
66 }
67
68 fn zod_namespaces() -> HashMap<&'static str, Vec<&'static NamespaceMemberDefinition>> {
69 let mut out = HashMap::<&'static str, Vec<&'static NamespaceMemberDefinition>>::new();
70 let members = inventory::iter::<NamespaceMemberDefinition>()
71 .filter(|member| Self::NS_NAMES.contains(&member.namespace()));
72
73 for member in members {
74 out.entry(member.namespace()).or_default().push(member);
75 }
76 out
77 }
78 fn generate<T>() -> String
79 where
80 T: codegen::ClientCodegen,
81 {
82 Self::generate_with_options::<T>(Default::default())
83 }
84
85 fn generate_with_options<T>(options: CodegenOptions) -> String
86 where
87 T: codegen::ClientCodegen,
88 {
89 let rpc_records = inventory::iter::<RpcMember>()
90 .filter(|member| Self::NS_NAMES.contains(&member.ns_name()))
91 .map(|m| (m.ns_name(), m.decl()));
92
93 let mut records: BTreeMap<&'static str, String> =
94 crate::NamespaceMemberDefinition::collect()
95 .into_iter()
96 .filter(|(ns, _)| Self::NS_NAMES.contains(ns))
97 .map(|(ns, defs)| {
98 (
99 ns,
100 defs.into_iter()
101 .map(|def| {
102 let td = match def.type_def() {
103 crate::TsTypeDef::Interface(inner) => {
104 format!(
105 "export interface {}{}{} {}",
106 options.prefix_interface,
107 def.name(),
108 options.suffix_interface,
109 inner
110 )
111 }
112
113 crate::TsTypeDef::Type(inner) => {
114 format!(
115 "export type {}{}{} = {};",
116 options.prefix_type,
117 def.name(),
118 options.suffix_type,
119 inner
120 )
121 }
122 };
123
124 format!(
125 "export const {}{}{}= {}\n{}\n\n",
126 options.prefix_schema,
127 def.name(),
128 options.suffix_schema,
129 def.schema(),
130 td
131 )
132 })
133 .collect(),
134 )
135 })
136 .collect();
137
138 for (name, code) in rpc_records {
139 let s = records.entry(name).or_default();
140 write!(s, "{}", code).unwrap();
141 }
142
143 let mut code = T::get();
144
145 for (ns, ns_code) in records.into_iter() {
146 write!(code, "export namespace {} {{ {} }}", ns, ns_code).expect("write failed");
147 }
148 code
149 }
150
151 async fn handle_request(
152 &mut self,
153 req: Request,
154 res: ResponseSender,
155 subscribers: &mut SubscriberMap,
156 );
157}