1use std::{collections::BTreeMap, fs::read_to_string, path::Path};
89
90use heck::{ToSnakeCase, ToUpperCamelCase};
91use parser::{parse_string, AnyType, PrimativeType, StructField};
92use proc_macro2::{Ident, Span, TokenStream};
93use quote::quote;
94
95mod parser;
96
97#[derive(Clone, Copy, Debug, Eq, PartialEq)]
99pub struct Config {
100 pub use_hashable_map: bool,
102}
103
104impl Default for Config {
105 fn default() -> Self {
106 Self {
107 use_hashable_map: false,
108 }
109 }
110}
111
112impl Config {
113 pub fn with_hash_map() -> Self {
115 Self::default()
116 }
117
118 pub fn with_hashable_map() -> Self {
120 Self {
121 use_hashable_map: true,
122 }
123 }
124}
125
126fn ident_from_string(s: &String) -> Ident {
127 Ident::new(s, Span::call_site())
128}
129
130pub fn bare_schema(schema_path: &Path, config: Config) -> proc_macro2::TokenStream {
137 let file = read_to_string(schema_path).unwrap();
138 let mut schema_generator = SchemaGenerator {
139 global_output: Default::default(),
140 user_type_registry: parse_string(&file),
141 config,
142 };
143
144 for (name, user_type) in &schema_generator.user_type_registry.clone() {
145 schema_generator.gen_user_type(&name, &user_type);
146 }
147
148 schema_generator.complete()
149}
150
151struct SchemaGenerator {
152 global_output: Vec<TokenStream>,
153 user_type_registry: BTreeMap<String, AnyType>,
154 config: Config,
155}
156
157impl SchemaGenerator {
158 fn complete(self) -> TokenStream {
161 let SchemaGenerator { global_output, .. } = self;
162 quote! {
163 #[allow(unused_imports)]
164 use serde::{Serialize, Deserialize};
165 #[allow(unused_imports)]
166 use serde_bare::{Uint, Int};
167
168 #(#global_output)*
169 }
170 }
171
172 fn gen_user_type(&mut self, name: &String, t: &AnyType) {
177 #[allow(unused_assignments)]
178 use AnyType::*;
179 let def = match t {
180 Primative(p) => {
181 let def = gen_primative_type_def(p);
182 let ident = ident_from_string(name);
183 quote! {
184 pub type #ident = #def;
185 }
186 }
187 List { inner, length } => {
188 let def = self.gen_list(name, inner.as_ref(), length);
189 let ident = ident_from_string(name);
190 quote! {
191 pub type #ident = #def;
192 }
193 }
194 Struct(fields) => {
195 self.gen_struct(name, fields);
196 TokenStream::new()
198 }
199 Map { key, value } => {
200 let map_def = self.gen_map(name, key.as_ref(), value.as_ref());
201 let ident = ident_from_string(name);
202 quote! {
203 pub type #ident = #map_def;
204 }
205 }
206 Optional(inner) => {
207 let inner_def = self.dispatch_type(name, inner);
208 let ident = ident_from_string(name);
209 quote! {
210 pub type #ident = #inner_def;
211 }
212 }
213 TypeReference(reference) => {
214 panic!("Type reference is not valid as a top level definition: {reference}")
215 }
216 Enum(members) => {
217 self.gen_enum(name, members);
218 TokenStream::new()
220 }
221 Union(members) => {
222 self.gen_union(name, members);
223 TokenStream::new()
225 }
226 };
227 self.global_output.push(def);
228 }
229
230 fn dispatch_type(&mut self, name: &String, any_type: &AnyType) -> TokenStream {
231 match any_type {
232 AnyType::Primative(p) => gen_primative_type_def(p),
233 AnyType::List { inner, length } => self.gen_list(name, inner.as_ref(), length),
234 AnyType::Struct(fields) => self.gen_struct(name, fields),
235 AnyType::Enum(members) => self.gen_enum(name, members),
236 AnyType::Map { key, value } => self.gen_map(name, key.as_ref(), value.as_ref()),
237 AnyType::Union(members) => self.gen_union(name, members),
238 AnyType::Optional(inner) => self.gen_option(name, inner),
239 AnyType::TypeReference(i) => {
240 let ident = ident_from_string(i);
241 quote! { #ident }
242 }
243 }
244 }
245
246 fn gen_map(&mut self, name: &String, key: &AnyType, value: &AnyType) -> TokenStream {
247 let key_def = self.dispatch_type(name, key);
248 let val_def = self.dispatch_type(name, value);
249 if self.config.use_hashable_map {
250 quote! {
251 rivet_util::serde::HashableMap<#key_def, #val_def>
252 }
253 } else {
254 quote! {
255 std::collections::HashMap<#key_def, #val_def>
256 }
257 }
258 }
259
260 fn gen_list(
261 &mut self,
262 name: &String,
263 inner_type: &AnyType,
264 size: &Option<usize>,
265 ) -> TokenStream {
266 let inner_def = self.dispatch_type(name, inner_type);
267 match *size {
268 Some(size) if size <= 32 => quote! {
269 [#inner_def; #size]
270 },
271 _ => quote! {
272 Vec<#inner_def>
273 },
274 }
275 }
276
277 fn gen_struct(&mut self, name: &String, fields: &Vec<StructField>) -> TokenStream {
278 let fields_clone = fields.clone();
280 let fields_gen = self.gen_struct_field(name, fields_clone);
281 let hash_derive = if self.config.use_hashable_map {
282 quote! { , Hash }
283 } else {
284 TokenStream::new()
285 };
286 self.gen_anonymous(name, |ident| {
287 quote! {
288 #[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone #hash_derive)]
289 pub struct #ident {
290 #(#fields_gen),*
291 }
292 }
293 })
294 }
295
296 fn gen_union(&mut self, name: &String, members: &Vec<AnyType>) -> TokenStream {
297 let mut members_def: Vec<TokenStream> = Vec::with_capacity(members.len());
298 for (i, member) in members.iter().enumerate() {
299 let is_void_type = match member {
301 AnyType::TypeReference(i) if self.user_type_registry.get(i).is_some() => {
302 let reference = self.user_type_registry.get(i).unwrap();
303 matches!(reference, AnyType::Primative(PrimativeType::Void))
304 }
305 _ => false,
306 };
307
308 #[allow(unused_assignments)]
312 let mut member_def = TokenStream::new();
313 member_def = match member {
314 AnyType::Struct(fields) => {
315 let fields_defs = self.gen_struct_field(name, fields.clone());
316 quote! {
317 {
318 #(#fields_defs),*
319 }
320 }
321 }
322 AnyType::TypeReference(i) if is_void_type => {
323 let inner_def = ident_from_string(i);
324 quote! {
326 #inner_def
327 }
328 }
329 _ => {
330 let inner_def = self.dispatch_type(&format!("{name}Member{i}"), member);
331 quote! {
333 #inner_def(#inner_def)
334 }
335 }
336 };
337 members_def.push(member_def);
338 }
339 let hash_derive = if self.config.use_hashable_map {
340 quote! { , Hash }
341 } else {
342 TokenStream::new()
343 };
344 self.gen_anonymous(name, |ident| {
345 quote! {
346 #[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone #hash_derive)]
347 pub enum #ident {
348 #(#members_def),*
349 }
350 }
351 })
352 }
353
354 fn gen_option(&mut self, name: &String, inner: &AnyType) -> TokenStream {
355 let inner_def = self.dispatch_type(name, inner);
356 quote! {
357 Option<#inner_def>
358 }
359 }
360
361 fn gen_struct_field(
362 &mut self,
363 struct_name: &String,
364 fields: Vec<StructField>,
365 ) -> Vec<TokenStream> {
366 let mut fields_gen: Vec<TokenStream> = Vec::with_capacity(fields.len());
367 for StructField { name, type_r } in fields {
368 let name = name.to_snake_case();
369 #[allow(unused_assignments)]
370 let field_gen = self.dispatch_type(&format!("{struct_name}{name}"), &type_r);
371 let ident = ident_from_string(&name);
372 fields_gen.push(quote! {
373 pub #ident: #field_gen
374 })
375 }
376 fields_gen
377 }
378
379 fn gen_enum(&mut self, name: &String, members: &Vec<(String, Option<usize>)>) -> TokenStream {
380 let member_defs = members.iter().map(|(name, val)| {
381 let ident = ident_from_string(&name.to_upper_camel_case());
382 if let Some(val) = val {
383 quote! {
384 #ident = #val
385 }
386 } else {
387 quote! {
388 #ident
389 }
390 }
391 });
392 let hash_derive = if self.config.use_hashable_map {
393 quote! { Hash, }
394 } else {
395 TokenStream::new()
396 };
397 self.gen_anonymous(name, |ident| {
398 quote! {
399 #[derive(Serialize, Deserialize, PartialEq, Eq, Debug, PartialOrd, Ord, #hash_derive Clone)]
400 #[repr(usize)]
401 pub enum #ident {
402 #(#member_defs),*
403 }
404 }
405 })
406 }
407
408 fn gen_anonymous(
413 &mut self,
414 name: &String,
415 inner: impl FnOnce(Ident) -> TokenStream,
416 ) -> TokenStream {
417 let ident = ident_from_string(name);
418 self.global_output.push(inner(ident.clone()));
419 quote! {
420 #ident
421 }
422 }
423}
424
425fn gen_primative_type_def(p: &PrimativeType) -> TokenStream {
426 use PrimativeType::*;
427 match p {
428 UInt => quote! { Uint },
429 U64 => quote! { u64 },
430 U32 => quote! { u32 },
431 U16 => quote! { u16 },
432 U8 => quote! { u8 },
433 Int => quote! { Int },
434 I64 => quote! { i64 },
435 I32 => quote! { i32 },
436 I16 => quote! { i16 },
437 I8 => quote! { i8 },
438 F64 => quote! { f64 },
439 F32 => quote! { f32 },
440 Str => quote! { String },
441 Data(s) => match s {
442 Some(size) if *size <= 32 => quote! { [u8; #size] },
443 _ => quote! { Vec<u8> },
444 },
445 Void => quote! { () },
446 Bool => quote! { bool },
447 }
448}