Skip to main content

arora_module_rust/
lib.rs

1pub mod rustfmt;
2
3use arora_module_core::{
4    header::generate_header_file, ImportAsset, ModuleAsset, ModuleDeclarationError,
5};
6use arora_registry::{
7    local::LocalRegistry, EnumerationFrozen, ModuleFrozen, ReadableRegistry, RegistryError,
8    StructureFrozen, TypeDefinitionFrozen,
9};
10use arora_types::record::ty::PrimitiveKind;
11use arora_types::record::{
12    module::frozen::{ExportKind, Parameter},
13    ty::{FrozenScalar, FrozenTy, Primitive},
14    FrozenReference,
15};
16use arora_types::record::{RecordType, Selector};
17use arora_types::ty::{
18    BOOLEAN_ID, F32_ID, F64_ID, I16_ID, I32_ID, I64_ID, I8_ID, STRING_ID, U16_ID, U32_ID, U64_ID,
19    U8_ID, UNIT_ID,
20};
21use arora_vfs::{Directory, Entry as VfsEntry, File, VfsError};
22use async_recursion::async_recursion;
23use convert_case::{Case, Casing};
24use derive_more::Display;
25use proc_macro2::Ident;
26use quote::{__private::TokenStream, format_ident, quote, ToTokens};
27use semver::VersionReq;
28use std::{
29    collections::{hash_map::Entry, HashMap, HashSet},
30    fmt::Display,
31    path,
32};
33use uuid::Uuid;
34
35/// Generates a set of sources organized in a virtual directory
36/// from a set of assets as produced by [`arora_module_core::analyze_module`].
37/// First, the types, then the modules, then the imports.
38pub async fn generate_sources(
39    assets: Vec<ModuleAsset>,
40    registry: &mut dyn ReadableRegistry,
41) -> Result<Directory, GenerationError> {
42    let mut result = generate_common_sources()?;
43    let mut imports_by_module: HashMap<Uuid, Vec<ImportAsset>> = HashMap::new();
44    let mut current_module = Option::<(Uuid, ModuleFrozen, String)>::None;
45    for asset in assets {
46        match asset {
47            ModuleAsset::Type(id, _, ty) => match ty {
48                TypeDefinitionFrozen::Primitive(_kind) => {}
49                TypeDefinitionFrozen::Enumeration(enumeration) => {
50                    let parent_path = registry
51                        .resolve_id(&enumeration.parent)
52                        .await
53                        .map_err(GenerationError::RegistryError)?;
54                    let enum_sources = generate_enumeration_source(&id, &enumeration, &parent_path)
55                        .map_err(GenerationError::VfsError)?;
56                    result = result.merge_with(&enum_sources);
57                }
58                TypeDefinitionFrozen::Structure(structure) => {
59                    let parent_path = registry
60                        .resolve_id(&structure.parent)
61                        .await
62                        .map_err(GenerationError::RegistryError)?;
63                    let struct_sources =
64                        generate_structure_source(&id, &structure, registry, &parent_path).await?;
65                    result = result.merge_with(&struct_sources);
66                }
67            },
68            ModuleAsset::Import(import) => {
69                match imports_by_module.entry(import.module_id.to_owned()) {
70                    Entry::Occupied(mut entry) => entry.get_mut().push(import),
71                    Entry::Vacant(entry) => {
72                        entry.insert(vec![import]);
73                    }
74                }
75            }
76            ModuleAsset::Module(ref module_id, _, ref module, ref executor) => {
77                let module_sources = generate_module_source(module, registry).await?;
78                result = result.merge_with(&module_sources);
79                assert!(current_module.is_none()); // Only one module to generate at a time.
80                current_module =
81                    Some((module_id.to_owned(), module.to_owned(), executor.to_owned()));
82            }
83        }
84    }
85
86    let mut all_imports = Vec::new();
87    for (module_id, ref mut imports) in imports_by_module {
88        // Generate bindings for imported functions.
89        let module_path = registry
90            .resolve_id(&module_id)
91            .await
92            .map_err(GenerationError::RegistryError)?;
93        let imports_sources =
94            generate_imports_from_module_source(&module_id, &module_path, imports, registry)
95                .await?;
96        result = result.merge_with(&imports_sources);
97        all_imports.append(imports);
98    }
99
100    // Produce the stripped `module.yaml` file.
101    let current_module = current_module.unwrap();
102    result = result.merge_with(
103        &generate_header_file(
104            &current_module.0,
105            &current_module.1,
106            &all_imports,
107            &current_module.2,
108        )
109        .map_err(GenerationError::ModuleDeclarationError)?,
110    );
111
112    // Also declare arora engine functions.
113    // On wasm32 these are real imports from the host engine; on other
114    // targets (host iteration / `cargo test`) we emit panicking stubs so
115    // the crate still links and trivial tests can run natively.
116    let engine_functions_declarations = quote! {
117      #[cfg(target_arch = "wasm32")]
118      #[link(wasm_import_module = "env")]
119      extern "C" {
120        pub fn arora_dispatch(module_id: usize, method_id: usize, arg: usize) -> usize;
121        pub fn arora_dispatch_indirect(callable_id: u64) -> usize;
122      }
123
124      #[cfg(not(target_arch = "wasm32"))]
125      pub unsafe extern "C" fn arora_dispatch(
126        _module_id: usize,
127        _method_id: usize,
128        _arg: usize,
129      ) -> usize {
130        panic!(
131          "arora_dispatch called on the host; this module is meant to run as a wasm guest under the arora engine"
132        );
133      }
134
135      #[cfg(not(target_arch = "wasm32"))]
136      pub unsafe extern "C" fn arora_dispatch_indirect(_callable_id: u64) -> usize {
137        panic!(
138          "arora_dispatch_indirect called on the host; this module is meant to run as a wasm guest under the arora engine"
139        );
140      }
141    };
142    result = result.merge_with(
143        &token_stream_to_file("arora.rs", &engine_functions_declarations)
144            .map_err(GenerationError::VfsError)?,
145    );
146
147    // Add the `mod.rs` files.
148    generate_mods_in_directories(&mut result)?;
149
150    Ok(result)
151}
152
153/// Generates `mod.rs` files and adds them at every level of the directory hierarchy
154/// where `.rs` files can be found. Returns true if it was generated.
155pub fn generate_mods_in_directories(dir: &mut Directory) -> Result<bool, GenerationError> {
156    let mut mods = Vec::new();
157    for (path, entry) in dir.list_mut() {
158        if let VfsEntry::Directory(ref mut dir) = entry {
159            if generate_mods_in_directories(dir)? {
160                mods.push(path);
161            }
162        } else {
163            if path.ends_with(".rs") {
164                mods.push(path[..path.len() - 3].to_string());
165            }
166        }
167    }
168    if !mods.is_empty() {
169        let mods = mods
170            .into_iter()
171            .map(|mod_name| format_ident!("{}", mod_name.to_case(Case::Snake)));
172        let tokens = quote! {
173          #(pub mod #mods;)*
174        };
175        dir.insert("mod.rs", File::new(tokens.to_string()))
176            .map_err(GenerationError::VfsError)?;
177        Ok(true)
178    } else {
179        Ok(false)
180    }
181}
182
183/// Generates YAML record files alongside a `records.json` index from the type assets
184/// produced by [`arora_module_core::analyze_module`].
185/// Must be called BEFORE [`generate_sources`] because `generate_sources` consumes `assets`.
186pub fn generate_records(
187    assets: &[ModuleAsset],
188    registry: &LocalRegistry,
189) -> Result<Directory, GenerationError> {
190    use arora_registry::local::ROOT_ID;
191
192    let mut vfs = Directory::new();
193    vfs.ensure_directories(&path::PathBuf::from("folder"))
194        .map_err(GenerationError::VfsError)?;
195    vfs.ensure_directories(&path::PathBuf::from("enumeration"))
196        .map_err(GenerationError::VfsError)?;
197    vfs.ensure_directories(&path::PathBuf::from("structure"))
198        .map_err(GenerationError::VfsError)?;
199
200    let mut json_records: Vec<serde_json::Value> = vec![
201        serde_json::json!({"id": UNIT_ID.to_string(), "name": "unit"}),
202        serde_json::json!({"id": BOOLEAN_ID.to_string(), "name": "bool"}),
203        serde_json::json!({"id": U8_ID.to_string(), "name": "u8"}),
204        serde_json::json!({"id": U16_ID.to_string(), "name": "u16"}),
205        serde_json::json!({"id": U32_ID.to_string(), "name": "u32"}),
206        serde_json::json!({"id": U64_ID.to_string(), "name": "u64"}),
207        serde_json::json!({"id": I8_ID.to_string(), "name": "i8"}),
208        serde_json::json!({"id": I16_ID.to_string(), "name": "i16"}),
209        serde_json::json!({"id": I32_ID.to_string(), "name": "i32"}),
210        serde_json::json!({"id": I64_ID.to_string(), "name": "i64"}),
211        serde_json::json!({"id": F32_ID.to_string(), "name": "f32"}),
212        serde_json::json!({"id": F64_ID.to_string(), "name": "f64"}),
213        serde_json::json!({"id": STRING_ID.to_string(), "name": "str"}),
214    ];
215    // Count of the hand-ordered primitive entries above. Everything appended
216    // after them is derived from `assets`, whose iteration order is not stable,
217    // so we sort that tail below to keep `records.json` from churning.
218    let primitive_count = json_records.len();
219
220    let mut seen_folders: HashSet<Uuid> = HashSet::new();
221
222    for asset in assets {
223        let (id, version, ty) = match asset {
224            ModuleAsset::Type(id, version, ty) => (id, version, ty),
225            _ => continue,
226        };
227
228        match ty {
229            TypeDefinitionFrozen::Primitive(_) => {}
230            TypeDefinitionFrozen::Enumeration(e) => {
231                let yaml = serde_yaml::to_string(e)
232                    .map_err(|err| GenerationError::Generic(err.to_string()))?;
233                let file_name = format!("{}@{}.yaml", id, version);
234                vfs.insert_at_path(
235                    path::PathBuf::from("enumeration").join(&file_name),
236                    File::new(yaml.as_bytes()),
237                )
238                .map_err(GenerationError::VfsError)?;
239
240                let mut entry = serde_json::json!({"id": id.to_string(), "name": e.name});
241                if e.parent != ROOT_ID {
242                    entry["parent"] = serde_json::json!(e.parent.to_string());
243                }
244                json_records.push(entry);
245                add_folder_chain(
246                    &e.parent,
247                    registry,
248                    &mut vfs,
249                    &mut json_records,
250                    &mut seen_folders,
251                )?;
252            }
253            TypeDefinitionFrozen::Structure(s) => {
254                let yaml = serde_yaml::to_string(s)
255                    .map_err(|err| GenerationError::Generic(err.to_string()))?;
256                let file_name = format!("{}@{}.yaml", id, version);
257                vfs.insert_at_path(
258                    path::PathBuf::from("structure").join(&file_name),
259                    File::new(yaml.as_bytes()),
260                )
261                .map_err(GenerationError::VfsError)?;
262
263                let mut entry = serde_json::json!({"id": id.to_string(), "name": s.name});
264                if s.parent != ROOT_ID {
265                    entry["parent"] = serde_json::json!(s.parent.to_string());
266                }
267                json_records.push(entry);
268                add_folder_chain(
269                    &s.parent,
270                    registry,
271                    &mut vfs,
272                    &mut json_records,
273                    &mut seen_folders,
274                )?;
275            }
276        }
277    }
278
279    // Sort the dynamically-collected records by id so `records.json` is stable
280    // across builds; the leading primitives keep their fixed, hand-authored order.
281    json_records[primitive_count..].sort_by(|a, b| a["id"].as_str().cmp(&b["id"].as_str()));
282
283    let records_json = serde_json::to_string(&json_records)
284        .map_err(|err| GenerationError::Generic(err.to_string()))?;
285    vfs.insert("records.json", File::new(records_json.as_bytes()))
286        .map_err(GenerationError::VfsError)?;
287
288    Ok(vfs)
289}
290
291fn add_folder_chain(
292    folder_id: &Uuid,
293    registry: &LocalRegistry,
294    vfs: &mut Directory,
295    json_records: &mut Vec<serde_json::Value>,
296    seen: &mut HashSet<Uuid>,
297) -> Result<(), GenerationError> {
298    use arora_registry::local::ROOT_ID;
299
300    if *folder_id == ROOT_ID || seen.contains(folder_id) {
301        return Ok(());
302    }
303
304    let Some((name, parent_opt)) = registry.record_name_and_parent(folder_id) else {
305        return Ok(());
306    };
307
308    seen.insert(*folder_id);
309
310    let yaml = match parent_opt {
311        Some(p) => format!("name: {}\nparent: {}\n", name, p),
312        None => format!("name: {}\n", name),
313    };
314    let file_name = format!("{}.yaml", folder_id);
315    vfs.insert_at_path(
316        path::PathBuf::from("folder").join(&file_name),
317        File::new(yaml.as_bytes()),
318    )
319    .map_err(GenerationError::VfsError)?;
320
321    let mut entry = serde_json::json!({"id": folder_id.to_string(), "name": name});
322    if let Some(p) = parent_opt {
323        if p != ROOT_ID {
324            entry["parent"] = serde_json::json!(p.to_string());
325        }
326        add_folder_chain(&p, registry, vfs, json_records, seen)?;
327    }
328    json_records.push(entry);
329
330    Ok(())
331}
332
333pub fn generate_common_sources() -> Result<Directory, GenerationError> {
334    let source = quote! {
335      use derive_more::Display;
336
337      #[derive(Display, Debug)]
338      pub struct DeserializationError {
339        #[display("deserialization error: {}", message)]
340        pub message: String,
341      }
342
343      impl std::error::Error for DeserializationError {}
344    };
345    token_stream_to_file("error.rs", &source).map_err(GenerationError::VfsError)
346}
347
348/// Generates a Rust source file for the given enumeration.
349/// It contains the type declaration and some functions
350/// to serialize and deserialize values.
351/// It depends on `arora_buffers`, `arora_types` and `uuid`.
352pub fn generate_enumeration_source(
353    id: &Uuid,
354    enumeration: &EnumerationFrozen,
355    parent_path: &str,
356) -> Result<Directory, VfsError> {
357    let uses = quote! {
358      use crate::arora_generated::error::DeserializationError;
359      use arora_buffers::*;
360      use arora_types::value::{ConversionError, Enumeration, Value};
361      use uuid::Uuid;
362    };
363
364    // Enum declaration.
365    let name = &enumeration.name;
366    let enum_name = name.to_case(Case::UpperCamel);
367    let enum_ident = type_ident(&enum_name);
368    let variants = enumeration.variants.iter();
369    let enum_contents = variants.clone().map(|(_, variant)| {
370        let variant_ident = format_ident!("{}", variant.name.to_case(Case::UpperCamel));
371        quote! { #variant_ident, }
372    });
373    let enum_declaration = quote! {
374      #[derive(Debug, PartialEq, Clone)]
375      pub enum #enum_ident {
376        #(#enum_contents)*
377      }
378    };
379
380    // Enum IDs.
381    let enum_id = id.to_string();
382    let enum_id_bytes = RawUuidValue(id);
383    let enum_upper_name = format_ident!("{}", enum_name.to_case(Case::UpperSnake));
384    let enum_const_id_ident = format_ident!("{}_ENUM_RAW_ID", enum_upper_name);
385    let enum_const_id_doc = format!("{}: {}", enum_name, enum_id);
386    let enum_id_declaration = quote! {
387      #[doc = #enum_const_id_doc]
388      pub const #enum_const_id_ident: [u8; 16] = #enum_id_bytes;
389    };
390
391    let variant_id_declarations = variants.clone().map(|(id, variant)| {
392        let id_string = id.to_string();
393        let id_bytes = RawUuidValue(id);
394        let variant_const_id_ident = enum_variant_const_id_ident(&enum_name, &variant.name);
395        let variant_doc = format!(
396            "{}: {}",
397            enum_variant_ident(&enum_name, &variant.name),
398            id_string
399        );
400        quote! {
401          #[doc = #variant_doc]
402          pub const #variant_const_id_ident: [u8; 16] = #id_bytes;
403        }
404    });
405
406    // Enum Serialization.
407    let serialization_match_branches = variants.clone().map(|(_, variant)| {
408        let variant_ident = enum_variant_ident(&enum_name, &variant.name);
409        let id_ident = enum_variant_const_id_ident(&enum_name, &variant.name);
410        quote! {
411          #variant_ident => #id_ident.as_slice(),
412        }
413    });
414
415    let into_impl = generate_into_impl(&enum_ident);
416    let serialization = quote! {
417      #into_impl
418
419      pub fn serialize_to_writer(value: &#enum_ident, writer: &mut BufferWriter) {
420        let enumeration_id = #enum_const_id_ident.as_slice();
421        let variant_id = match value {
422          #(#serialization_match_branches)*
423        };
424        writer.add_enumeration_value(enumeration_id, variant_id);
425        writer.add_unit();
426      }
427    };
428
429    // Enum Deserialization.
430    let deserialization_cases = variants.clone().map(|(_, variant)| {
431        let variant_const_id_ident = enum_variant_const_id_ident(&enum_name, &variant.name);
432        let variant_ident = enum_variant_ident(&enum_name, &variant.name);
433        quote! {
434          #variant_const_id_ident => Ok(#variant_ident),
435        }
436    });
437
438    let deserialization = quote! {
439      impl TryFrom<&[u8]> for #enum_ident {
440        type Error = DeserializationError;
441
442        fn try_from(buffer: &[u8]) -> Result<Self, Self::Error> {
443          let mut reader = BufferReader::new(buffer);
444          return deserialize_from_reader(&mut reader, true)
445        }
446      }
447
448      pub fn deserialize_from_reader(reader: &mut BufferReader, check_type: bool) -> Result<#enum_ident, DeserializationError> {
449        if check_type {
450          let type_raw_id_opt = reader.next_type();
451          if type_raw_id_opt.is_none() {
452            return Err(DeserializationError{ message: "missing next type information".to_string() })
453          }
454          if type_raw_id_opt.unwrap() != TYPE_ENUMERATION {
455            return Err(DeserializationError{ message: "next type is not an enumeration".to_string() })
456          }
457        }
458
459        if #enum_const_id_ident != reader.get_structure_field() {
460          return Err(DeserializationError{ message: "missing variant information".to_string() })
461        }
462
463        let variant_raw_id = reader.get_enumeration_value_raw();
464        match variant_raw_id.try_into().expect("enum id is of unexpected length") {
465          #(#deserialization_cases)*
466          _ => Err(DeserializationError{ message: "unexpected variant".to_string() })
467        }
468      }
469    };
470
471    // Conversion to generic `Value`.
472    let to_value_cases = variants.clone().map(|(_, variant)| {
473        let variant_ident = enum_variant_ident(&enum_name, &variant.name);
474        let id_ident = enum_variant_const_id_ident(&enum_name, &variant.name);
475        quote! {
476          #variant_ident => Value::Enumeration(Enumeration {
477            id: Uuid::from_bytes(#enum_const_id_ident),
478            variant_id: Uuid::from_bytes(#id_ident),
479            value: Box::new(Value::Unit),
480          }),
481        }
482    });
483
484    let to_value = quote! {
485      impl Into<Value> for #enum_ident {
486        fn into(self) -> Value {
487          match self {
488            #(#to_value_cases)*
489          }
490        }
491      }
492    };
493
494    // Conversion from generic `Value`.
495    let from_value_cases = variants.map(|(_, variant)| {
496        let variant_const_id_ident = enum_variant_const_id_ident(&enum_name, &variant.name);
497        let variant_ident = enum_variant_ident(&enum_name, &variant.name);
498        quote! {
499          #variant_const_id_ident => Ok(#variant_ident),
500        }
501    });
502
503    let from_value = quote! {
504      impl TryFrom<Value> for #enum_ident {
505        type Error = ConversionError;
506        fn try_from(value: Value) -> Result<Self, Self::Error> {
507          if let Value::Enumeration(as_enum) = value {
508            if *as_enum.id.as_bytes() == #enum_const_id_ident {
509              match *as_enum.variant_id.as_bytes() {
510                #(#from_value_cases)*
511                _ => Err(Self::Error { message: "unexpected variant".to_string() }),
512              }
513            } else {
514              Err(Self::Error {
515                message: "unexpected enum type ID".to_string(),
516              })
517            }
518          } else {
519            Err(Self::Error {
520              message: "unexpected kind".to_string(),
521            })
522          }
523        }
524      }
525    };
526
527    // Putting it all together.
528    let type_source = quote! {
529      #uses
530      #enum_declaration
531      #serialization
532      #deserialization
533      #to_value
534      #from_value
535      #enum_id_declaration
536      #(#variant_id_declarations)*
537    };
538
539    let base_file_name = enumeration.name.to_case(Case::Snake);
540    let file_path = if parent_path.is_empty() {
541        format!("{}.rs", base_file_name)
542    } else {
543        format!("{}/{}.rs", parent_path.replace('.', "/"), base_file_name)
544    };
545    token_stream_to_file(file_path, &type_source)
546}
547
548/// Generates a Rust source file for the given structure.
549/// It contains the type declaration and some functions
550/// to serialize and deserialize values.
551/// It depends on `arora-buffers`, `arora-types`, `arora-registry` and `uuid`.
552pub async fn generate_structure_source(
553    id: &Uuid,
554    structure: &StructureFrozen,
555    registry: &mut dyn ReadableRegistry,
556    parent_path: &str,
557) -> Result<Directory, GenerationError> {
558    // Struct declaration.
559    let name = &structure.name;
560    let struct_ident = type_ident(name);
561    let mut field_declarations = Vec::new();
562    for (_, field) in &structure.fields {
563        let field_ident = variable_ident(&field.name);
564        let field_type_ident =
565            type_ident_from_frozen(&field.ty, registry, PrefixWithMod::Yes).await?;
566        field_declarations.push(quote! { pub #field_ident: #field_type_ident });
567    }
568    let struct_declaration = quote! {
569      pub struct #struct_ident {
570        #(#field_declarations),*
571      }
572    };
573
574    // Struct IDs.
575    let id_str = id.to_string();
576    let id_bytes = RawUuidValue(id);
577    let upper_name = format_ident!("{}", name.to_case(Case::UpperSnake));
578    let const_id_ident = format_ident!("{}_STRUCT_RAW_ID", upper_name);
579    let const_id_doc = format!("{}: {}", name, id_str);
580    let id_declaration = quote! {
581      #[doc = #const_id_doc]
582      pub const #const_id_ident: [u8; 16] = #id_bytes;
583    };
584
585    let field_id_declarations = structure.fields.iter().map(|(field_id, field)| {
586        let field_id_bytes = RawUuidValue(field_id);
587        let field_const_id_ident = struct_field_const_id_ident(name, &field.name);
588        let field_doc = format!("{}: {}", struct_field_ident(name, &field.name), field_id,);
589        quote! {
590          #[doc = #field_doc]
591          pub const #field_const_id_ident: [u8; 16] = #field_id_bytes;
592        }
593    });
594
595    // Struct Serialization.
596    let mut fields_serialization = Vec::new();
597    for (_, field) in &structure.fields {
598        let field_const_id_ident = struct_field_const_id_ident(name, &field.name);
599        let field_ident = variable_ident(&field.name);
600        let value_expression = quote! { value.#field_ident };
601        let serialize =
602            generate_serialize_from_frozen(&field.ty, value_expression, registry).await?;
603        fields_serialization.push(quote! {
604          writer.add_structure_field(&#field_const_id_ident);
605          #serialize
606        });
607    }
608    let field_count = fields_serialization.len() as u32;
609
610    let into_impl = generate_into_impl(&struct_ident);
611    let serialization = quote! {
612      #into_impl
613
614      pub fn serialize_to_writer(value: &#struct_ident, writer: &mut BufferWriter) {
615        let structure_id = #const_id_ident.as_slice();
616        writer.begin_structure(structure_id, #field_count);
617        #(#fields_serialization)*
618      }
619    };
620
621    // Struct Deserialization.
622    // We convert each field we read into an optional,
623    // then we move all of them into the result structure.
624    let mut field_variable_declarations = Vec::new();
625    for (_, field) in &structure.fields {
626        let variable_ident = struct_field_intermediate_variable_ident(name, &field.name);
627        let type_ident = type_ident_from_frozen(&field.ty, registry, PrefixWithMod::Yes).await?;
628        field_variable_declarations
629            .push(quote! { let mut #variable_ident: Option<#type_ident> = None; });
630    }
631
632    let mut deserialization_cases = Vec::new();
633    for (_, field) in &structure.fields {
634        let field_const_id_ident = struct_field_const_id_ident(name, &field.name);
635        let field_variable_ident = struct_field_intermediate_variable_ident(name, &field.name);
636        let deserialize =
637            generate_deserialize_from_frozen(&field.ty, registry, CheckType::Yes).await?;
638        deserialization_cases.push(quote! {
639          if field_raw_id == #field_const_id_ident {
640            #field_variable_ident = Some(#deserialize);
641          }
642        });
643    }
644
645    let struct_field_assignment = structure.fields.iter().map(|(_, field)| {
646        let field_ident = variable_ident(&field.name);
647        let variable_ident = struct_field_intermediate_variable_ident(name, &field.name);
648        quote! { #field_ident: #variable_ident.unwrap() }
649    });
650
651    let try_from_impl = generate_try_from_impl(&struct_ident);
652    let expected_field_count = structure.fields.len();
653    let deserialization = quote! {
654      #try_from_impl
655
656      pub fn deserialize_from_reader(reader: &mut BufferReader, check_type: bool) -> Result<#struct_ident, DeserializationError> {
657        let field_count = if check_type {
658          let type_raw_id_opt = reader.next_type();
659          if type_raw_id_opt.is_none() {
660            return Err(DeserializationError{ message: "missing next type information".to_string() })
661          }
662          if type_raw_id_opt.unwrap() != TYPE_STRUCTURE {
663            return Err(DeserializationError{ message: "next type is not a structure".to_string() })
664          }
665          let (structure_raw_id, field_count) = reader.get_structure();
666          if #const_id_ident != structure_raw_id {
667            return Err(DeserializationError{ message: "structure id does not match".to_string() })
668          }
669          field_count
670        } else {
671          reader.get_structure_raw()
672        };
673        if #expected_field_count != field_count as usize {
674          return Err(DeserializationError{
675            message: format!("expected {} fields, found {}", #expected_field_count, field_count)
676          })
677        }
678
679        #(#field_variable_declarations)*
680        for _ in 0..field_count {
681          let field_raw_id = reader.get_structure_field();
682          #(#deserialization_cases) else* else {
683            return Err(DeserializationError {
684              message: format!("unexpected struct field {}", Uuid::from_slice(field_raw_id).unwrap().to_string())
685            })
686          }
687        }
688
689        Ok(#struct_ident {
690          #(#struct_field_assignment,)*
691        })
692      }
693    };
694
695    // Conversion to/from the generic `Value` vocabulary (mirrors the enum path).
696    // Without this, generated structs only round-trip through the binary buffer
697    // format; with it they flow through the Arora data store as `Value::Structure`.
698    let mut to_value_fields = Vec::new();
699    for (_, field) in &structure.fields {
700        let field_ident = variable_ident(&field.name);
701        let field_const_id_ident = struct_field_const_id_ident(name, &field.name);
702        let field_value = generate_value_from_frozen(&field.ty, quote! { self.#field_ident });
703        to_value_fields.push(quote! {
704          StructureField {
705            id: Uuid::from_bytes(#field_const_id_ident),
706            value: Box::new(#field_value),
707          }
708        });
709    }
710    let to_value = quote! {
711      impl Into<Value> for #struct_ident {
712        fn into(self) -> Value {
713          Value::Structure(Structure {
714            id: Uuid::from_bytes(#const_id_ident),
715            fields: vec![#(#to_value_fields),*],
716          })
717        }
718      }
719    };
720
721    let mut from_value_field_vars = Vec::new();
722    let mut from_value_cases = Vec::new();
723    let mut from_value_assignments = Vec::new();
724    for (_, field) in &structure.fields {
725        let field_ident = variable_ident(&field.name);
726        let field_const_id_ident = struct_field_const_id_ident(name, &field.name);
727        let field_var = struct_field_intermediate_variable_ident(name, &field.name);
728        let field_type_ident =
729            type_ident_from_frozen(&field.ty, registry, PrefixWithMod::Yes).await?;
730        let extract = generate_field_from_value_frozen(
731            &field.ty,
732            quote! { *field.value },
733            &field.name,
734            registry,
735        )
736        .await?;
737        from_value_field_vars
738            .push(quote! { let mut #field_var: Option<#field_type_ident> = None; });
739        from_value_cases.push(quote! { #field_const_id_ident => { #field_var = Some(#extract); } });
740        let missing_message = format!("missing field {}", field.name);
741        from_value_assignments.push(quote! {
742          #field_ident: #field_var.ok_or_else(|| ConversionError { message: #missing_message.to_string() })?
743        });
744    }
745    let from_value = quote! {
746      impl TryFrom<Value> for #struct_ident {
747        type Error = ConversionError;
748        fn try_from(value: Value) -> Result<Self, Self::Error> {
749          if let Value::Structure(as_struct) = value {
750            if *as_struct.id.as_bytes() != #const_id_ident {
751              return Err(ConversionError { message: "unexpected structure type ID".to_string() });
752            }
753            #(#from_value_field_vars)*
754            for field in as_struct.fields {
755              match *field.id.as_bytes() {
756                #(#from_value_cases)*
757                _ => return Err(ConversionError { message: "unexpected struct field".to_string() }),
758              }
759            }
760            Ok(#struct_ident {
761              #(#from_value_assignments,)*
762            })
763          } else {
764            Err(ConversionError { message: "unexpected kind".to_string() })
765          }
766        }
767      }
768    };
769
770    let type_source = quote! {
771      use arora_buffers::*;
772      use uuid::Uuid;
773      use arora_types::value::{ConversionError, Structure, StructureField, Value};
774      use crate::arora_generated::error::DeserializationError;
775      #struct_declaration
776      #serialization
777      #deserialization
778      #to_value
779      #from_value
780      #id_declaration
781      #(#field_id_declarations)*
782    };
783
784    let base_file_name = structure.name.to_case(Case::Snake);
785    let file_path = if parent_path.is_empty() {
786        format!("{}.rs", base_file_name)
787    } else {
788        format!("{}/{}.rs", parent_path.replace('.', "/"), base_file_name)
789    };
790    token_stream_to_file(file_path, &type_source).map_err(GenerationError::VfsError)
791}
792
793/// Generates a virtual source file with wrappers for every symbol imported by the module.
794/// It contains human-readable public functions that can be used by the module implementation,
795/// under the path of the module, as `path::to::module::import`.
796async fn generate_imports_from_module_source(
797    module_id: &Uuid,
798    module_path: &str,
799    imports: &Vec<ImportAsset>,
800    registry: &mut dyn ReadableRegistry,
801) -> Result<Directory, GenerationError> {
802    // Using dependent types.
803    let uses = {
804        let mut dependencies = HashSet::<&FrozenReference>::new();
805        for import in imports {
806            import.import.dependencies(&mut dependencies);
807        }
808        let mut uses = Vec::new();
809        for dep in dependencies {
810            let dep_selector = Selector::Id(dep.id);
811            let type_def = match registry
812                .get_type(
813                    &dep_selector,
814                    &VersionReq::parse(dep.version.to_string().as_str()).unwrap(),
815                )
816                .await
817            {
818                Ok(TypeDefinitionFrozen::Primitive(_)) => continue,
819                Ok(type_definition) => type_definition,
820                Err(RegistryError::NotAType { selector: _ }) => continue,
821                Err(err) => return Err(GenerationError::RegistryError(err)),
822            };
823            let type_ident =
824                type_ident_from_definition(&type_def, &dep.id, registry, PrefixWithMod::Yes)
825                    .await?;
826            uses.push(type_ident);
827        }
828        uses
829    };
830
831    let splitted_module_path: Vec<&str> = module_path.split(".").collect();
832    let module_name = splitted_module_path.last().unwrap();
833
834    // Declare the ID of the module to use it locally.
835    let module_const_id_ident =
836        format_ident!("{}_MODULE_ID", module_name.to_case(Case::UpperSnake),);
837    let module_id_declaration = generate_const_id_declaration(
838        &module_name.to_string(),
839        &module_const_id_ident,
840        module_id,
841        Public::No,
842    );
843
844    // Declare each imported function.
845    let mut functions_declarations = Vec::<TokenStream>::new();
846    for import in imports {
847        let ExportKind::Function(function_symbol) = &import.import.kind;
848        let function_name = &import.import.name;
849        let function_ident = format_ident!("{}", function_name);
850        let mut parameters_declarations = Vec::new();
851        for param in function_symbol.parameters.values() {
852            let maybe_mut = if param.mutable {
853                quote! { mut }
854            } else {
855                quote! {}
856            };
857            let param_name_ident = format_ident!("{}", param.name);
858            let param_type_ident =
859                type_ident_from_frozen(&param.ty, registry, PrefixWithMod::Yes).await?;
860            parameters_declarations.push(quote! {
861              #maybe_mut #param_name_ident: #param_type_ident
862            });
863        }
864        let ret_type_ident =
865            type_ident_from_frozen(&function_symbol.return_ty, registry, PrefixWithMod::Yes)
866                .await?;
867
868        // And implement the call.
869        // First declare the const ids.
870        let function_const_id_ident = function_const_id_ident(function_name);
871        let function_id_declaration = generate_const_id_declaration(
872            function_name,
873            &function_const_id_ident,
874            &import.id,
875            Public::No,
876        );
877        let param_ids_declarations = {
878            let mut param_ids_declarations = Vec::new();
879            for (id, param) in &function_symbol.parameters {
880                param_ids_declarations.push(generate_const_id_declaration(
881                    &format!("{}.{}", function_name, param.name),
882                    &function_param_const_id_ident(function_name, &param.name),
883                    id,
884                    Public::No,
885                ));
886            }
887            param_ids_declarations
888        };
889
890        let ids_declaration = quote! {
891          #function_id_declaration
892          #(#param_ids_declarations)*
893        };
894
895        // Then prepare a call argument structure.
896        // It consists in a struct with the function id as id,
897        // and with one field for each param.
898        let add_args = {
899            let mut add_args = Vec::new();
900            for param in function_symbol.parameters.values() {
901                let function_param_const_id_ident =
902                    function_param_const_id_ident(function_name, &param.name);
903                let param_name_ident = format_ident!("{}", param.name);
904                let serialize_arg = generate_serialize_from_frozen(
905                    &param.ty,
906                    param_name_ident.into_token_stream(),
907                    registry,
908                )
909                .await?;
910                add_args.push(quote! {
911                  writer.add_structure_field(#function_param_const_id_ident.as_slice());
912                  #serialize_arg;
913                });
914            }
915            add_args
916        };
917        let nof_args = add_args.len() as u32;
918        let prepare_call_structure = quote! {
919          let mut writer = BufferWriter::new();
920          writer.begin_structure(#function_const_id_ident.as_slice(), #nof_args);
921          #(#add_args)*
922          let arg = writer.finalize();
923        };
924
925        // Then perform the call.
926        let perform_call = quote! {
927          let result_buffer_addr = unsafe {
928            arora_dispatch(
929              #module_const_id_ident.as_ptr() as usize,
930              #function_const_id_ident.as_ptr() as usize,
931              arg.as_ptr() as usize,
932            )
933          };
934        };
935
936        // Then parse the result.
937        let prepare_parsing = quote! {
938          let result_buffer_ptr = result_buffer_addr as *const u8;
939          let input_size_bytes: &[u8; 4] =
940            unsafe { std::slice::from_raw_parts(result_buffer_ptr, BUFFER_SIZE_SIZE) }
941              .try_into()
942              .expect("input is too small");
943          let input_size = u32::from_le_bytes(*input_size_bytes) as usize;
944          let input =
945            unsafe { std::slice::from_raw_parts(result_buffer_ptr, BUFFER_SIZE_SIZE + input_size) };
946          let mut reader = BufferReader::new(&input);
947        };
948
949        // It consists in a struct with the function id as id,
950        let check_result_struct = quote! {
951          let type_raw_id_opt = reader.next_type();
952          assert!(!type_raw_id_opt.is_none());
953          assert_eq!(type_raw_id_opt.unwrap(), TYPE_STRUCTURE);
954          let (result_struct_id, result_field_count) = reader.get_structure();
955          assert_eq!(result_struct_id, #function_const_id_ident);
956        };
957
958        // with one field for the return value,
959        // plus one field for each param.
960        // Mutate the mutable parameters
961        // and return.
962        let deserialize_ret =
963            generate_deserialize_from_frozen(&function_symbol.return_ty, registry, CheckType::Yes)
964                .await?;
965
966        let process_params = if nof_args > 1 {
967            let declare_mutable_params = {
968                let mut declare_mutable_params = Vec::new();
969                for param in function_symbol.parameters.values() {
970                    if param.mutable {
971                        let param_name_ident = format_ident!("{}", param.name);
972                        declare_mutable_params.push(quote! {
973                          let mut #param_name_ident = None;
974                        })
975                    }
976                }
977                declare_mutable_params
978            };
979
980            let deserialize_params = {
981                let mut deserialize_params = Vec::new();
982                for param in function_symbol.parameters.values() {
983                    if param.mutable {
984                        let param_name_ident = format_ident!("{}", param.name);
985                        let function_param_const_id_ident =
986                            function_param_const_id_ident(function_name, &param.name);
987                        let deserialize_param =
988                            generate_deserialize_from_frozen(&param.ty, registry, CheckType::Yes)
989                                .await?;
990                        deserialize_params.push(quote! {
991              x if *x == #function_param_const_id_ident => *#param_name_ident = #deserialize_param,
992            });
993                    }
994                }
995                deserialize_params
996            };
997
998            quote! {
999              #(#declare_mutable_params)*
1000              for _i in 1u32..#nof_args {
1001                let next_field_id = reader.get_structure_field();
1002                match next_field_id {
1003                  #(#deserialize_params)*
1004                  x => panic!("found unexpected mutated argument id: {:#?}", x),
1005                }
1006              }
1007            }
1008        } else {
1009            quote! {}
1010        };
1011
1012        let process_result = quote! {
1013          assert_eq!(result_field_count, #nof_args);
1014          let first_field_id = reader.get_structure_field();
1015          assert_eq!(first_field_id, #function_const_id_ident);
1016          let ret = #deserialize_ret;
1017          #process_params
1018          ret
1019        };
1020
1021        // This makes an import function.
1022        functions_declarations.push(quote! {
1023          pub fn #function_ident (#(#parameters_declarations),*) -> #ret_type_ident {
1024            #ids_declaration
1025            #prepare_call_structure
1026            #perform_call
1027            #prepare_parsing
1028            #check_result_struct
1029            #process_result
1030          }
1031        });
1032    }
1033
1034    // This makes a module import.
1035    let source = quote! {
1036      #(#uses)*
1037      use arora_buffers::*;
1038      use crate::arora_generated::arora::arora_dispatch;
1039      #module_id_declaration
1040      #(#functions_declarations)*
1041    };
1042
1043    let file_path = splitted_module_path
1044        .iter()
1045        .map(|name| name.to_case(Case::Snake))
1046        .fold(String::new(), |acc, name| {
1047            if acc.is_empty() {
1048                name
1049            } else {
1050                format!("{}/{}", acc, name)
1051            }
1052        })
1053        + ".rs";
1054    token_stream_to_file(file_path, &source).map_err(GenerationError::VfsError)
1055}
1056
1057/// Generates the interface of a module, i.e. the declarations of its exported functions.
1058async fn generate_module_source(
1059    module: &ModuleFrozen,
1060    registry: &mut dyn ReadableRegistry,
1061) -> Result<Directory, GenerationError> {
1062    // Function Uses.
1063    let exports = &module.exports;
1064    let use_functions = exports
1065        .values()
1066        .map(|export| format_ident!("{}", export.name));
1067
1068    // Function and param IDs.
1069    let function_ids = exports.iter().flat_map(|(function_id, export)| {
1070        let ExportKind::Function(function_symbol) = &export.kind;
1071        let mut id_declarations = Vec::with_capacity(function_symbol.parameters.len() + 1);
1072        id_declarations.push(generate_const_id_declaration(
1073            &export.name,
1074            &function_const_id_ident(&export.name),
1075            function_id,
1076            Public::Yes,
1077        ));
1078        for (param_id, param) in &function_symbol.parameters {
1079            id_declarations.push(generate_const_id_declaration(
1080                &format!("{}.{}", export.name, param.name),
1081                &function_param_const_id_ident(&export.name, &param.name),
1082                param_id,
1083                Public::Yes,
1084            ));
1085        }
1086        id_declarations
1087    });
1088
1089    // Functions declarations exported for Arora.
1090    let function_declarations = {
1091        let mut function_declarations = Vec::new();
1092        for (export_id, export) in exports {
1093            let function_ident = format_ident!("{}", export.name);
1094            let ExportKind::Function(function_symbol) = &export.kind;
1095            let const_id_ident = function_const_id_ident(&export.name);
1096
1097            let call_check = quote! {
1098              let mut reader = BufferReader::new(&input);
1099              let type_raw_id_opt = reader.next_type();
1100              if type_raw_id_opt.is_none() {
1101                return Err("input is empty".to_string());
1102              }
1103              if type_raw_id_opt.unwrap() != TYPE_STRUCTURE {
1104                return Err(format!("expected structure input, got type {:?}", type_raw_id_opt));
1105              }
1106              let (structure_raw_id, field_count) = reader.get_structure();
1107              if structure_raw_id != &#const_id_ident {
1108                return Err("function id mismatch in input".to_string());
1109              }
1110            };
1111
1112            let param_declarations = {
1113                let mut param_declarations = Vec::new();
1114                for (param_id, param) in &function_symbol.parameters {
1115                    let param_var_ident = param_ident(param_id, param);
1116                    let param_type_ident =
1117                        type_ident_from_frozen(&param.ty, registry, PrefixWithMod::Yes).await?;
1118                    param_declarations.push(
1119                        quote! { let mut #param_var_ident: Option<#param_type_ident> = None; },
1120                    );
1121                }
1122                param_declarations
1123            };
1124
1125            let deserialization_cases = {
1126                let mut deserialization_cases = Vec::new();
1127                for (param_id, param) in &function_symbol.parameters {
1128                    let param_const_id_ident =
1129                        function_param_const_id_ident(&export.name, &param.name);
1130                    let param_var_ident = param_ident(param_id, param);
1131                    let deserialize =
1132                        generate_deserialize_from_frozen(&param.ty, registry, CheckType::YesResult)
1133                            .await?;
1134                    deserialization_cases.push(quote! {
1135                      if field_raw_id == #param_const_id_ident {
1136                        #param_var_ident = Some(#deserialize);
1137                      }
1138                    });
1139                }
1140                deserialization_cases
1141            };
1142
1143            let deserialize_params = if function_symbol.parameters.is_empty() {
1144                quote! {
1145                  if field_count != 0 {
1146                    return Err(format!("expected 0 parameters but got {}", field_count));
1147                  }
1148                }
1149            } else {
1150                quote! {
1151                  #(#param_declarations)*
1152                  for _ in 0..field_count {
1153                    let field_raw_id = reader.get_structure_field();
1154                    #(#deserialization_cases else)* {
1155                      return Err(format!("unexpected parameter {:?}", field_raw_id));
1156                    }
1157                  }
1158                }
1159            };
1160
1161            let param_args = function_symbol.parameter_ordering.iter().map(|param_id| {
1162                let param = function_symbol.parameters.get(param_id).unwrap();
1163                let param_var_ident = param_ident(param_id, param);
1164                if param.mutable {
1165                    quote! { &mut #param_var_ident }
1166                } else {
1167                    quote! { #param_var_ident }
1168                }
1169            });
1170
1171            let call_and_write_result = {
1172                let result_ident = match &function_symbol.return_ty {
1173                    FrozenTy::Primitive(Primitive { kind }) if *kind == PrimitiveKind::Unit => {
1174                        quote! { _ }
1175                    }
1176                    _ => quote! { result },
1177                };
1178                let serialize_result = generate_serialize_from_frozen(
1179                    &function_symbol.return_ty,
1180                    result_ident.clone(),
1181                    registry,
1182                )
1183                .await?;
1184                quote! {
1185                  let #result_ident = #function_ident (#(#param_args),*);
1186                  #serialize_result;
1187                }
1188            };
1189
1190            let write_mutated_params: Vec<TokenStream> = {
1191                let mut write_mutated_params = Vec::new();
1192                for (param_id, param) in &function_symbol.parameters {
1193                    if param.mutable {
1194                        let param_var_ident = param_ident(param_id, param);
1195                        let param_const_id_ident =
1196                            function_param_const_id_ident(&export.name, &param.name);
1197                        let serialize_param = generate_serialize_from_frozen(
1198                            &param.ty,
1199                            quote! {#param_var_ident.unwrap()},
1200                            registry,
1201                        )
1202                        .await?;
1203                        write_mutated_params.push(quote! {
1204                          writer.add_structure_field(&#param_const_id_ident);
1205                          #serialize_param;
1206                        });
1207                    }
1208                }
1209                write_mutated_params
1210            };
1211            let nof_mutated_params = write_mutated_params.len();
1212
1213            let uuid_suffix = export_id.to_string().replace("-", "_");
1214            let arora_function_ident = format_ident!("arora_function_{}", uuid_suffix);
1215            let doc = export.name.to_string();
1216            function_declarations.push(quote! {
1217        #[doc = #doc]
1218        #[no_mangle]
1219        pub extern "C" fn #arora_function_ident (input_addr: usize) -> usize {
1220          let input_ptr = input_addr as *const u8;
1221          const INPUT_SIZE_SIZE: usize = std::mem::size_of::<u32>();
1222          let input_size_bytes: &[u8; 4] = unsafe {
1223            std::slice::from_raw_parts(input_ptr, INPUT_SIZE_SIZE)
1224          }.try_into().expect("input is too small");
1225          let input_size = u32::from_le_bytes(*input_size_bytes) as usize;
1226          let input = unsafe {
1227            std::slice::from_raw_parts(input_ptr, INPUT_SIZE_SIZE + input_size)
1228          };
1229          let _result: ::std::result::Result<::std::boxed::Box<[u8]>, ::std::string::String> = (|| {
1230            #call_check
1231            #deserialize_params
1232            let mut writer = BufferWriter::new();
1233            writer.begin_structure(&#const_id_ident, (#nof_mutated_params + 1) as u32);
1234            writer.add_structure_field(&#const_id_ident);
1235            #call_and_write_result
1236            #(#write_mutated_params)*
1237            ::std::result::Result::Ok(writer.finalize())
1238          })();
1239          match _result {
1240            ::std::result::Result::Ok(buf) => ::std::boxed::Box::leak(buf).as_ptr() as usize,
1241            ::std::result::Result::Err(msg) => {
1242              let mut writer = BufferWriter::new();
1243              writer.add_error(&msg);
1244              ::std::boxed::Box::leak(writer.finalize()).as_ptr() as usize
1245            }
1246          }
1247        }
1248      });
1249        }
1250        function_declarations
1251    };
1252
1253    // Putting it all together.
1254    let source = quote! {
1255      use arora_buffers::*;
1256      use crate::{arora_generated, #(#use_functions),*};
1257      #(#function_declarations)*
1258      #(#function_ids)*
1259    };
1260    token_stream_to_file("export.rs", &source).map_err(GenerationError::VfsError)
1261}
1262
1263pub fn generate_into_impl(type_ident: &Ident) -> TokenStream {
1264    quote! {
1265      impl Into<Box<[u8]>> for #type_ident {
1266        fn into(self) -> Box<[u8]> {
1267          let mut writer = BufferWriter::new();
1268          serialize_to_writer(&self, &mut writer);
1269          writer.finalize()
1270        }
1271      }
1272    }
1273}
1274
1275pub fn generate_try_from_impl(type_ident: &Ident) -> TokenStream {
1276    quote! {
1277      impl TryFrom<&[u8]> for #type_ident {
1278        type Error = DeserializationError;
1279
1280        fn try_from(buffer: &[u8]) -> Result<Self, Self::Error> {
1281          let mut reader = BufferReader::new(buffer);
1282          return deserialize_from_reader(&mut reader, true)
1283        }
1284      }
1285    }
1286}
1287
1288/// Generate an expression building a generic `Value` from a Rust field value.
1289/// Counterpart of [`generate_serialize_from_frozen`] for the in-memory `Value`
1290/// vocabulary instead of the binary buffer format.
1291fn generate_value_from_frozen(ty: &FrozenTy, value_expression: TokenStream) -> TokenStream {
1292    match ty {
1293        FrozenTy::Primitive(primitive) => match primitive.kind {
1294            PrimitiveKind::Unit => quote! { Value::Unit },
1295            PrimitiveKind::Boolean => quote! { Value::Boolean(#value_expression) },
1296            PrimitiveKind::U8 => quote! { Value::U8(#value_expression) },
1297            PrimitiveKind::U16 => quote! { Value::U16(#value_expression) },
1298            PrimitiveKind::U32 => quote! { Value::U32(#value_expression) },
1299            PrimitiveKind::U64 => quote! { Value::U64(#value_expression) },
1300            PrimitiveKind::I8 => quote! { Value::I8(#value_expression) },
1301            PrimitiveKind::I16 => quote! { Value::I16(#value_expression) },
1302            PrimitiveKind::I32 => quote! { Value::I32(#value_expression) },
1303            PrimitiveKind::I64 => quote! { Value::I64(#value_expression) },
1304            PrimitiveKind::F32 => quote! { Value::F32(#value_expression) },
1305            PrimitiveKind::F64 => quote! { Value::F64(#value_expression) },
1306            PrimitiveKind::String => quote! { Value::String(#value_expression) },
1307            PrimitiveKind::ArrayBoolean => quote! { Value::ArrayBoolean(#value_expression) },
1308            PrimitiveKind::ArrayU8 => quote! { Value::ArrayU8(#value_expression) },
1309            PrimitiveKind::ArrayU16 => quote! { Value::ArrayU16(#value_expression) },
1310            PrimitiveKind::ArrayU32 => quote! { Value::ArrayU32(#value_expression) },
1311            PrimitiveKind::ArrayU64 => quote! { Value::ArrayU64(#value_expression) },
1312            PrimitiveKind::ArrayI8 => quote! { Value::ArrayI8(#value_expression) },
1313            PrimitiveKind::ArrayI16 => quote! { Value::ArrayI16(#value_expression) },
1314            PrimitiveKind::ArrayI32 => quote! { Value::ArrayI32(#value_expression) },
1315            PrimitiveKind::ArrayI64 => quote! { Value::ArrayI64(#value_expression) },
1316            PrimitiveKind::ArrayF32 => quote! { Value::ArrayF32(#value_expression) },
1317            PrimitiveKind::ArrayF64 => quote! { Value::ArrayF64(#value_expression) },
1318            PrimitiveKind::ArrayString => quote! { Value::ArrayString(#value_expression) },
1319        },
1320        // Nested struct/enum types implement `Into<Value>`.
1321        FrozenTy::FrozenScalar(_) => quote! { Into::<Value>::into(#value_expression) },
1322        // Arrays of struct/enum types collapse to `Value::ArrayValue`.
1323        FrozenTy::FrozenArray(_) => quote! {
1324            Value::ArrayValue(
1325                #value_expression.into_iter().map(|__element| Into::<Value>::into(__element)).collect()
1326            )
1327        },
1328    }
1329}
1330
1331/// Generate an expression extracting a Rust field value out of a generic `Value`.
1332/// Counterpart of [`generate_deserialize_from_frozen`]. The produced expression
1333/// may `return Err(ConversionError { .. })` from the enclosing `try_from`.
1334#[async_recursion(?Send)]
1335async fn generate_field_from_value_frozen(
1336    ty: &FrozenTy,
1337    value_expression: TokenStream,
1338    field_name: &str,
1339    registry: &mut dyn ReadableRegistry,
1340) -> Result<TokenStream, GenerationError> {
1341    match ty {
1342        FrozenTy::Primitive(primitive) => {
1343            let mismatch = format!("field {}: unexpected value kind", field_name);
1344            let arm = match primitive.kind {
1345                PrimitiveKind::Unit => quote! { Value::Unit => () },
1346                PrimitiveKind::Boolean => quote! { Value::Boolean(__v) => __v },
1347                PrimitiveKind::U8 => quote! { Value::U8(__v) => __v },
1348                PrimitiveKind::U16 => quote! { Value::U16(__v) => __v },
1349                PrimitiveKind::U32 => quote! { Value::U32(__v) => __v },
1350                PrimitiveKind::U64 => quote! { Value::U64(__v) => __v },
1351                PrimitiveKind::I8 => quote! { Value::I8(__v) => __v },
1352                PrimitiveKind::I16 => quote! { Value::I16(__v) => __v },
1353                PrimitiveKind::I32 => quote! { Value::I32(__v) => __v },
1354                PrimitiveKind::I64 => quote! { Value::I64(__v) => __v },
1355                PrimitiveKind::F32 => quote! { Value::F32(__v) => __v },
1356                PrimitiveKind::F64 => quote! { Value::F64(__v) => __v },
1357                PrimitiveKind::String => quote! { Value::String(__v) => __v },
1358                PrimitiveKind::ArrayBoolean => quote! { Value::ArrayBoolean(__v) => __v },
1359                PrimitiveKind::ArrayU8 => quote! { Value::ArrayU8(__v) => __v },
1360                PrimitiveKind::ArrayU16 => quote! { Value::ArrayU16(__v) => __v },
1361                PrimitiveKind::ArrayU32 => quote! { Value::ArrayU32(__v) => __v },
1362                PrimitiveKind::ArrayU64 => quote! { Value::ArrayU64(__v) => __v },
1363                PrimitiveKind::ArrayI8 => quote! { Value::ArrayI8(__v) => __v },
1364                PrimitiveKind::ArrayI16 => quote! { Value::ArrayI16(__v) => __v },
1365                PrimitiveKind::ArrayI32 => quote! { Value::ArrayI32(__v) => __v },
1366                PrimitiveKind::ArrayI64 => quote! { Value::ArrayI64(__v) => __v },
1367                PrimitiveKind::ArrayF32 => quote! { Value::ArrayF32(__v) => __v },
1368                PrimitiveKind::ArrayF64 => quote! { Value::ArrayF64(__v) => __v },
1369                PrimitiveKind::ArrayString => quote! { Value::ArrayString(__v) => __v },
1370            };
1371            Ok(quote! {
1372                match #value_expression {
1373                    #arm,
1374                    _ => return Err(ConversionError { message: #mismatch.to_string() }),
1375                }
1376            })
1377        }
1378        FrozenTy::FrozenScalar(_) => {
1379            let type_ident = type_ident_from_frozen(ty, registry, PrefixWithMod::Yes).await?;
1380            Ok(quote! { <#type_ident as TryFrom<Value>>::try_from(#value_expression)? })
1381        }
1382        FrozenTy::FrozenArray(array) => {
1383            let element_ty = FrozenTy::FrozenScalar(FrozenScalar {
1384                reference: array.reference.to_owned(),
1385            });
1386            let element_conversion = generate_field_from_value_frozen(
1387                &element_ty,
1388                quote! { __element },
1389                field_name,
1390                registry,
1391            )
1392            .await?;
1393            let mismatch = format!("field {}: expected array value", field_name);
1394            Ok(quote! {
1395                match #value_expression {
1396                    Value::ArrayValue(__items) => {
1397                        let mut __out = Vec::with_capacity(__items.len());
1398                        for __element in __items {
1399                            __out.push(#element_conversion);
1400                        }
1401                        __out
1402                    }
1403                    _ => return Err(ConversionError { message: #mismatch.to_string() }),
1404                }
1405            })
1406        }
1407    }
1408}
1409
1410async fn generate_serialize_from_frozen(
1411    ty: &FrozenTy,
1412    value_expression: TokenStream,
1413    registry: &mut dyn ReadableRegistry,
1414) -> Result<TokenStream, GenerationError> {
1415    match ty {
1416        FrozenTy::Primitive(primitive) => {
1417            let generate_serialize_primitive_array =
1418                |primitive_type_id: &Uuid, write_function: TokenStream| {
1419                    let id_bytes = RawUuidValue(primitive_type_id);
1420                    quote! {
1421                      writer.add_array_primitive(#id_bytes, #value_expression.len() as u32);
1422                      #write_function (#value_expression);
1423                    }
1424                };
1425            Ok(match primitive.kind {
1426                PrimitiveKind::Unit => quote! { writer.add_unit() },
1427                PrimitiveKind::Boolean => quote! { writer.add_boolean(#value_expression) },
1428                PrimitiveKind::U8 => quote! { writer.add_u8(#value_expression) },
1429                PrimitiveKind::U16 => quote! { writer.add_u16(#value_expression) },
1430                PrimitiveKind::U32 => quote! { writer.add_u32(#value_expression) },
1431                PrimitiveKind::U64 => quote! { writer.add_u64(#value_expression) },
1432                PrimitiveKind::I8 => quote! { writer.add_i8(#value_expression) },
1433                PrimitiveKind::I16 => quote! { writer.add_i16(#value_expression) },
1434                PrimitiveKind::I32 => quote! { writer.add_i32(#value_expression) },
1435                PrimitiveKind::I64 => quote! { writer.add_i64(#value_expression) },
1436                PrimitiveKind::F32 => quote! { writer.add_f32(#value_expression) },
1437                PrimitiveKind::F64 => quote! { writer.add_f64(#value_expression) },
1438                PrimitiveKind::String => quote! { writer.add_string(#value_expression.as_str()) },
1439                PrimitiveKind::ArrayBoolean => generate_serialize_primitive_array(
1440                    &BOOLEAN_ID,
1441                    quote! { writer.add_boolean_bulk },
1442                ),
1443                PrimitiveKind::ArrayU8 => {
1444                    generate_serialize_primitive_array(&U8_ID, quote! { writer.add_u8_bulk })
1445                }
1446                PrimitiveKind::ArrayU16 => {
1447                    generate_serialize_primitive_array(&U16_ID, quote! { writer.add_u16_bulk })
1448                }
1449                PrimitiveKind::ArrayU32 => {
1450                    generate_serialize_primitive_array(&U32_ID, quote! { writer.add_u32_bulk })
1451                }
1452                PrimitiveKind::ArrayU64 => {
1453                    generate_serialize_primitive_array(&U64_ID, quote! { writer.add_u64_bulk })
1454                }
1455                PrimitiveKind::ArrayI8 => {
1456                    generate_serialize_primitive_array(&I8_ID, quote! { writer.add_i8_bulk })
1457                }
1458                PrimitiveKind::ArrayI16 => {
1459                    generate_serialize_primitive_array(&I16_ID, quote! { writer.add_i16_bulk })
1460                }
1461                PrimitiveKind::ArrayI32 => {
1462                    generate_serialize_primitive_array(&I32_ID, quote! { writer.add_i32_bulk })
1463                }
1464                PrimitiveKind::ArrayI64 => {
1465                    generate_serialize_primitive_array(&I64_ID, quote! { writer.add_i64_bulk })
1466                }
1467                PrimitiveKind::ArrayF32 => {
1468                    generate_serialize_primitive_array(&F32_ID, quote! { writer.add_f32_bulk })
1469                }
1470                PrimitiveKind::ArrayF64 => {
1471                    generate_serialize_primitive_array(&F64_ID, quote! { writer.add_f64_bulk })
1472                }
1473                PrimitiveKind::ArrayString => {
1474                    let id_bytes = RawUuidValue(&STRING_ID);
1475                    quote! {
1476                      writer.add_array_primitive(#id_bytes, #value_expression.len() as u32);
1477                      for s in #value_expression {
1478                        writer.add_string(s.as_str());
1479                      }
1480                    }
1481                }
1482            })
1483        }
1484        FrozenTy::FrozenScalar(scalar) => {
1485            let mod_prefix = generated_mod_ident_from_id(&scalar.reference.id, registry)
1486                .await
1487                .map_err(GenerationError::RegistryError)?;
1488            Ok(quote! { #mod_prefix serialize_to_writer(&#value_expression, &mut writer) })
1489        }
1490        FrozenTy::FrozenArray(array) => {
1491            let type_def = registry
1492                .get_type(
1493                    &Selector::Id(array.reference.id),
1494                    &VersionReq::parse(array.reference.version.0.to_string().as_str()).unwrap(),
1495                )
1496                .await
1497                .map_err(GenerationError::RegistryError)?;
1498            let id_bytes = RawUuidValue(&array.reference.id);
1499            let add_array_args = quote! { #id_bytes, #value_expression.len() };
1500            let prepare_array = match type_def {
1501                TypeDefinitionFrozen::Primitive(_) => {
1502                    unreachable!("got an array of primitive type instead of a primitive array type")
1503                }
1504                TypeDefinitionFrozen::Enumeration(_) => {
1505                    quote! { writer.add_array_enumeration(#add_array_args); }
1506                }
1507                TypeDefinitionFrozen::Structure(_) => {
1508                    quote! { writer.add_array_structure(#add_array_args); }
1509                }
1510            };
1511            let mod_prefix = generated_mod_ident_from_id(&array.reference.id, registry)
1512                .await
1513                .map_err(GenerationError::RegistryError)?;
1514            let serialize_element =
1515                quote! { #mod_prefix serialize_to_writer(&#value_expression, &mut writer) };
1516            Ok(quote! {
1517              #prepare_array
1518              for element in #value_expression {
1519                #serialize_element;
1520              }
1521            })
1522        }
1523    }
1524}
1525
1526#[async_recursion(?Send)]
1527async fn generate_deserialize_from_frozen(
1528    ty: &FrozenTy,
1529    registry: &mut dyn ReadableRegistry,
1530    check_type: CheckType,
1531) -> Result<TokenStream, GenerationError> {
1532    match ty {
1533        FrozenTy::Primitive(primitive) => {
1534            let type_kind_ident = type_kind_ident_from_primitive(&primitive.kind);
1535
1536            let generate_deserialize = |deserialize: TokenStream| {
1537                let type_check = match check_type {
1538                    CheckType::Yes => quote! {
1539                      {
1540                        let _next_type = reader.next_type();
1541                        assert_eq!(_next_type, Some(#type_kind_ident), "type mismatch");
1542                      }
1543                    },
1544                    CheckType::YesResult => quote! {
1545                      {
1546                        let _next_type = reader.next_type();
1547                        if _next_type != Some(#type_kind_ident) {
1548                          return Err(format!("type mismatch: expected {:?} but got {:?}", #type_kind_ident, _next_type));
1549                        }
1550                      }
1551                    },
1552                    CheckType::No => quote! {},
1553                };
1554                quote! {{
1555                    #type_check
1556                    #deserialize
1557                  }
1558                }
1559            };
1560
1561            let generate_deserialize_base_type = |type_ident: TokenStream| {
1562                let getter = format_ident!("get_{}", type_ident.to_string());
1563                generate_deserialize(quote! { reader.#getter() })
1564            };
1565
1566            let generate_deserialize_array = |deserialize_array: TokenStream| {
1567                let array_type_check = match check_type {
1568                    CheckType::Yes => quote! {
1569                      {
1570                        let _at = reader.next_type();
1571                        assert_eq!(_at, Some(TYPE_ARRAY));
1572                      }
1573                      let (ty, count) = reader.get_array();
1574                      assert_eq!(ty, #type_kind_ident);
1575                    },
1576                    _ => quote! {
1577                      {
1578                        let _at = reader.next_type();
1579                        if _at != Some(TYPE_ARRAY) {
1580                          return Err(format!("expected array, got {:?}", _at));
1581                        }
1582                      }
1583                      let (ty, count) = reader.get_array();
1584                      if ty != #type_kind_ident {
1585                        return Err(format!("expected array element type {:?}, got {:?}", #type_kind_ident, ty));
1586                      }
1587                    },
1588                };
1589                quote! {{
1590                    #array_type_check
1591                    #deserialize_array
1592                  }
1593                }
1594            };
1595            Ok(match primitive.kind {
1596                PrimitiveKind::Unit => {
1597                    quote! { Result::<(), DeserializationError>::Ok(reader.get_unit()) }
1598                }
1599                PrimitiveKind::Boolean => generate_deserialize(quote! { reader.get_boolean() }),
1600                PrimitiveKind::U8 => generate_deserialize_base_type(quote! {u8}),
1601                PrimitiveKind::U16 => generate_deserialize_base_type(quote! {u16}),
1602                PrimitiveKind::U32 => generate_deserialize_base_type(quote! {u32}),
1603                PrimitiveKind::U64 => generate_deserialize_base_type(quote! {u64}),
1604                PrimitiveKind::I8 => generate_deserialize_base_type(quote! {i8}),
1605                PrimitiveKind::I16 => generate_deserialize_base_type(quote! {i16}),
1606                PrimitiveKind::I32 => generate_deserialize_base_type(quote! {i32}),
1607                PrimitiveKind::I64 => generate_deserialize_base_type(quote! {i64}),
1608                PrimitiveKind::F32 => generate_deserialize_base_type(quote! {f32}),
1609                PrimitiveKind::F64 => generate_deserialize_base_type(quote! {f64}),
1610                PrimitiveKind::String => generate_deserialize(quote! {
1611                  reader.get_string().to_string()
1612                }),
1613                PrimitiveKind::ArrayBoolean => generate_deserialize_array(quote! {
1614                  reader.get_boolean_bulk(count)
1615                }),
1616                PrimitiveKind::ArrayU8 => generate_deserialize_array(quote! {
1617                  reader.get_u8_bulk(count)
1618                }),
1619                PrimitiveKind::ArrayU16 => generate_deserialize_array(quote! {
1620                  reader.get_u16_bulk(count)
1621                }),
1622                PrimitiveKind::ArrayU32 => generate_deserialize_array(quote! {
1623                  reader.get_u32_bulk(count)
1624                }),
1625                PrimitiveKind::ArrayU64 => generate_deserialize_array(quote! {
1626                  reader.get_u64_bulk(count)
1627                }),
1628                PrimitiveKind::ArrayI8 => generate_deserialize_array(quote! {
1629                  reader.get_i8_bulk(count)
1630                }),
1631                PrimitiveKind::ArrayI16 => generate_deserialize_array(quote! {
1632                  reader.get_i16_bulk(count)
1633                }),
1634                PrimitiveKind::ArrayI32 => generate_deserialize_array(quote! {
1635                  reader.get_i32_bulk(count)
1636                }),
1637                PrimitiveKind::ArrayI64 => generate_deserialize_array(quote! {
1638                  reader.get_i64_bulk(count)
1639                }),
1640                PrimitiveKind::ArrayF32 => generate_deserialize_array(quote! {
1641                  reader.get_f32_bulk(count)
1642                }),
1643                PrimitiveKind::ArrayF64 => generate_deserialize_array(quote! {
1644                  reader.get_f64_bulk(count)
1645                }),
1646                PrimitiveKind::ArrayString => {
1647                    let deserialize_element = generate_deserialize(quote! {
1648                      Result::<String, DeserializationError>::Ok(reader.get_string().to_string())
1649                    });
1650                    generate_deserialize_array(quote! {
1651                      let mut res = Vec::<String>::with_capacity(count as usize);
1652                      for _i in 0..count {
1653                        res.push(#deserialize_element);
1654                      }
1655                      res
1656                    })
1657                }
1658            })
1659        }
1660        FrozenTy::FrozenScalar(scalar) => {
1661            let mod_prefix = generated_mod_ident_from_id(&scalar.reference.id, registry)
1662                .await
1663                .map_err(GenerationError::RegistryError)?;
1664            let check_type_bool = check_type != CheckType::No;
1665            let type_ident =
1666                type_ident_from_id(&scalar.reference.id, registry, PrefixWithMod::Yes).await?;
1667            let type_str = type_ident.to_string();
1668            Ok(match check_type {
1669                CheckType::YesResult => quote! {
1670                  #mod_prefix deserialize_from_reader(&mut reader, #check_type_bool)
1671                    .map_err(|e| format!("failed to deserialize {}: {}", #type_str, e))?
1672                },
1673                _ => quote! {
1674                  #mod_prefix deserialize_from_reader(&mut reader, #check_type_bool)
1675                    .expect(&format!("failed to deserialize {}", #type_str))
1676                },
1677            })
1678        }
1679        FrozenTy::FrozenArray(array) => {
1680            let type_ident =
1681                type_ident_from_id(&array.reference.id, registry, PrefixWithMod::Yes).await?;
1682            let deserialize_element = generate_deserialize_from_frozen(
1683                &FrozenTy::FrozenScalar(FrozenScalar {
1684                    reference: array.reference.to_owned(),
1685                }),
1686                registry,
1687                CheckType::No,
1688            )
1689            .await?;
1690            let type_enum = match registry
1691                .type_of(&Selector::Id(array.reference.id.to_owned()))
1692                .await
1693                .map_err(GenerationError::RegistryError)?
1694            {
1695                RecordType::Enumeration => quote! { TYPE_ENUMERATION },
1696                RecordType::Structure => quote! { TYPE_STRUCTURE },
1697                _ => unreachable!("unexpected type of element in array"),
1698            };
1699            let raw_id = RawUuidValue(&array.reference.id);
1700            let array_checks = match check_type {
1701                CheckType::Yes => quote! {
1702                  {
1703                    let _at = reader.next_type();
1704                    assert_eq!(_at, Some(TYPE_ARRAY));
1705                  }
1706                  let (ty, count) = reader.get_array();
1707                  assert_eq!(ty, #type_enum);
1708                  {
1709                    let _id = reader.get_structure_field();
1710                    assert_eq!(_id, &#raw_id);
1711                  }
1712                },
1713                _ => quote! {
1714                  {
1715                    let _at = reader.next_type();
1716                    if _at != Some(TYPE_ARRAY) {
1717                      return Err(format!("expected array, got {:?}", _at));
1718                    }
1719                  }
1720                  let (ty, count) = reader.get_array();
1721                  if ty != #type_enum {
1722                    return Err(format!("expected array element type {:?}, got {:?}", #type_enum, ty));
1723                  }
1724                  {
1725                    let _id = reader.get_structure_field();
1726                    if _id != &#raw_id {
1727                      return Err("array type id mismatch".to_string());
1728                    }
1729                  }
1730                },
1731            };
1732            Ok(quote! {{
1733              #array_checks
1734              let mut res = Vec::<#type_ident>::with_capacity(count as usize);
1735              for _i in 0..count {
1736                res.push(#deserialize_element);
1737              }
1738              res
1739            }})
1740        }
1741    }
1742}
1743
1744pub fn token_stream_to_file<P: AsRef<path::Path>>(
1745    file_path: P,
1746    tokens: &TokenStream,
1747) -> Result<Directory, VfsError> {
1748    let file_path = file_path.as_ref();
1749    let file_name = file_path.file_name().unwrap().to_str().unwrap();
1750    let parent_path = file_path.parent().unwrap();
1751    let mut output = Directory::new();
1752    let parent_dir = match output.ensure_directories(parent_path) {
1753        Ok(dir) => dir,
1754        Err(VfsError::EmptyPath) => &mut output,
1755        Err(err) => return Err(err),
1756    };
1757    parent_dir.insert(file_name, File::new(tokens.to_string()))?;
1758    Ok(output)
1759}
1760
1761pub fn type_ident(type_name: &String) -> Ident {
1762    format_ident!("{}", type_name.to_case(Case::UpperCamel))
1763}
1764
1765pub fn struct_field_const_id_ident(struct_name: &String, field_name: &String) -> Ident {
1766    format_ident!(
1767        "{}_{}_FIELD_RAW_ID",
1768        struct_name.to_case(Case::UpperSnake),
1769        field_name.to_case(Case::UpperSnake)
1770    )
1771}
1772
1773pub fn struct_field_ident(struct_name: &String, field_name: &String) -> TokenStream {
1774    format!(
1775        "{}::{}",
1776        struct_name.to_case(Case::UpperCamel),
1777        field_name.to_case(Case::UpperCamel)
1778    )
1779    .parse()
1780    .unwrap()
1781}
1782
1783pub fn struct_field_intermediate_variable_ident(
1784    struct_name: &String,
1785    field_name: &String,
1786) -> Ident {
1787    format_ident!(
1788        "{}_{}",
1789        struct_name.to_case(Case::Snake),
1790        field_name.to_case(Case::Snake),
1791    )
1792}
1793
1794pub fn enum_variant_ident(enum_name: &String, variant_name: &String) -> TokenStream {
1795    format!(
1796        "{}::{}",
1797        enum_name.to_case(Case::UpperCamel),
1798        variant_name.to_case(Case::UpperCamel),
1799    )
1800    .parse()
1801    .unwrap()
1802}
1803
1804pub fn enum_variant_const_id_ident(enum_name: &String, variant_name: &String) -> Ident {
1805    format_ident!(
1806        "{}_{}_VARIANT_RAW_ID",
1807        enum_name.to_case(Case::UpperSnake),
1808        variant_name.to_case(Case::UpperSnake),
1809    )
1810}
1811
1812/// Generates the const declaration of the ID associated to the given name (and ident).
1813pub fn generate_const_id_declaration(
1814    name: &String,
1815    ident: &Ident,
1816    id: &Uuid,
1817    public: Public,
1818) -> TokenStream {
1819    let id_str = id.to_string();
1820    let id_bytes = RawUuidValue(id);
1821    let const_id_doc = format!("{}: {}", name, id_str);
1822    let maybe_pub = match public {
1823        Public::Yes => quote! { pub },
1824        Public::No => quote! {},
1825    };
1826    quote! {
1827      #[doc = #const_id_doc]
1828      #maybe_pub const #ident: [u8; 16] = #id_bytes;
1829    }
1830}
1831
1832pub fn function_const_id_ident(function_name: &String) -> Ident {
1833    format_ident!(
1834        "{}_FUNCTION_RAW_ID",
1835        function_name.to_case(Case::UpperSnake),
1836    )
1837}
1838
1839pub fn function_param_const_id_ident(function_name: &String, param_name: &String) -> Ident {
1840    format_ident!(
1841        "{}_{}_PARAMETER_RAW_ID",
1842        function_name.to_case(Case::UpperSnake),
1843        param_name.to_case(Case::UpperSnake),
1844    )
1845}
1846
1847fn param_ident(param_id: &Uuid, param: &Parameter) -> Ident {
1848    let param_id_sanitized = param_id.to_string().replace("-", "");
1849    format_ident!(
1850        "param_{}_{}",
1851        param.name.to_case(Case::Snake),
1852        param_id_sanitized
1853    )
1854}
1855
1856pub fn variable_ident(name: &String) -> Ident {
1857    format_ident!("{}", name.to_case(Case::Snake))
1858}
1859
1860async fn type_ident_from_frozen(
1861    ty: &FrozenTy,
1862    registry: &mut dyn ReadableRegistry,
1863    with_mod: PrefixWithMod,
1864) -> Result<TokenStream, GenerationError> {
1865    Ok(match ty {
1866        FrozenTy::Primitive(primitive) => match *primitive {
1867            Primitive::UNIT => quote! { () },
1868            Primitive::BOOLEAN => quote!(bool),
1869            Primitive::U8 => quote!(u8),
1870            Primitive::U16 => quote!(u16),
1871            Primitive::U32 => quote!(u32),
1872            Primitive::U64 => quote!(u64),
1873            Primitive::I8 => quote!(i8),
1874            Primitive::I16 => quote!(i16),
1875            Primitive::I32 => quote!(i32),
1876            Primitive::I64 => quote!(i64),
1877            Primitive::F32 => quote!(f32),
1878            Primitive::F64 => quote!(f64),
1879            Primitive::STRING => quote!(String),
1880            Primitive::ARRAY_BOOLEAN => quote!(Vec<bool>),
1881            Primitive::ARRAY_U8 => quote!(Vec<u8>),
1882            Primitive::ARRAY_U16 => quote!(Vec<u16>),
1883            Primitive::ARRAY_U32 => quote!(Vec<u32>),
1884            Primitive::ARRAY_U64 => quote!(Vec<u64>),
1885            Primitive::ARRAY_I8 => quote!(Vec<i8>),
1886            Primitive::ARRAY_I16 => quote!(Vec<i16>),
1887            Primitive::ARRAY_I32 => quote!(Vec<i32>),
1888            Primitive::ARRAY_I64 => quote!(Vec<i64>),
1889            Primitive::ARRAY_F32 => quote!(Vec<f32>),
1890            Primitive::ARRAY_F64 => quote!(Vec<f64>),
1891            Primitive::ARRAY_STRING => quote!(Vec<String>),
1892        },
1893        FrozenTy::FrozenScalar(scalar) => {
1894            type_ident_from_id(&scalar.reference.id, registry, with_mod).await?
1895        }
1896        FrozenTy::FrozenArray(array) => {
1897            let type_ident = type_ident_from_id(&array.reference.id, registry, with_mod).await?;
1898            quote! { Vec<#type_ident> }
1899        }
1900    })
1901}
1902
1903async fn type_ident_from_id(
1904    id: &Uuid,
1905    registry: &mut dyn ReadableRegistry,
1906    with_mod: PrefixWithMod,
1907) -> Result<TokenStream, GenerationError> {
1908    let type_def = registry
1909        .get_type(&Selector::Id(id.to_owned()), &VersionReq::STAR)
1910        .await
1911        .map_err(GenerationError::RegistryError)?;
1912    type_ident_from_definition(&type_def, id, registry, with_mod).await
1913}
1914
1915async fn type_ident_from_definition(
1916    type_def: &TypeDefinitionFrozen,
1917    id: &Uuid,
1918    registry: &mut dyn ReadableRegistry,
1919    with_mod: PrefixWithMod,
1920) -> Result<TokenStream, GenerationError> {
1921    Ok(match type_def {
1922        TypeDefinitionFrozen::Primitive(primitive) => match primitive {
1923            PrimitiveKind::Unit => quote! { () },
1924            PrimitiveKind::Boolean => quote!(bool),
1925            PrimitiveKind::U8 => quote!(u8),
1926            PrimitiveKind::U16 => quote!(u16),
1927            PrimitiveKind::U32 => quote!(u32),
1928            PrimitiveKind::U64 => quote!(u64),
1929            PrimitiveKind::I8 => quote!(i8),
1930            PrimitiveKind::I16 => quote!(i16),
1931            PrimitiveKind::I32 => quote!(i32),
1932            PrimitiveKind::I64 => quote!(i64),
1933            PrimitiveKind::F32 => quote!(f32),
1934            PrimitiveKind::F64 => quote!(f64),
1935            PrimitiveKind::String => quote!(String),
1936            PrimitiveKind::ArrayBoolean => quote!(Vec<bool>),
1937            PrimitiveKind::ArrayU8 => quote!(Vec<u8>),
1938            PrimitiveKind::ArrayU16 => quote!(Vec<u16>),
1939            PrimitiveKind::ArrayU32 => quote!(Vec<u32>),
1940            PrimitiveKind::ArrayU64 => quote!(Vec<u64>),
1941            PrimitiveKind::ArrayI8 => quote!(Vec<i8>),
1942            PrimitiveKind::ArrayI16 => quote!(Vec<i16>),
1943            PrimitiveKind::ArrayI32 => quote!(Vec<i32>),
1944            PrimitiveKind::ArrayI64 => quote!(Vec<i64>),
1945            PrimitiveKind::ArrayF32 => quote!(Vec<f32>),
1946            PrimitiveKind::ArrayF64 => quote!(Vec<f64>),
1947            PrimitiveKind::ArrayString => quote!(Vec<String>),
1948        },
1949        TypeDefinitionFrozen::Enumeration(enumeration) => {
1950            type_ident_from_name_and_id(&enumeration.name, id, registry, with_mod)
1951                .await
1952                .map_err(GenerationError::RegistryError)?
1953        }
1954        TypeDefinitionFrozen::Structure(structure) => {
1955            type_ident_from_name_and_id(&structure.name, id, registry, with_mod)
1956                .await
1957                .map_err(GenerationError::RegistryError)?
1958        }
1959    })
1960}
1961
1962async fn type_ident_from_name_and_id(
1963    name: &String,
1964    id: &Uuid,
1965    registry: &mut dyn ReadableRegistry,
1966    with_mod: PrefixWithMod,
1967) -> Result<TokenStream, RegistryError> {
1968    let mod_prefix = match with_mod {
1969        PrefixWithMod::Yes => generated_mod_ident_from_id(id, registry).await?,
1970        PrefixWithMod::No => quote! {},
1971    };
1972    let type_ident = type_ident(name);
1973    Ok(quote! { #mod_prefix #type_ident })
1974}
1975
1976fn type_kind_ident_from_primitive(primitive: &PrimitiveKind) -> TokenStream {
1977    match primitive {
1978        PrimitiveKind::Unit => quote! { TYPE_UNIT },
1979        PrimitiveKind::Boolean => quote! { TYPE_BOOLEAN },
1980        PrimitiveKind::U8 => quote! { TYPE_U8 },
1981        PrimitiveKind::U16 => quote! { TYPE_U16 },
1982        PrimitiveKind::U32 => quote! { TYPE_U32 },
1983        PrimitiveKind::U64 => quote! { TYPE_U64 },
1984        PrimitiveKind::I8 => quote! { TYPE_I8 },
1985        PrimitiveKind::I16 => quote! { TYPE_I16 },
1986        PrimitiveKind::I32 => quote! { TYPE_I32 },
1987        PrimitiveKind::I64 => quote! { TYPE_I64 },
1988        PrimitiveKind::F32 => quote! { TYPE_F32 },
1989        PrimitiveKind::F64 => quote! { TYPE_F64 },
1990        PrimitiveKind::String => quote! { TYPE_STRING },
1991        PrimitiveKind::ArrayBoolean
1992        | PrimitiveKind::ArrayU8
1993        | PrimitiveKind::ArrayU16
1994        | PrimitiveKind::ArrayU32
1995        | PrimitiveKind::ArrayU64
1996        | PrimitiveKind::ArrayI8
1997        | PrimitiveKind::ArrayI16
1998        | PrimitiveKind::ArrayI32
1999        | PrimitiveKind::ArrayI64
2000        | PrimitiveKind::ArrayF32
2001        | PrimitiveKind::ArrayF64
2002        | PrimitiveKind::ArrayString => quote! { TYPE_ARRAY },
2003    }
2004}
2005
2006/// Generates the identifier for the mod publishing the record of the given id.
2007async fn generated_mod_ident_from_id(
2008    id: &Uuid,
2009    registry: &mut dyn ReadableRegistry,
2010) -> Result<TokenStream, RegistryError> {
2011    let mod_path = registry.resolve_id(id).await?;
2012    let mod_ident = mod_ident_from_path(&mod_path);
2013    Ok(quote! { arora_generated::#mod_ident })
2014}
2015
2016fn mod_ident_from_path(path: &str) -> TokenStream {
2017    let path_parts = path.split(".").collect::<Vec<&str>>();
2018    let path_parts = path_parts
2019        .iter()
2020        .map(|part| format_ident!("{}", part.to_case(Case::Snake)));
2021    quote! { #(#path_parts ::)* }
2022}
2023
2024#[derive(Clone, Copy, Eq, PartialEq)]
2025pub enum CheckType {
2026    /// assert_eq!-based checks — panics on mismatch (struct/import deserialization contexts)
2027    Yes,
2028    /// return Err(String) — for export handler Result<Box<[u8]>, String> closure context
2029    YesResult,
2030    No,
2031}
2032
2033#[derive(Clone, Copy, Eq, PartialEq)]
2034pub enum PrefixWithMod {
2035    Yes,
2036    No,
2037}
2038
2039#[derive(Clone, Copy, Eq, PartialEq)]
2040pub enum Public {
2041    Yes,
2042    No,
2043}
2044
2045#[derive(Display, Debug)]
2046pub enum GenerationError {
2047    ModuleDeclarationError(ModuleDeclarationError),
2048    RegistryError(RegistryError),
2049    VfsError(VfsError),
2050    IoError(std::io::Error),
2051    Generic(String),
2052}
2053
2054impl std::error::Error for GenerationError {}
2055
2056/// A helper to format a Uuid into an inlined byte array.
2057pub struct RawUuidValue<'a>(pub &'a Uuid);
2058
2059impl<'a> Display for RawUuidValue<'a> {
2060    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2061        write!(f, "{:#04x?}", self.0.as_bytes())
2062    }
2063}
2064
2065impl<'a> ToTokens for RawUuidValue<'a> {
2066    fn to_tokens(&self, tokens: &mut TokenStream) {
2067        let new_tokens: TokenStream = self.to_string().parse().unwrap();
2068        tokens.extend(new_tokens);
2069    }
2070}