1use crate::schema;
2
3struct Generator {
4 level: usize,
5 output: String,
6}
7
8impl Generator {
9 fn new() -> Self {
10 Self {
11 level: 0,
12 output: String::new(),
13 }
14 }
15 fn begin(&mut self, line: &str) {
16 self.line(line);
17 self.level += 1;
18 }
19 fn end(&mut self, line: &str) {
20 self.level -= 1;
21 if !line.is_empty() {
22 self.line(line);
23 }
24 }
25 fn line(&mut self, line: &str) {
26 if !line.is_empty() {
27 for _ in 0..self.level {
28 self.output += " ";
29 }
30 self.output += line;
31 }
32 self.output += "\n";
33 }
34}
35
36impl From<Generator> for String {
37 fn from(gen: Generator) -> Self {
38 gen.output
39 }
40}
41
42pub fn gen(doc: &schema::Document) -> String {
43 let mut gen = Generator::new();
44 gen.line("// GENERATED CODE - DO NOT EDIT!");
45 gen.line("");
46 gen.line("import * as webwire from './webwire'");
49 gen.line("");
50 gen_namespace(&doc.ns, &mut gen);
51 gen.into()
52}
53
54fn gen_namespace(ns: &schema::Namespace, gen: &mut Generator) {
55 for type_ in ns.types.values() {
56 gen.line("");
57 gen_type(&*type_, gen);
58 }
59 for service in ns.services.values() {
60 gen.line("");
61 gen_service(service, gen);
62 gen.line("");
63 gen_consumer(ns, service, gen);
64 }
65 for child_ns in ns.namespaces.values() {
66 gen.line("");
67 gen.begin(&format!("export namespace {} {{", child_ns.name()));
68 gen_namespace(child_ns, gen);
69 gen.end("}");
70 }
71}
72
73fn gen_type(type_: &schema::UserDefinedType, gen: &mut Generator) {
74 match type_ {
75 schema::UserDefinedType::Enum(enum_) => gen_enum(&*enum_.borrow(), gen),
76 schema::UserDefinedType::Struct(struct_) => gen_struct(&*struct_.borrow(), gen),
77 schema::UserDefinedType::Fieldset(fieldset) => gen_fieldset(&*fieldset.borrow(), gen),
78 }
79}
80
81fn gen_enum(enum_: &schema::Enum, gen: &mut Generator) {
82 let enum_name = &enum_.fqtn.name;
83 if enum_.all_variants.is_empty() {
84 gen.line(&format!("export type _{}Variants = never", enum_name));
85 gen.line(&format!("export type {} = never", enum_.fqtn.name));
86 return;
87 }
88 gen.line(&format!(
89 "export type _{}Variants = {}",
90 enum_name,
91 enum_
92 .all_variants
93 .iter()
94 .map(|v| format!("\"{}\"", v.name))
95 .collect::<Vec<_>>()
96 .join(" | ")
97 ));
98 gen.begin(&format!("export type {} =", enum_.fqtn.name));
99 for variant in enum_.all_variants.iter() {
100 gen.line(&match &variant.value_type {
101 Some(value_type) => format!(
102 "| {{ [P in Exclude<_{}Variants, \"{}\">]?: never }} & {{ {}: {} }}",
103 enum_name,
104 variant.name,
105 variant.name,
106 gen_typeref(value_type)
107 ),
108 None => format!("| \"{}\"", variant.name),
109 });
110 }
113 gen.end("");
114}
115
116fn gen_struct(struct_: &schema::Struct, gen: &mut Generator) {
117 let generics = if struct_.generics.is_empty() {
118 "".to_string()
119 } else {
120 format!("<{}>", struct_.generics.join(", "))
121 };
122 gen.begin(&format!(
123 "export interface {}{} {{",
124 struct_.fqtn.name, generics
125 ));
126 for field in struct_.fields.iter() {
127 let opt = if field.optional { "?" } else { "" };
128 gen.line(&format!(
129 "{}{}: {},",
130 field.name,
131 opt,
132 gen_typeref(&field.type_)
133 ));
134 }
135 gen.end("}");
136}
137
138fn gen_fieldset(fieldset: &schema::Fieldset, gen: &mut Generator) {
139 let generics = if fieldset.generics.is_empty() {
140 "".to_string()
141 } else {
142 format!("<{}>", fieldset.generics.join(", "))
143 };
144 gen.begin(&format!(
145 "export interface {}{} {{",
146 fieldset.fqtn.name, generics
147 ));
148 for field in fieldset.fields.iter() {
149 let opt = if field.optional { "?" } else { "" };
151 gen.line(&format!(
152 "{}{}: {},",
153 field.name,
154 opt,
155 gen_typeref(&field.field.as_ref().unwrap().type_)
156 ));
157 }
158 gen.end("}");
159}
160
161fn method_signature(method: &schema::Method) -> String {
162 let input = match &method.input {
163 Some(t) => format!("input: {}", gen_typeref(&t)),
164 None => String::new(),
165 };
166 let output = match &method.output {
167 Some(t) => gen_typeref(t),
168 None => "void".to_string(),
169 };
170 format!("{}({}): Promise<{}>", method.name, input, output)
171}
172
173fn gen_service(service: &schema::Service, gen: &mut Generator) {
174 gen.begin(&format!("export interface {} {{", service.name));
175 for method in service.methods.iter() {
176 gen.line(&format!("{},", method_signature(&method)));
177 }
178 gen.end("}");
179}
180
181fn gen_consumer(ns: &schema::Namespace, service: &schema::Service, gen: &mut Generator) {
182 gen.begin(&format!(
183 "export class {}Consumer implements {} {{",
184 service.name, service.name
185 ));
186 gen.line("_client: webwire.Client");
187 gen.begin("constructor(client: webwire.Client) {");
188 gen.line("this._client = client");
189 gen.end("}");
190 for method in service.methods.iter() {
191 gen.begin(&format!("async {} {{", method_signature(&method)));
192 let fqsn = if ns.path.is_empty() {
193 service.name.to_owned()
194 } else {
195 format!("{}.{}", ns.path.join("."), service.name)
196 };
197 let input_param = if method.input.is_some() {
198 ", input"
199 } else {
200 ""
201 };
202 gen.line(&format!(
203 "return await this._client.request('{}', '{}'{})",
204 fqsn, method.name, input_param,
205 ));
206 gen.end("}");
207 }
208 gen.end("}");
209}
210
211pub fn gen_typeref(type_: &schema::Type) -> String {
212 match type_ {
213 schema::Type::None => "null".to_string(),
214 schema::Type::Boolean => "boolean".to_string(),
215 schema::Type::Integer => "number".to_string(),
216 schema::Type::Float => "number".to_string(),
217 schema::Type::String => "string".to_string(),
218 schema::Type::UUID => "webwire.UUID".to_string(),
219 schema::Type::Date => "webwire.Date".to_string(),
220 schema::Type::Time => "webwire.Time".to_string(),
221 schema::Type::DateTime => "webwire.DateTime".to_string(),
222 schema::Type::Option(some) => format!("webwire.Option<{}>", gen_typeref(some)),
223 schema::Type::Result(ok, err) => {
224 format!("webwire.Result<{}, {}>", gen_typeref(ok), gen_typeref(err))
225 }
226 schema::Type::Array(array) => format!("Array<{}>", gen_typeref(&array.item_type)),
228 schema::Type::Map(map) => format!(
229 "Map<{}, {}>",
230 gen_typeref(&map.key_type),
231 gen_typeref(&map.value_type)
232 ),
233 schema::Type::Ref(typeref) => {
235 let typeref_fqtn = typeref.fqtn();
236 let fqtn = if typeref_fqtn.ns.is_empty() {
237 typeref_fqtn.name.clone()
238 } else {
239 let ns = typeref_fqtn.ns.join(".");
240 format!("{}.{}", ns, typeref_fqtn.name)
241 };
242 let generics = typeref.generics();
243 if !generics.is_empty() {
244 let generics = generics
245 .iter()
246 .map(gen_typeref)
247 .collect::<Vec<_>>()
248 .join(", ");
249 format!("{}<{}>", fqtn, generics)
250 } else {
251 fqtn
252 }
253 }
254 schema::Type::Builtin(name) => name.to_string(),
255 }
256}