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: Vec<TokenStream> = variants
408        .clone()
409        .map(|(_, variant)| {
410            let variant_ident = enum_variant_ident(&enum_name, &variant.name);
411            let id_ident = enum_variant_const_id_ident(&enum_name, &variant.name);
412            quote! {
413              #variant_ident => #id_ident.as_slice(),
414            }
415        })
416        .collect();
417
418    let into_impl = generate_into_impl(&enum_ident);
419    let serialization = quote! {
420      #into_impl
421
422      pub fn serialize_to_writer(value: &#enum_ident, writer: &mut BufferWriter) {
423        let enumeration_id = #enum_const_id_ident.as_slice();
424        let variant_id = match value {
425          #(#serialization_match_branches)*
426        };
427        writer.add_enumeration_value(enumeration_id, variant_id);
428        writer.add_unit();
429      }
430
431      /// Serializes the enumeration as a *raw* element — the variant id and its
432      /// payload only, without the leading `TYPE_ENUMERATION` tag or the 16-byte
433      /// enumeration id (carried once by the array header). Mirrors
434      /// `arora_buffers::serde_uuid`'s `Value::ArrayEnumeration` so an array of
435      /// enumerations marshals identically across the `arora_call` boundary.
436      pub fn serialize_to_writer_raw(value: &#enum_ident, writer: &mut BufferWriter) {
437        let variant_id = match value {
438          #(#serialization_match_branches)*
439        };
440        writer.add_enumeration_value_raw(variant_id);
441        writer.add_unit();
442      }
443    };
444
445    // Enum Deserialization.
446    let deserialization_cases = variants.clone().map(|(_, variant)| {
447        let variant_const_id_ident = enum_variant_const_id_ident(&enum_name, &variant.name);
448        let variant_ident = enum_variant_ident(&enum_name, &variant.name);
449        quote! {
450          #variant_const_id_ident => Ok(#variant_ident),
451        }
452    });
453
454    let deserialization = quote! {
455      impl TryFrom<&[u8]> for #enum_ident {
456        type Error = DeserializationError;
457
458        fn try_from(buffer: &[u8]) -> Result<Self, Self::Error> {
459          let mut reader = BufferReader::new(buffer);
460          return deserialize_from_reader(&mut reader, true)
461        }
462      }
463
464      pub fn deserialize_from_reader(reader: &mut BufferReader, check_type: bool) -> Result<#enum_ident, DeserializationError> {
465        if check_type {
466          let type_raw_id_opt = reader.next_type();
467          if type_raw_id_opt.is_none() {
468            return Err(DeserializationError{ message: "missing next type information".to_string() })
469          }
470          if type_raw_id_opt.unwrap() != TYPE_ENUMERATION {
471            return Err(DeserializationError{ message: "next type is not an enumeration".to_string() })
472          }
473          // The enumeration id is only present in the full (non-raw) encoding.
474          // In the raw element form used inside arrays it is carried once by the
475          // array header, so it is not read per element.
476          if #enum_const_id_ident != reader.get_structure_field() {
477            return Err(DeserializationError{ message: "missing variant information".to_string() })
478          }
479        }
480
481        let variant_raw_id = reader.get_enumeration_value_raw();
482        // Consume the variant's payload written by `add_unit` on the
483        // serialization side (enumeration values carry a `Unit`). Without this
484        // the reader is left one byte short, corrupting whatever value follows
485        // (for example the next element of an array of enumerations).
486        reader.next_type();
487        match variant_raw_id.try_into().expect("enum id is of unexpected length") {
488          #(#deserialization_cases)*
489          _ => Err(DeserializationError{ message: "unexpected variant".to_string() })
490        }
491      }
492    };
493
494    // Conversion to generic `Value`.
495    let to_value_cases = variants.clone().map(|(_, variant)| {
496        let variant_ident = enum_variant_ident(&enum_name, &variant.name);
497        let id_ident = enum_variant_const_id_ident(&enum_name, &variant.name);
498        quote! {
499          #variant_ident => Value::Enumeration(Enumeration {
500            id: Uuid::from_bytes(#enum_const_id_ident),
501            variant_id: Uuid::from_bytes(#id_ident),
502            value: Box::new(Value::Unit),
503          }),
504        }
505    });
506
507    let to_value = quote! {
508      impl Into<Value> for #enum_ident {
509        fn into(self) -> Value {
510          match self {
511            #(#to_value_cases)*
512          }
513        }
514      }
515    };
516
517    // Conversion from generic `Value`.
518    let from_value_cases = variants.map(|(_, variant)| {
519        let variant_const_id_ident = enum_variant_const_id_ident(&enum_name, &variant.name);
520        let variant_ident = enum_variant_ident(&enum_name, &variant.name);
521        quote! {
522          #variant_const_id_ident => Ok(#variant_ident),
523        }
524    });
525
526    let from_value = quote! {
527      impl TryFrom<Value> for #enum_ident {
528        type Error = ConversionError;
529        fn try_from(value: Value) -> Result<Self, Self::Error> {
530          if let Value::Enumeration(as_enum) = value {
531            if *as_enum.id.as_bytes() == #enum_const_id_ident {
532              match *as_enum.variant_id.as_bytes() {
533                #(#from_value_cases)*
534                _ => Err(Self::Error { message: "unexpected variant".to_string() }),
535              }
536            } else {
537              Err(Self::Error {
538                message: "unexpected enum type ID".to_string(),
539              })
540            }
541          } else {
542            Err(Self::Error {
543              message: "unexpected kind".to_string(),
544            })
545          }
546        }
547      }
548    };
549
550    // Putting it all together.
551    let type_source = quote! {
552      #uses
553      #enum_declaration
554      #serialization
555      #deserialization
556      #to_value
557      #from_value
558      #enum_id_declaration
559      #(#variant_id_declarations)*
560    };
561
562    let base_file_name = enumeration.name.to_case(Case::Snake);
563    let file_path = if parent_path.is_empty() {
564        format!("{}.rs", base_file_name)
565    } else {
566        format!("{}/{}.rs", parent_path.replace('.', "/"), base_file_name)
567    };
568    token_stream_to_file(file_path, &type_source)
569}
570
571/// Generates a Rust source file for the given structure.
572/// It contains the type declaration and some functions
573/// to serialize and deserialize values.
574/// It depends on `arora-buffers`, `arora-types`, `arora-registry` and `uuid`.
575pub async fn generate_structure_source(
576    id: &Uuid,
577    structure: &StructureFrozen,
578    registry: &mut dyn ReadableRegistry,
579    parent_path: &str,
580) -> Result<Directory, GenerationError> {
581    // Struct declaration.
582    let name = &structure.name;
583    let struct_ident = type_ident(name);
584    // A field that is a *scalar* reference back to this very structure would make
585    // the Rust type infinitely sized, so it is `Box`-wrapped. (Array/`Vec`
586    // self-references don't need this — a `Vec` is already heap-allocated — and
587    // are what make a recursive type actually constructible.)
588    let is_self_ref = |field: &arora_types::record::structure::frozen::StructureField| matches!(&field.ty, FrozenTy::FrozenScalar(scalar) if scalar.reference.id == *id);
589    let mut field_declarations = Vec::new();
590    for (_, field) in &structure.fields {
591        let field_ident = variable_ident(&field.name);
592        let mut field_type_ident =
593            type_ident_from_frozen(&field.ty, registry, PrefixWithMod::Yes).await?;
594        if is_self_ref(field) {
595            field_type_ident = quote! { Box<#field_type_ident> };
596        }
597        field_declarations.push(quote! { pub #field_ident: #field_type_ident });
598    }
599    let struct_declaration = quote! {
600      #[derive(Debug, PartialEq, Clone)]
601      pub struct #struct_ident {
602        #(#field_declarations),*
603      }
604    };
605
606    // Struct IDs.
607    let id_str = id.to_string();
608    let id_bytes = RawUuidValue(id);
609    let upper_name = format_ident!("{}", name.to_case(Case::UpperSnake));
610    let const_id_ident = format_ident!("{}_STRUCT_RAW_ID", upper_name);
611    let const_id_doc = format!("{}: {}", name, id_str);
612    let id_declaration = quote! {
613      #[doc = #const_id_doc]
614      pub const #const_id_ident: [u8; 16] = #id_bytes;
615    };
616
617    let field_id_declarations = structure.fields.iter().map(|(field_id, field)| {
618        let field_id_bytes = RawUuidValue(field_id);
619        let field_const_id_ident = struct_field_const_id_ident(name, &field.name);
620        let field_doc = format!("{}: {}", struct_field_ident(name, &field.name), field_id,);
621        quote! {
622          #[doc = #field_doc]
623          pub const #field_const_id_ident: [u8; 16] = #field_id_bytes;
624        }
625    });
626
627    // Struct Serialization.
628    let mut fields_serialization = Vec::new();
629    for (_, field) in &structure.fields {
630        let field_const_id_ident = struct_field_const_id_ident(name, &field.name);
631        let field_ident = variable_ident(&field.name);
632        let value_expression = quote! { value.#field_ident };
633        let serialize =
634            generate_serialize_from_frozen(&field.ty, value_expression, registry).await?;
635        // The trailing `;` terminates the field's serialization statement so
636        // that consecutive fields don't run together (a struct with more than
637        // one field would otherwise emit `writer.add_f32(..) writer.add_..`).
638        fields_serialization.push(quote! {
639          writer.add_structure_field(&#field_const_id_ident);
640          #serialize;
641        });
642    }
643    let field_count = fields_serialization.len() as u32;
644
645    let into_impl = generate_into_impl(&struct_ident);
646    let serialization = quote! {
647      #into_impl
648
649      pub fn serialize_to_writer(value: &#struct_ident, writer: &mut BufferWriter) {
650        let structure_id = #const_id_ident.as_slice();
651        writer.begin_structure(structure_id, #field_count);
652        #(#fields_serialization)*
653      }
654
655      /// Serializes the structure as a *raw* element — the field count and
656      /// fields only, without the leading `TYPE_STRUCTURE` tag or the 16-byte
657      /// structure id. This is the on-wire encoding of an element inside an
658      /// array of structures, where the id is carried once by the array header.
659      /// It mirrors `arora_buffers::serde_uuid`'s `Value::ArrayStructure` so the
660      /// generated codegen and the generic value codec agree across the
661      /// `arora_call` boundary.
662      pub fn serialize_to_writer_raw(value: &#struct_ident, writer: &mut BufferWriter) {
663        writer.begin_structure_raw(#field_count);
664        #(#fields_serialization)*
665      }
666    };
667
668    // Struct Deserialization.
669    // We convert each field we read into an optional,
670    // then we move all of them into the result structure.
671    let mut field_variable_declarations = Vec::new();
672    for (_, field) in &structure.fields {
673        let variable_ident = struct_field_intermediate_variable_ident(name, &field.name);
674        let mut type_ident =
675            type_ident_from_frozen(&field.ty, registry, PrefixWithMod::Yes).await?;
676        if is_self_ref(field) {
677            type_ident = quote! { Box<#type_ident> };
678        }
679        field_variable_declarations
680            .push(quote! { let mut #variable_ident: Option<#type_ident> = None; });
681    }
682
683    let mut deserialization_cases = Vec::new();
684    for (_, field) in &structure.fields {
685        let field_const_id_ident = struct_field_const_id_ident(name, &field.name);
686        let field_variable_ident = struct_field_intermediate_variable_ident(name, &field.name);
687        let deserialize =
688            generate_deserialize_from_frozen(&field.ty, registry, CheckType::Yes).await?;
689        let deserialize = if is_self_ref(field) {
690            quote! { Box::new(#deserialize) }
691        } else {
692            deserialize
693        };
694        deserialization_cases.push(quote! {
695          if field_raw_id == #field_const_id_ident {
696            #field_variable_ident = Some(#deserialize);
697          }
698        });
699    }
700
701    let struct_field_assignment = structure.fields.iter().map(|(_, field)| {
702        let field_ident = variable_ident(&field.name);
703        let variable_ident = struct_field_intermediate_variable_ident(name, &field.name);
704        quote! { #field_ident: #variable_ident.unwrap() }
705    });
706
707    let try_from_impl = generate_try_from_impl(&struct_ident);
708    let expected_field_count = structure.fields.len();
709    let deserialization = quote! {
710      #try_from_impl
711
712      pub fn deserialize_from_reader(reader: &mut BufferReader, check_type: bool) -> Result<#struct_ident, DeserializationError> {
713        let field_count = if check_type {
714          let type_raw_id_opt = reader.next_type();
715          if type_raw_id_opt.is_none() {
716            return Err(DeserializationError{ message: "missing next type information".to_string() })
717          }
718          if type_raw_id_opt.unwrap() != TYPE_STRUCTURE {
719            return Err(DeserializationError{ message: "next type is not a structure".to_string() })
720          }
721          let (structure_raw_id, field_count) = reader.get_structure();
722          if #const_id_ident != structure_raw_id {
723            return Err(DeserializationError{ message: "structure id does not match".to_string() })
724          }
725          field_count
726        } else {
727          reader.get_structure_raw()
728        };
729        if #expected_field_count != field_count as usize {
730          return Err(DeserializationError{
731            message: format!("expected {} fields, found {}", #expected_field_count, field_count)
732          })
733        }
734
735        #(#field_variable_declarations)*
736        for _ in 0..field_count {
737          let field_raw_id = reader.get_structure_field();
738          #(#deserialization_cases) else* else {
739            return Err(DeserializationError {
740              message: format!("unexpected struct field {}", Uuid::from_slice(field_raw_id).unwrap().to_string())
741            })
742          }
743        }
744
745        Ok(#struct_ident {
746          #(#struct_field_assignment,)*
747        })
748      }
749    };
750
751    // Conversion to/from the generic `Value` vocabulary (mirrors the enum path).
752    // Without this, generated structs only round-trip through the binary buffer
753    // format; with it they flow through the Arora data store as `Value::Structure`.
754    let mut to_value_fields = Vec::new();
755    for (_, field) in &structure.fields {
756        let field_ident = variable_ident(&field.name);
757        let field_const_id_ident = struct_field_const_id_ident(name, &field.name);
758        // Deref the `Box` on a self-referential field back to the owned value.
759        let field_access = if is_self_ref(field) {
760            quote! { *self.#field_ident }
761        } else {
762            quote! { self.#field_ident }
763        };
764        let field_value = generate_value_from_frozen(&field.ty, field_access, registry).await?;
765        to_value_fields.push(quote! {
766          StructureField {
767            id: Uuid::from_bytes(#field_const_id_ident),
768            value: Box::new(#field_value),
769          }
770        });
771    }
772    let to_value = quote! {
773      impl Into<Value> for #struct_ident {
774        fn into(self) -> Value {
775          Value::Structure(Structure {
776            id: Uuid::from_bytes(#const_id_ident),
777            fields: vec![#(#to_value_fields),*],
778          })
779        }
780      }
781    };
782
783    let mut from_value_field_vars = Vec::new();
784    let mut from_value_cases = Vec::new();
785    let mut from_value_assignments = Vec::new();
786    for (_, field) in &structure.fields {
787        let field_ident = variable_ident(&field.name);
788        let field_const_id_ident = struct_field_const_id_ident(name, &field.name);
789        let field_var = struct_field_intermediate_variable_ident(name, &field.name);
790        let mut field_type_ident =
791            type_ident_from_frozen(&field.ty, registry, PrefixWithMod::Yes).await?;
792        let extract = generate_field_from_value_frozen(
793            &field.ty,
794            quote! { *field.value },
795            &field.name,
796            registry,
797        )
798        .await?;
799        let extract = if is_self_ref(field) {
800            field_type_ident = quote! { Box<#field_type_ident> };
801            quote! { Box::new(#extract) }
802        } else {
803            extract
804        };
805        from_value_field_vars
806            .push(quote! { let mut #field_var: Option<#field_type_ident> = None; });
807        from_value_cases.push(quote! { #field_const_id_ident => { #field_var = Some(#extract); } });
808        let missing_message = format!("missing field {}", field.name);
809        from_value_assignments.push(quote! {
810          #field_ident: #field_var.ok_or_else(|| ConversionError { message: #missing_message.to_string() })?
811        });
812    }
813    let from_value = quote! {
814      impl TryFrom<Value> for #struct_ident {
815        type Error = ConversionError;
816        fn try_from(value: Value) -> Result<Self, Self::Error> {
817          if let Value::Structure(as_struct) = value {
818            if *as_struct.id.as_bytes() != #const_id_ident {
819              return Err(ConversionError { message: "unexpected structure type ID".to_string() });
820            }
821            #(#from_value_field_vars)*
822            for field in as_struct.fields {
823              match *field.id.as_bytes() {
824                #(#from_value_cases)*
825                _ => return Err(ConversionError { message: "unexpected struct field".to_string() }),
826              }
827            }
828            Ok(#struct_ident {
829              #(#from_value_assignments,)*
830            })
831          } else {
832            Err(ConversionError { message: "unexpected kind".to_string() })
833          }
834        }
835      }
836    };
837
838    // Structures with fields of other *declared* types refer to sibling
839    // generated modules as `arora_generated::<module>::…`, so `arora_generated`
840    // must be in scope here. Structures made only of primitives or dynamic
841    // `Value` fields don't (and an unused import would trip `-D warnings`).
842    let references_generated_modules = structure.fields.values().any(|field| match &field.ty {
843        FrozenTy::Primitive(_) => false,
844        FrozenTy::FrozenScalar(scalar) => !is_dynamic_value_id(&scalar.reference.id),
845        FrozenTy::FrozenArray(array) => !is_dynamic_value_id(&array.reference.id),
846    });
847    let generated_modules_use = if references_generated_modules {
848        quote! { use crate::arora_generated; }
849    } else {
850        quote! {}
851    };
852
853    // The `Value`-vocabulary conversions for array-of-structure /
854    // array-of-enumeration fields reference extra value types; import them only
855    // when a field needs them so structs without such fields don't carry unused
856    // imports (which would trip `-D warnings` in consumers).
857    let mut needs_structure_without_id = false;
858    let mut needs_enumeration_value_types = false;
859    for field in structure.fields.values() {
860        if let FrozenTy::FrozenArray(array) = &field.ty {
861            if is_dynamic_value_id(&array.reference.id) {
862                continue;
863            }
864            match registry
865                .type_of(&Selector::Id(array.reference.id.to_owned()))
866                .await
867                .map_err(GenerationError::RegistryError)?
868            {
869                RecordType::Structure => needs_structure_without_id = true,
870                RecordType::Enumeration => needs_enumeration_value_types = true,
871                _ => {}
872            }
873        }
874    }
875    let mut value_imports: Vec<TokenStream> = vec![
876        quote! { ConversionError },
877        quote! { Structure },
878        quote! { StructureField },
879        quote! { Value },
880    ];
881    if needs_structure_without_id {
882        value_imports.push(quote! { StructureWithoutId });
883    }
884    if needs_enumeration_value_types {
885        value_imports.push(quote! { Enumeration });
886        value_imports.push(quote! { EnumerationWithoutId });
887    }
888
889    let type_source = quote! {
890      use arora_buffers::*;
891      use uuid::Uuid;
892      use arora_types::value::{ #(#value_imports),* };
893      use crate::arora_generated::error::DeserializationError;
894      #generated_modules_use
895      #struct_declaration
896      #serialization
897      #deserialization
898      #to_value
899      #from_value
900      #id_declaration
901      #(#field_id_declarations)*
902    };
903
904    let base_file_name = structure.name.to_case(Case::Snake);
905    let file_path = if parent_path.is_empty() {
906        format!("{}.rs", base_file_name)
907    } else {
908        format!("{}/{}.rs", parent_path.replace('.', "/"), base_file_name)
909    };
910    token_stream_to_file(file_path, &type_source).map_err(GenerationError::VfsError)
911}
912
913/// Generates a virtual source file with wrappers for every symbol imported by the module.
914/// It contains human-readable public functions that can be used by the module implementation,
915/// under the path of the module, as `path::to::module::import`.
916async fn generate_imports_from_module_source(
917    module_id: &Uuid,
918    module_path: &str,
919    imports: &Vec<ImportAsset>,
920    registry: &mut dyn ReadableRegistry,
921) -> Result<Directory, GenerationError> {
922    // Using dependent types.
923    let uses = {
924        let mut dependencies = HashSet::<&FrozenReference>::new();
925        for import in imports {
926            import.import.dependencies(&mut dependencies);
927        }
928        let mut uses = Vec::new();
929        for dep in dependencies {
930            let dep_selector = Selector::Id(dep.id);
931            let type_def = match registry
932                .get_type(
933                    &dep_selector,
934                    &VersionReq::parse(dep.version.to_string().as_str()).unwrap(),
935                )
936                .await
937            {
938                Ok(TypeDefinitionFrozen::Primitive(_)) => continue,
939                Ok(type_definition) => type_definition,
940                Err(RegistryError::NotAType { selector: _ }) => continue,
941                Err(err) => return Err(GenerationError::RegistryError(err)),
942            };
943            let type_ident =
944                type_ident_from_definition(&type_def, &dep.id, registry, PrefixWithMod::Yes)
945                    .await?;
946            uses.push(type_ident);
947        }
948        uses
949    };
950
951    let splitted_module_path: Vec<&str> = module_path.split(".").collect();
952    let module_name = splitted_module_path.last().unwrap();
953
954    // Declare the ID of the module to use it locally.
955    let module_const_id_ident =
956        format_ident!("{}_MODULE_ID", module_name.to_case(Case::UpperSnake),);
957    let module_id_declaration = generate_const_id_declaration(
958        &module_name.to_string(),
959        &module_const_id_ident,
960        module_id,
961        Public::No,
962    );
963
964    // Declare each imported function.
965    let mut functions_declarations = Vec::<TokenStream>::new();
966    for import in imports {
967        let ExportKind::Function(function_symbol) = &import.import.kind;
968        let function_name = &import.import.name;
969        let function_ident = format_ident!("{}", function_name);
970        let mut parameters_declarations = Vec::new();
971        for param in function_symbol.parameters.values() {
972            let maybe_mut = if param.mutable {
973                quote! { mut }
974            } else {
975                quote! {}
976            };
977            let param_name_ident = format_ident!("{}", param.name);
978            let param_type_ident =
979                type_ident_from_frozen(&param.ty, registry, PrefixWithMod::Yes).await?;
980            parameters_declarations.push(quote! {
981              #maybe_mut #param_name_ident: #param_type_ident
982            });
983        }
984        let ret_type_ident =
985            type_ident_from_frozen(&function_symbol.return_ty, registry, PrefixWithMod::Yes)
986                .await?;
987
988        // And implement the call.
989        // First declare the const ids.
990        let function_const_id_ident = function_const_id_ident(function_name);
991        let function_id_declaration = generate_const_id_declaration(
992            function_name,
993            &function_const_id_ident,
994            &import.id,
995            Public::No,
996        );
997        let param_ids_declarations = {
998            let mut param_ids_declarations = Vec::new();
999            for (id, param) in &function_symbol.parameters {
1000                param_ids_declarations.push(generate_const_id_declaration(
1001                    &format!("{}.{}", function_name, param.name),
1002                    &function_param_const_id_ident(function_name, &param.name),
1003                    id,
1004                    Public::No,
1005                ));
1006            }
1007            param_ids_declarations
1008        };
1009
1010        let ids_declaration = quote! {
1011          #function_id_declaration
1012          #(#param_ids_declarations)*
1013        };
1014
1015        // Then prepare a call argument structure.
1016        // It consists in a struct with the function id as id,
1017        // and with one field for each param.
1018        let add_args = {
1019            let mut add_args = Vec::new();
1020            for param in function_symbol.parameters.values() {
1021                let function_param_const_id_ident =
1022                    function_param_const_id_ident(function_name, &param.name);
1023                let param_name_ident = format_ident!("{}", param.name);
1024                let serialize_arg = generate_serialize_from_frozen(
1025                    &param.ty,
1026                    param_name_ident.into_token_stream(),
1027                    registry,
1028                )
1029                .await?;
1030                add_args.push(quote! {
1031                  writer.add_structure_field(#function_param_const_id_ident.as_slice());
1032                  #serialize_arg;
1033                });
1034            }
1035            add_args
1036        };
1037        let nof_args = add_args.len() as u32;
1038        let prepare_call_structure = quote! {
1039          let mut writer = BufferWriter::new();
1040          let writer = &mut writer;
1041          writer.begin_structure(#function_const_id_ident.as_slice(), #nof_args);
1042          #(#add_args)*
1043          let arg = writer.finalize();
1044        };
1045
1046        // Then perform the call.
1047        let perform_call = quote! {
1048          let result_buffer_addr = unsafe {
1049            arora_dispatch(
1050              #module_const_id_ident.as_ptr() as usize,
1051              #function_const_id_ident.as_ptr() as usize,
1052              arg.as_ptr() as usize,
1053            )
1054          };
1055        };
1056
1057        // Then parse the result.
1058        let prepare_parsing = quote! {
1059          let result_buffer_ptr = result_buffer_addr as *const u8;
1060          let input_size_bytes: &[u8; 4] =
1061            unsafe { std::slice::from_raw_parts(result_buffer_ptr, BUFFER_SIZE_SIZE) }
1062              .try_into()
1063              .expect("input is too small");
1064          let input_size = u32::from_le_bytes(*input_size_bytes) as usize;
1065          let input =
1066            unsafe { std::slice::from_raw_parts(result_buffer_ptr, BUFFER_SIZE_SIZE + input_size) };
1067          let mut reader = BufferReader::new(&input);
1068          let reader = &mut reader;
1069        };
1070
1071        // It consists in a struct with the function id as id,
1072        let check_result_struct = quote! {
1073          let type_raw_id_opt = reader.next_type();
1074          assert!(!type_raw_id_opt.is_none());
1075          assert_eq!(type_raw_id_opt.unwrap(), TYPE_STRUCTURE);
1076          let (result_struct_id, result_field_count) = reader.get_structure();
1077          assert_eq!(result_struct_id, #function_const_id_ident);
1078        };
1079
1080        // with one field for the return value,
1081        // plus one field for each param.
1082        // Mutate the mutable parameters
1083        // and return.
1084        let deserialize_ret =
1085            generate_deserialize_from_frozen(&function_symbol.return_ty, registry, CheckType::Yes)
1086                .await?;
1087
1088        let process_params = if nof_args > 1 {
1089            let declare_mutable_params = {
1090                let mut declare_mutable_params = Vec::new();
1091                for param in function_symbol.parameters.values() {
1092                    if param.mutable {
1093                        let param_name_ident = format_ident!("{}", param.name);
1094                        declare_mutable_params.push(quote! {
1095                          let mut #param_name_ident = None;
1096                        })
1097                    }
1098                }
1099                declare_mutable_params
1100            };
1101
1102            let deserialize_params = {
1103                let mut deserialize_params = Vec::new();
1104                for param in function_symbol.parameters.values() {
1105                    if param.mutable {
1106                        let param_name_ident = format_ident!("{}", param.name);
1107                        let function_param_const_id_ident =
1108                            function_param_const_id_ident(function_name, &param.name);
1109                        let deserialize_param =
1110                            generate_deserialize_from_frozen(&param.ty, registry, CheckType::Yes)
1111                                .await?;
1112                        deserialize_params.push(quote! {
1113              x if *x == #function_param_const_id_ident => *#param_name_ident = #deserialize_param,
1114            });
1115                    }
1116                }
1117                deserialize_params
1118            };
1119
1120            quote! {
1121              #(#declare_mutable_params)*
1122              for _i in 1u32..#nof_args {
1123                let next_field_id = reader.get_structure_field();
1124                match next_field_id {
1125                  #(#deserialize_params)*
1126                  x => panic!("found unexpected mutated argument id: {:#?}", x),
1127                }
1128              }
1129            }
1130        } else {
1131            quote! {}
1132        };
1133
1134        let process_result = quote! {
1135          assert_eq!(result_field_count, #nof_args);
1136          let first_field_id = reader.get_structure_field();
1137          assert_eq!(first_field_id, #function_const_id_ident);
1138          let ret = #deserialize_ret;
1139          #process_params
1140          ret
1141        };
1142
1143        // This makes an import function.
1144        functions_declarations.push(quote! {
1145          pub fn #function_ident (#(#parameters_declarations),*) -> #ret_type_ident {
1146            #ids_declaration
1147            #prepare_call_structure
1148            #perform_call
1149            #prepare_parsing
1150            #check_result_struct
1151            #process_result
1152          }
1153        });
1154    }
1155
1156    // This makes a module import.
1157    let source = quote! {
1158      #(#uses)*
1159      use arora_buffers::*;
1160      use crate::arora_generated::arora::arora_dispatch;
1161      #module_id_declaration
1162      #(#functions_declarations)*
1163    };
1164
1165    let file_path = splitted_module_path
1166        .iter()
1167        .map(|name| name.to_case(Case::Snake))
1168        .fold(String::new(), |acc, name| {
1169            if acc.is_empty() {
1170                name
1171            } else {
1172                format!("{}/{}", acc, name)
1173            }
1174        })
1175        + ".rs";
1176    token_stream_to_file(file_path, &source).map_err(GenerationError::VfsError)
1177}
1178
1179/// Generates the interface of a module, i.e. the declarations of its exported functions.
1180async fn generate_module_source(
1181    module: &ModuleFrozen,
1182    registry: &mut dyn ReadableRegistry,
1183) -> Result<Directory, GenerationError> {
1184    // Function Uses.
1185    let exports = &module.exports;
1186    let use_functions = exports
1187        .values()
1188        .map(|export| format_ident!("{}", export.name));
1189
1190    // Function and param IDs.
1191    let function_ids = exports.iter().flat_map(|(function_id, export)| {
1192        let ExportKind::Function(function_symbol) = &export.kind;
1193        let mut id_declarations = Vec::with_capacity(function_symbol.parameters.len() + 1);
1194        id_declarations.push(generate_const_id_declaration(
1195            &export.name,
1196            &function_const_id_ident(&export.name),
1197            function_id,
1198            Public::Yes,
1199        ));
1200        for (param_id, param) in &function_symbol.parameters {
1201            id_declarations.push(generate_const_id_declaration(
1202                &format!("{}.{}", export.name, param.name),
1203                &function_param_const_id_ident(&export.name, &param.name),
1204                param_id,
1205                Public::Yes,
1206            ));
1207        }
1208        id_declarations
1209    });
1210
1211    // Functions declarations exported for Arora.
1212    let function_declarations = {
1213        let mut function_declarations = Vec::new();
1214        for (export_id, export) in exports {
1215            let function_ident = format_ident!("{}", export.name);
1216            let ExportKind::Function(function_symbol) = &export.kind;
1217            let const_id_ident = function_const_id_ident(&export.name);
1218
1219            let call_check = quote! {
1220              let mut reader = BufferReader::new(&input);
1221              let reader = &mut reader;
1222              let type_raw_id_opt = reader.next_type();
1223              if type_raw_id_opt.is_none() {
1224                return Err("input is empty".to_string());
1225              }
1226              if type_raw_id_opt.unwrap() != TYPE_STRUCTURE {
1227                return Err(format!("expected structure input, got type {:?}", type_raw_id_opt));
1228              }
1229              let (structure_raw_id, field_count) = reader.get_structure();
1230              if structure_raw_id != &#const_id_ident {
1231                return Err("function id mismatch in input".to_string());
1232              }
1233            };
1234
1235            let param_declarations = {
1236                let mut param_declarations = Vec::new();
1237                for (param_id, param) in &function_symbol.parameters {
1238                    let param_var_ident = param_ident(param_id, param);
1239                    let param_type_ident =
1240                        type_ident_from_frozen(&param.ty, registry, PrefixWithMod::Yes).await?;
1241                    param_declarations.push(
1242                        quote! { let mut #param_var_ident: Option<#param_type_ident> = None; },
1243                    );
1244                }
1245                param_declarations
1246            };
1247
1248            let deserialization_cases = {
1249                let mut deserialization_cases = Vec::new();
1250                for (param_id, param) in &function_symbol.parameters {
1251                    let param_const_id_ident =
1252                        function_param_const_id_ident(&export.name, &param.name);
1253                    let param_var_ident = param_ident(param_id, param);
1254                    let deserialize =
1255                        generate_deserialize_from_frozen(&param.ty, registry, CheckType::YesResult)
1256                            .await?;
1257                    deserialization_cases.push(quote! {
1258                      if field_raw_id == #param_const_id_ident {
1259                        #param_var_ident = Some(#deserialize);
1260                      }
1261                    });
1262                }
1263                deserialization_cases
1264            };
1265
1266            let deserialize_params = if function_symbol.parameters.is_empty() {
1267                quote! {
1268                  if field_count != 0 {
1269                    return Err(format!("expected 0 parameters but got {}", field_count));
1270                  }
1271                }
1272            } else {
1273                quote! {
1274                  #(#param_declarations)*
1275                  for _ in 0..field_count {
1276                    let field_raw_id = reader.get_structure_field();
1277                    #(#deserialization_cases else)* {
1278                      return Err(format!("unexpected parameter {:?}", field_raw_id));
1279                    }
1280                  }
1281                }
1282            };
1283
1284            let param_args = function_symbol.parameter_ordering.iter().map(|param_id| {
1285                let param = function_symbol.parameters.get(param_id).unwrap();
1286                let param_var_ident = param_ident(param_id, param);
1287                if param.mutable {
1288                    quote! { &mut #param_var_ident }
1289                } else {
1290                    quote! { #param_var_ident }
1291                }
1292            });
1293
1294            let call_and_write_result = {
1295                let result_ident = match &function_symbol.return_ty {
1296                    FrozenTy::Primitive(Primitive { kind }) if *kind == PrimitiveKind::Unit => {
1297                        quote! { _ }
1298                    }
1299                    _ => quote! { result },
1300                };
1301                let serialize_result = generate_serialize_from_frozen(
1302                    &function_symbol.return_ty,
1303                    result_ident.clone(),
1304                    registry,
1305                )
1306                .await?;
1307                quote! {
1308                  let #result_ident = #function_ident (#(#param_args),*);
1309                  #serialize_result;
1310                }
1311            };
1312
1313            let write_mutated_params: Vec<TokenStream> = {
1314                let mut write_mutated_params = Vec::new();
1315                for (param_id, param) in &function_symbol.parameters {
1316                    if param.mutable {
1317                        let param_var_ident = param_ident(param_id, param);
1318                        let param_const_id_ident =
1319                            function_param_const_id_ident(&export.name, &param.name);
1320                        let serialize_param = generate_serialize_from_frozen(
1321                            &param.ty,
1322                            quote! {#param_var_ident.unwrap()},
1323                            registry,
1324                        )
1325                        .await?;
1326                        write_mutated_params.push(quote! {
1327                          writer.add_structure_field(&#param_const_id_ident);
1328                          #serialize_param;
1329                        });
1330                    }
1331                }
1332                write_mutated_params
1333            };
1334            let nof_mutated_params = write_mutated_params.len();
1335
1336            let uuid_suffix = export_id.to_string().replace("-", "_");
1337            let arora_function_ident = format_ident!("arora_function_{}", uuid_suffix);
1338            let doc = export.name.to_string();
1339            function_declarations.push(quote! {
1340        #[doc = #doc]
1341        #[no_mangle]
1342        pub extern "C" fn #arora_function_ident (input_addr: usize) -> usize {
1343          let input_ptr = input_addr as *const u8;
1344          const INPUT_SIZE_SIZE: usize = std::mem::size_of::<u32>();
1345          let input_size_bytes: &[u8; 4] = unsafe {
1346            std::slice::from_raw_parts(input_ptr, INPUT_SIZE_SIZE)
1347          }.try_into().expect("input is too small");
1348          let input_size = u32::from_le_bytes(*input_size_bytes) as usize;
1349          let input = unsafe {
1350            std::slice::from_raw_parts(input_ptr, INPUT_SIZE_SIZE + input_size)
1351          };
1352          let _result: ::std::result::Result<::std::boxed::Box<[u8]>, ::std::string::String> = (|| {
1353            #call_check
1354            #deserialize_params
1355            let mut writer = BufferWriter::new();
1356            let writer = &mut writer;
1357            writer.begin_structure(&#const_id_ident, (#nof_mutated_params + 1) as u32);
1358            writer.add_structure_field(&#const_id_ident);
1359            #call_and_write_result
1360            #(#write_mutated_params)*
1361            ::std::result::Result::Ok(writer.finalize())
1362          })();
1363          match _result {
1364            ::std::result::Result::Ok(buf) => ::std::boxed::Box::leak(buf).as_ptr() as usize,
1365            ::std::result::Result::Err(msg) => {
1366              let mut writer = BufferWriter::new();
1367              writer.add_error(&msg);
1368              ::std::boxed::Box::leak(writer.finalize()).as_ptr() as usize
1369            }
1370          }
1371        }
1372      });
1373        }
1374        function_declarations
1375    };
1376
1377    // Putting it all together.
1378    let source = quote! {
1379      use arora_buffers::*;
1380      use crate::{arora_generated, #(#use_functions),*};
1381      #(#function_declarations)*
1382      #(#function_ids)*
1383    };
1384    token_stream_to_file("export.rs", &source).map_err(GenerationError::VfsError)
1385}
1386
1387pub fn generate_into_impl(type_ident: &Ident) -> TokenStream {
1388    quote! {
1389      impl Into<Box<[u8]>> for #type_ident {
1390        fn into(self) -> Box<[u8]> {
1391          let mut writer = BufferWriter::new();
1392          serialize_to_writer(&self, &mut writer);
1393          writer.finalize()
1394        }
1395      }
1396    }
1397}
1398
1399pub fn generate_try_from_impl(type_ident: &Ident) -> TokenStream {
1400    quote! {
1401      impl TryFrom<&[u8]> for #type_ident {
1402        type Error = DeserializationError;
1403
1404        fn try_from(buffer: &[u8]) -> Result<Self, Self::Error> {
1405          let mut reader = BufferReader::new(buffer);
1406          return deserialize_from_reader(&mut reader, true)
1407        }
1408      }
1409    }
1410}
1411
1412/// Generate an expression building a generic `Value` from a Rust field value.
1413/// Counterpart of [`generate_serialize_from_frozen`] for the in-memory `Value`
1414/// vocabulary instead of the binary buffer format.
1415async fn generate_value_from_frozen(
1416    ty: &FrozenTy,
1417    value_expression: TokenStream,
1418    registry: &mut dyn ReadableRegistry,
1419) -> Result<TokenStream, GenerationError> {
1420    Ok(match ty {
1421        FrozenTy::Primitive(primitive) => match primitive.kind {
1422            PrimitiveKind::Unit => quote! { Value::Unit },
1423            PrimitiveKind::Boolean => quote! { Value::Boolean(#value_expression) },
1424            PrimitiveKind::U8 => quote! { Value::U8(#value_expression) },
1425            PrimitiveKind::U16 => quote! { Value::U16(#value_expression) },
1426            PrimitiveKind::U32 => quote! { Value::U32(#value_expression) },
1427            PrimitiveKind::U64 => quote! { Value::U64(#value_expression) },
1428            PrimitiveKind::I8 => quote! { Value::I8(#value_expression) },
1429            PrimitiveKind::I16 => quote! { Value::I16(#value_expression) },
1430            PrimitiveKind::I32 => quote! { Value::I32(#value_expression) },
1431            PrimitiveKind::I64 => quote! { Value::I64(#value_expression) },
1432            PrimitiveKind::F32 => quote! { Value::F32(#value_expression) },
1433            PrimitiveKind::F64 => quote! { Value::F64(#value_expression) },
1434            PrimitiveKind::String => quote! { Value::String(#value_expression) },
1435            PrimitiveKind::ArrayBoolean => quote! { Value::ArrayBoolean(#value_expression) },
1436            PrimitiveKind::ArrayU8 => quote! { Value::ArrayU8(#value_expression) },
1437            PrimitiveKind::ArrayU16 => quote! { Value::ArrayU16(#value_expression) },
1438            PrimitiveKind::ArrayU32 => quote! { Value::ArrayU32(#value_expression) },
1439            PrimitiveKind::ArrayU64 => quote! { Value::ArrayU64(#value_expression) },
1440            PrimitiveKind::ArrayI8 => quote! { Value::ArrayI8(#value_expression) },
1441            PrimitiveKind::ArrayI16 => quote! { Value::ArrayI16(#value_expression) },
1442            PrimitiveKind::ArrayI32 => quote! { Value::ArrayI32(#value_expression) },
1443            PrimitiveKind::ArrayI64 => quote! { Value::ArrayI64(#value_expression) },
1444            PrimitiveKind::ArrayF32 => quote! { Value::ArrayF32(#value_expression) },
1445            PrimitiveKind::ArrayF64 => quote! { Value::ArrayF64(#value_expression) },
1446            PrimitiveKind::ArrayString => quote! { Value::ArrayString(#value_expression) },
1447        },
1448        // A dynamic-value field already holds a `Value`; pass it through.
1449        FrozenTy::FrozenScalar(scalar) if is_dynamic_value_id(&scalar.reference.id) => {
1450            quote! { #value_expression }
1451        }
1452        // Nested struct/enum types implement `Into<Value>`.
1453        FrozenTy::FrozenScalar(_) => quote! { Into::<Value>::into(#value_expression) },
1454        // A dynamic array is already a `Vec<Value>`.
1455        FrozenTy::FrozenArray(array) if is_dynamic_value_id(&array.reference.id) => {
1456            quote! { Value::ArrayValue(#value_expression.into_iter().collect()) }
1457        }
1458        // Arrays of structures/enumerations become the corresponding typed
1459        // `Value::ArrayStructure` / `Value::ArrayEnumeration` (the id carried
1460        // once), matching `serde_uuid` — so `T -> Value -> serde_uuid` produces
1461        // the same bytes as the direct buffer path.
1462        FrozenTy::FrozenArray(array) => {
1463            let raw_id = RawUuidValue(&array.reference.id);
1464            match registry
1465                .type_of(&Selector::Id(array.reference.id.to_owned()))
1466                .await
1467                .map_err(GenerationError::RegistryError)?
1468            {
1469                RecordType::Structure => quote! {
1470                    Value::ArrayStructure {
1471                        id: Uuid::from_bytes(#raw_id),
1472                        elements: #value_expression
1473                            .into_iter()
1474                            .map(|__element| match Into::<Value>::into(__element) {
1475                                Value::Structure(__s) => StructureWithoutId { fields: __s.fields },
1476                                _ => unreachable!("generated structure did not convert to Value::Structure"),
1477                            })
1478                            .collect(),
1479                    }
1480                },
1481                RecordType::Enumeration => quote! {
1482                    Value::ArrayEnumeration {
1483                        id: Uuid::from_bytes(#raw_id),
1484                        elements: #value_expression
1485                            .into_iter()
1486                            .map(|__element| match Into::<Value>::into(__element) {
1487                                Value::Enumeration(__e) => EnumerationWithoutId {
1488                                    variant_id: __e.variant_id,
1489                                    value: __e.value,
1490                                },
1491                                _ => unreachable!("generated enumeration did not convert to Value::Enumeration"),
1492                            })
1493                            .collect(),
1494                    }
1495                },
1496                _ => unreachable!("unexpected array element record type"),
1497            }
1498        }
1499    })
1500}
1501
1502/// Generate an expression extracting a Rust field value out of a generic `Value`.
1503/// Counterpart of [`generate_deserialize_from_frozen`]. The produced expression
1504/// may `return Err(ConversionError { .. })` from the enclosing `try_from`.
1505#[async_recursion(?Send)]
1506async fn generate_field_from_value_frozen(
1507    ty: &FrozenTy,
1508    value_expression: TokenStream,
1509    field_name: &str,
1510    registry: &mut dyn ReadableRegistry,
1511) -> Result<TokenStream, GenerationError> {
1512    match ty {
1513        FrozenTy::Primitive(primitive) => {
1514            let mismatch = format!("field {}: unexpected value kind", field_name);
1515            let arm = match primitive.kind {
1516                PrimitiveKind::Unit => quote! { Value::Unit => () },
1517                PrimitiveKind::Boolean => quote! { Value::Boolean(__v) => __v },
1518                PrimitiveKind::U8 => quote! { Value::U8(__v) => __v },
1519                PrimitiveKind::U16 => quote! { Value::U16(__v) => __v },
1520                PrimitiveKind::U32 => quote! { Value::U32(__v) => __v },
1521                PrimitiveKind::U64 => quote! { Value::U64(__v) => __v },
1522                PrimitiveKind::I8 => quote! { Value::I8(__v) => __v },
1523                PrimitiveKind::I16 => quote! { Value::I16(__v) => __v },
1524                PrimitiveKind::I32 => quote! { Value::I32(__v) => __v },
1525                PrimitiveKind::I64 => quote! { Value::I64(__v) => __v },
1526                PrimitiveKind::F32 => quote! { Value::F32(__v) => __v },
1527                PrimitiveKind::F64 => quote! { Value::F64(__v) => __v },
1528                PrimitiveKind::String => quote! { Value::String(__v) => __v },
1529                PrimitiveKind::ArrayBoolean => quote! { Value::ArrayBoolean(__v) => __v },
1530                PrimitiveKind::ArrayU8 => quote! { Value::ArrayU8(__v) => __v },
1531                PrimitiveKind::ArrayU16 => quote! { Value::ArrayU16(__v) => __v },
1532                PrimitiveKind::ArrayU32 => quote! { Value::ArrayU32(__v) => __v },
1533                PrimitiveKind::ArrayU64 => quote! { Value::ArrayU64(__v) => __v },
1534                PrimitiveKind::ArrayI8 => quote! { Value::ArrayI8(__v) => __v },
1535                PrimitiveKind::ArrayI16 => quote! { Value::ArrayI16(__v) => __v },
1536                PrimitiveKind::ArrayI32 => quote! { Value::ArrayI32(__v) => __v },
1537                PrimitiveKind::ArrayI64 => quote! { Value::ArrayI64(__v) => __v },
1538                PrimitiveKind::ArrayF32 => quote! { Value::ArrayF32(__v) => __v },
1539                PrimitiveKind::ArrayF64 => quote! { Value::ArrayF64(__v) => __v },
1540                PrimitiveKind::ArrayString => quote! { Value::ArrayString(__v) => __v },
1541            };
1542            Ok(quote! {
1543                match #value_expression {
1544                    #arm,
1545                    _ => return Err(ConversionError { message: #mismatch.to_string() }),
1546                }
1547            })
1548        }
1549        FrozenTy::FrozenScalar(scalar) if is_dynamic_value_id(&scalar.reference.id) => {
1550            // The field already is a `Value`; take it verbatim.
1551            Ok(quote! { #value_expression })
1552        }
1553        FrozenTy::FrozenScalar(_) => {
1554            let type_ident = type_ident_from_frozen(ty, registry, PrefixWithMod::Yes).await?;
1555            Ok(quote! { <#type_ident as TryFrom<Value>>::try_from(#value_expression)? })
1556        }
1557        // A dynamic array is a `Vec<Value>` taken verbatim.
1558        FrozenTy::FrozenArray(array) if is_dynamic_value_id(&array.reference.id) => {
1559            let mismatch = format!("field {}: expected array value", field_name);
1560            Ok(quote! {
1561                match #value_expression {
1562                    Value::ArrayValue(__items) => __items,
1563                    _ => return Err(ConversionError { message: #mismatch.to_string() }),
1564                }
1565            })
1566        }
1567        // Arrays of structures/enumerations are extracted from the typed
1568        // `Value::ArrayStructure` / `Value::ArrayEnumeration`; each element is
1569        // rebuilt into a full `Value::Structure` / `Value::Enumeration` (the id
1570        // reattached from the array header) and converted through the element's
1571        // own `TryFrom<Value>`.
1572        FrozenTy::FrozenArray(array) => {
1573            let type_ident =
1574                type_ident_from_id(&array.reference.id, registry, PrefixWithMod::Yes).await?;
1575            match registry
1576                .type_of(&Selector::Id(array.reference.id.to_owned()))
1577                .await
1578                .map_err(GenerationError::RegistryError)?
1579            {
1580                RecordType::Structure => {
1581                    let mismatch = format!("field {}: expected array of structures", field_name);
1582                    Ok(quote! {
1583                        match #value_expression {
1584                            Value::ArrayStructure { id: __id, elements: __items } => {
1585                                let mut __out = Vec::with_capacity(__items.len());
1586                                for __swi in __items {
1587                                    __out.push(<#type_ident as TryFrom<Value>>::try_from(
1588                                        Value::Structure(Structure { id: __id, fields: __swi.fields })
1589                                    )?);
1590                                }
1591                                __out
1592                            }
1593                            _ => return Err(ConversionError { message: #mismatch.to_string() }),
1594                        }
1595                    })
1596                }
1597                RecordType::Enumeration => {
1598                    let mismatch = format!("field {}: expected array of enumerations", field_name);
1599                    Ok(quote! {
1600                        match #value_expression {
1601                            Value::ArrayEnumeration { id: __id, elements: __items } => {
1602                                let mut __out = Vec::with_capacity(__items.len());
1603                                for __ewi in __items {
1604                                    __out.push(<#type_ident as TryFrom<Value>>::try_from(
1605                                        Value::Enumeration(Enumeration {
1606                                            id: __id,
1607                                            variant_id: __ewi.variant_id,
1608                                            value: __ewi.value,
1609                                        })
1610                                    )?);
1611                                }
1612                                __out
1613                            }
1614                            _ => return Err(ConversionError { message: #mismatch.to_string() }),
1615                        }
1616                    })
1617                }
1618                _ => unreachable!("unexpected array element record type"),
1619            }
1620        }
1621    }
1622}
1623
1624async fn generate_serialize_from_frozen(
1625    ty: &FrozenTy,
1626    value_expression: TokenStream,
1627    registry: &mut dyn ReadableRegistry,
1628) -> Result<TokenStream, GenerationError> {
1629    match ty {
1630        FrozenTy::Primitive(primitive) => {
1631            let generate_serialize_primitive_array =
1632                |primitive_type_id: &Uuid, write_function: TokenStream| {
1633                    let id_bytes = RawUuidValue(primitive_type_id);
1634                    quote! {
1635                      writer.add_array_primitive(#id_bytes, #value_expression.len() as u32);
1636                      #write_function (#value_expression);
1637                    }
1638                };
1639            Ok(match primitive.kind {
1640                PrimitiveKind::Unit => quote! { writer.add_unit() },
1641                PrimitiveKind::Boolean => quote! { writer.add_boolean(#value_expression) },
1642                PrimitiveKind::U8 => quote! { writer.add_u8(#value_expression) },
1643                PrimitiveKind::U16 => quote! { writer.add_u16(#value_expression) },
1644                PrimitiveKind::U32 => quote! { writer.add_u32(#value_expression) },
1645                PrimitiveKind::U64 => quote! { writer.add_u64(#value_expression) },
1646                PrimitiveKind::I8 => quote! { writer.add_i8(#value_expression) },
1647                PrimitiveKind::I16 => quote! { writer.add_i16(#value_expression) },
1648                PrimitiveKind::I32 => quote! { writer.add_i32(#value_expression) },
1649                PrimitiveKind::I64 => quote! { writer.add_i64(#value_expression) },
1650                PrimitiveKind::F32 => quote! { writer.add_f32(#value_expression) },
1651                PrimitiveKind::F64 => quote! { writer.add_f64(#value_expression) },
1652                PrimitiveKind::String => quote! { writer.add_string(#value_expression.as_str()) },
1653                PrimitiveKind::ArrayBoolean => generate_serialize_primitive_array(
1654                    &BOOLEAN_ID,
1655                    quote! { writer.add_boolean_bulk },
1656                ),
1657                PrimitiveKind::ArrayU8 => {
1658                    generate_serialize_primitive_array(&U8_ID, quote! { writer.add_u8_bulk })
1659                }
1660                PrimitiveKind::ArrayU16 => {
1661                    generate_serialize_primitive_array(&U16_ID, quote! { writer.add_u16_bulk })
1662                }
1663                PrimitiveKind::ArrayU32 => {
1664                    generate_serialize_primitive_array(&U32_ID, quote! { writer.add_u32_bulk })
1665                }
1666                PrimitiveKind::ArrayU64 => {
1667                    generate_serialize_primitive_array(&U64_ID, quote! { writer.add_u64_bulk })
1668                }
1669                PrimitiveKind::ArrayI8 => {
1670                    generate_serialize_primitive_array(&I8_ID, quote! { writer.add_i8_bulk })
1671                }
1672                PrimitiveKind::ArrayI16 => {
1673                    generate_serialize_primitive_array(&I16_ID, quote! { writer.add_i16_bulk })
1674                }
1675                PrimitiveKind::ArrayI32 => {
1676                    generate_serialize_primitive_array(&I32_ID, quote! { writer.add_i32_bulk })
1677                }
1678                PrimitiveKind::ArrayI64 => {
1679                    generate_serialize_primitive_array(&I64_ID, quote! { writer.add_i64_bulk })
1680                }
1681                PrimitiveKind::ArrayF32 => {
1682                    generate_serialize_primitive_array(&F32_ID, quote! { writer.add_f32_bulk })
1683                }
1684                PrimitiveKind::ArrayF64 => {
1685                    generate_serialize_primitive_array(&F64_ID, quote! { writer.add_f64_bulk })
1686                }
1687                PrimitiveKind::ArrayString => {
1688                    let id_bytes = RawUuidValue(&STRING_ID);
1689                    quote! {
1690                      writer.add_array_primitive(#id_bytes, #value_expression.len() as u32);
1691                      for s in #value_expression {
1692                        writer.add_string(s.as_str());
1693                      }
1694                    }
1695                }
1696            })
1697        }
1698        FrozenTy::FrozenScalar(scalar) => {
1699            // Dynamic-value fields carry a generic `Value`; delegate to the
1700            // runtime's self-describing value serializer.
1701            if is_dynamic_value_id(&scalar.reference.id) {
1702                return Ok(quote! {
1703                  arora_buffers::serde_uuid::serialize_to_writer(&#value_expression, writer)
1704                });
1705            }
1706            let mod_prefix = generated_mod_ident_from_id(&scalar.reference.id, registry)
1707                .await
1708                .map_err(GenerationError::RegistryError)?;
1709            // `writer` is always a `&mut BufferWriter` binding, so reborrow it
1710            // (`&mut writer` would be a `&mut &mut BufferWriter`).
1711            Ok(quote! { #mod_prefix serialize_to_writer(&#value_expression, writer) })
1712        }
1713        FrozenTy::FrozenArray(array) => {
1714            let type_def = registry
1715                .get_type(
1716                    &Selector::Id(array.reference.id),
1717                    &VersionReq::parse(array.reference.version.0.to_string().as_str()).unwrap(),
1718                )
1719                .await
1720                .map_err(GenerationError::RegistryError)?;
1721            let id_bytes = RawUuidValue(&array.reference.id);
1722            // `add_array_{structure,enumeration}` take the type id as `&[u8]`;
1723            // `#id_bytes` expands to a `[u8; 16]` literal, so borrow it.
1724            let add_array_args = quote! { &#id_bytes, #value_expression.len() as u32 };
1725            let prepare_array = match type_def {
1726                TypeDefinitionFrozen::Primitive(_) => {
1727                    unreachable!("got an array of primitive type instead of a primitive array type")
1728                }
1729                TypeDefinitionFrozen::Enumeration(_) => {
1730                    quote! { writer.add_array_enumeration(#add_array_args); }
1731                }
1732                TypeDefinitionFrozen::Structure(_) => {
1733                    quote! { writer.add_array_structure(#add_array_args); }
1734                }
1735            };
1736            let mod_prefix = generated_mod_ident_from_id(&array.reference.id, registry)
1737                .await
1738                .map_err(GenerationError::RegistryError)?;
1739            // Serialize each element (borrowing the vector so we don't move the
1740            // field out of `&value`), not the whole vector. Elements take the
1741            // value by reference, and `element` is already a reference because we
1742            // iterate over `&value.field`.
1743            //
1744            // Elements (structures and enumerations alike) are written in the
1745            // *raw* encoding (`serialize_to_writer_raw`: no per-element type tag
1746            // or id — the id is carried once by the array header) to match
1747            // `serde_uuid`'s `Value::ArrayStructure` / `Value::ArrayEnumeration`,
1748            // so the codegen and the generic value codec agree across the
1749            // `arora_call` boundary.
1750            Ok(quote! {
1751              #prepare_array
1752              for element in &#value_expression {
1753                #mod_prefix serialize_to_writer_raw(element, writer);
1754              }
1755            })
1756        }
1757    }
1758}
1759
1760#[async_recursion(?Send)]
1761async fn generate_deserialize_from_frozen(
1762    ty: &FrozenTy,
1763    registry: &mut dyn ReadableRegistry,
1764    check_type: CheckType,
1765) -> Result<TokenStream, GenerationError> {
1766    match ty {
1767        FrozenTy::Primitive(primitive) => {
1768            let type_kind_ident = type_kind_ident_from_primitive(&primitive.kind);
1769
1770            let generate_deserialize = |deserialize: TokenStream| {
1771                let type_check = match check_type {
1772                    CheckType::Yes => quote! {
1773                      {
1774                        let _next_type = reader.next_type();
1775                        assert_eq!(_next_type, Some(#type_kind_ident), "type mismatch");
1776                      }
1777                    },
1778                    CheckType::YesResult => quote! {
1779                      {
1780                        let _next_type = reader.next_type();
1781                        if _next_type != Some(#type_kind_ident) {
1782                          return Err(format!("type mismatch: expected {:?} but got {:?}", #type_kind_ident, _next_type));
1783                        }
1784                      }
1785                    },
1786                    CheckType::No => quote! {},
1787                };
1788                quote! {{
1789                    #type_check
1790                    #deserialize
1791                  }
1792                }
1793            };
1794
1795            let generate_deserialize_base_type = |type_ident: TokenStream| {
1796                let getter = format_ident!("get_{}", type_ident.to_string());
1797                generate_deserialize(quote! { reader.#getter() })
1798            };
1799
1800            let generate_deserialize_array = |deserialize_array: TokenStream| {
1801                let array_type_check = match check_type {
1802                    CheckType::Yes => quote! {
1803                      {
1804                        let _at = reader.next_type();
1805                        assert_eq!(_at, Some(TYPE_ARRAY));
1806                      }
1807                      let (ty, count) = reader.get_array();
1808                      assert_eq!(ty, #type_kind_ident);
1809                    },
1810                    _ => quote! {
1811                      {
1812                        let _at = reader.next_type();
1813                        if _at != Some(TYPE_ARRAY) {
1814                          return Err(format!("expected array, got {:?}", _at));
1815                        }
1816                      }
1817                      let (ty, count) = reader.get_array();
1818                      if ty != #type_kind_ident {
1819                        return Err(format!("expected array element type {:?}, got {:?}", #type_kind_ident, ty));
1820                      }
1821                    },
1822                };
1823                quote! {{
1824                    #array_type_check
1825                    #deserialize_array
1826                  }
1827                }
1828            };
1829            Ok(match primitive.kind {
1830                PrimitiveKind::Unit => {
1831                    quote! { Result::<(), DeserializationError>::Ok(reader.get_unit()) }
1832                }
1833                PrimitiveKind::Boolean => generate_deserialize(quote! { reader.get_boolean() }),
1834                PrimitiveKind::U8 => generate_deserialize_base_type(quote! {u8}),
1835                PrimitiveKind::U16 => generate_deserialize_base_type(quote! {u16}),
1836                PrimitiveKind::U32 => generate_deserialize_base_type(quote! {u32}),
1837                PrimitiveKind::U64 => generate_deserialize_base_type(quote! {u64}),
1838                PrimitiveKind::I8 => generate_deserialize_base_type(quote! {i8}),
1839                PrimitiveKind::I16 => generate_deserialize_base_type(quote! {i16}),
1840                PrimitiveKind::I32 => generate_deserialize_base_type(quote! {i32}),
1841                PrimitiveKind::I64 => generate_deserialize_base_type(quote! {i64}),
1842                PrimitiveKind::F32 => generate_deserialize_base_type(quote! {f32}),
1843                PrimitiveKind::F64 => generate_deserialize_base_type(quote! {f64}),
1844                PrimitiveKind::String => generate_deserialize(quote! {
1845                  reader.get_string().to_string()
1846                }),
1847                PrimitiveKind::ArrayBoolean => generate_deserialize_array(quote! {
1848                  reader.get_boolean_bulk(count)
1849                }),
1850                PrimitiveKind::ArrayU8 => generate_deserialize_array(quote! {
1851                  reader.get_u8_bulk(count)
1852                }),
1853                PrimitiveKind::ArrayU16 => generate_deserialize_array(quote! {
1854                  reader.get_u16_bulk(count)
1855                }),
1856                PrimitiveKind::ArrayU32 => generate_deserialize_array(quote! {
1857                  reader.get_u32_bulk(count)
1858                }),
1859                PrimitiveKind::ArrayU64 => generate_deserialize_array(quote! {
1860                  reader.get_u64_bulk(count)
1861                }),
1862                PrimitiveKind::ArrayI8 => generate_deserialize_array(quote! {
1863                  reader.get_i8_bulk(count)
1864                }),
1865                PrimitiveKind::ArrayI16 => generate_deserialize_array(quote! {
1866                  reader.get_i16_bulk(count)
1867                }),
1868                PrimitiveKind::ArrayI32 => generate_deserialize_array(quote! {
1869                  reader.get_i32_bulk(count)
1870                }),
1871                PrimitiveKind::ArrayI64 => generate_deserialize_array(quote! {
1872                  reader.get_i64_bulk(count)
1873                }),
1874                PrimitiveKind::ArrayF32 => generate_deserialize_array(quote! {
1875                  reader.get_f32_bulk(count)
1876                }),
1877                PrimitiveKind::ArrayF64 => generate_deserialize_array(quote! {
1878                  reader.get_f64_bulk(count)
1879                }),
1880                PrimitiveKind::ArrayString => {
1881                    let deserialize_element = generate_deserialize(quote! {
1882                      Result::<String, DeserializationError>::Ok(reader.get_string().to_string())
1883                    });
1884                    generate_deserialize_array(quote! {
1885                      let mut res = Vec::<String>::with_capacity(count as usize);
1886                      for _i in 0..count {
1887                        res.push(#deserialize_element);
1888                      }
1889                      res
1890                    })
1891                }
1892            })
1893        }
1894        FrozenTy::FrozenScalar(scalar) => {
1895            // Dynamic-value fields read back a self-describing generic `Value`.
1896            if is_dynamic_value_id(&scalar.reference.id) {
1897                return Ok(quote! {
1898                  arora_buffers::serde_uuid::deserialize_from_reader(reader)
1899                });
1900            }
1901            let mod_prefix = generated_mod_ident_from_id(&scalar.reference.id, registry)
1902                .await
1903                .map_err(GenerationError::RegistryError)?;
1904            let check_type_bool = check_type != CheckType::No;
1905            let type_ident =
1906                type_ident_from_id(&scalar.reference.id, registry, PrefixWithMod::Yes).await?;
1907            let type_str = type_ident.to_string();
1908            // `reader` is always a `&mut BufferReader` binding (the generated
1909            // `deserialize_from_reader` takes it by mutable reference, and the
1910            // export/import handlers rebind it as one). Pass it through by
1911            // reborrow — `&mut reader` would be a `&mut &mut BufferReader`.
1912            Ok(match check_type {
1913                CheckType::YesResult => quote! {
1914                  #mod_prefix deserialize_from_reader(reader, #check_type_bool)
1915                    .map_err(|e| format!("failed to deserialize {}: {}", #type_str, e))?
1916                },
1917                _ => quote! {
1918                  #mod_prefix deserialize_from_reader(reader, #check_type_bool)
1919                    .expect(&format!("failed to deserialize {}", #type_str))
1920                },
1921            })
1922        }
1923        FrozenTy::FrozenArray(array) => {
1924            let type_ident =
1925                type_ident_from_id(&array.reference.id, registry, PrefixWithMod::Yes).await?;
1926            let element_record_type = registry
1927                .type_of(&Selector::Id(array.reference.id.to_owned()))
1928                .await
1929                .map_err(GenerationError::RegistryError)?;
1930            // Elements (structures and enumerations) are stored raw — no
1931            // per-element tag/id; the id is carried once by the array header,
1932            // matching `serde_uuid`'s `Value::ArrayStructure` /
1933            // `Value::ArrayEnumeration`. `CheckType::No` invokes the raw read
1934            // path (`get_structure_raw` for structures; the variant id + payload
1935            // without the enumeration id for enumerations).
1936            let element_check_type = CheckType::No;
1937            let deserialize_element = generate_deserialize_from_frozen(
1938                &FrozenTy::FrozenScalar(FrozenScalar {
1939                    reference: array.reference.to_owned(),
1940                }),
1941                registry,
1942                element_check_type,
1943            )
1944            .await?;
1945            let type_enum = match element_record_type {
1946                RecordType::Enumeration => quote! { TYPE_ENUMERATION },
1947                RecordType::Structure => quote! { TYPE_STRUCTURE },
1948                _ => unreachable!("unexpected type of element in array"),
1949            };
1950            let raw_id = RawUuidValue(&array.reference.id);
1951            let array_checks = match check_type {
1952                CheckType::Yes => quote! {
1953                  {
1954                    let _at = reader.next_type();
1955                    assert_eq!(_at, Some(TYPE_ARRAY));
1956                  }
1957                  let (ty, count) = reader.get_array();
1958                  assert_eq!(ty, #type_enum);
1959                  {
1960                    let _id = reader.get_structure_field();
1961                    assert_eq!(_id, &#raw_id);
1962                  }
1963                },
1964                _ => quote! {
1965                  {
1966                    let _at = reader.next_type();
1967                    if _at != Some(TYPE_ARRAY) {
1968                      return Err(format!("expected array, got {:?}", _at));
1969                    }
1970                  }
1971                  let (ty, count) = reader.get_array();
1972                  if ty != #type_enum {
1973                    return Err(format!("expected array element type {:?}, got {:?}", #type_enum, ty));
1974                  }
1975                  {
1976                    let _id = reader.get_structure_field();
1977                    if _id != &#raw_id {
1978                      return Err("array type id mismatch".to_string());
1979                    }
1980                  }
1981                },
1982            };
1983            Ok(quote! {{
1984              #array_checks
1985              let mut res = Vec::<#type_ident>::with_capacity(count as usize);
1986              for _i in 0..count {
1987                res.push(#deserialize_element);
1988              }
1989              res
1990            }})
1991        }
1992    }
1993}
1994
1995pub fn token_stream_to_file<P: AsRef<path::Path>>(
1996    file_path: P,
1997    tokens: &TokenStream,
1998) -> Result<Directory, VfsError> {
1999    let file_path = file_path.as_ref();
2000    let file_name = file_path.file_name().unwrap().to_str().unwrap();
2001    let parent_path = file_path.parent().unwrap();
2002    let mut output = Directory::new();
2003    let parent_dir = match output.ensure_directories(parent_path) {
2004        Ok(dir) => dir,
2005        Err(VfsError::EmptyPath) => &mut output,
2006        Err(err) => return Err(err),
2007    };
2008    parent_dir.insert(file_name, File::new(tokens.to_string()))?;
2009    Ok(output)
2010}
2011
2012pub fn type_ident(type_name: &String) -> Ident {
2013    format_ident!("{}", type_name.to_case(Case::UpperCamel))
2014}
2015
2016pub fn struct_field_const_id_ident(struct_name: &String, field_name: &String) -> Ident {
2017    format_ident!(
2018        "{}_{}_FIELD_RAW_ID",
2019        struct_name.to_case(Case::UpperSnake),
2020        field_name.to_case(Case::UpperSnake)
2021    )
2022}
2023
2024pub fn struct_field_ident(struct_name: &String, field_name: &String) -> TokenStream {
2025    format!(
2026        "{}::{}",
2027        struct_name.to_case(Case::UpperCamel),
2028        field_name.to_case(Case::UpperCamel)
2029    )
2030    .parse()
2031    .unwrap()
2032}
2033
2034pub fn struct_field_intermediate_variable_ident(
2035    struct_name: &String,
2036    field_name: &String,
2037) -> Ident {
2038    format_ident!(
2039        "{}_{}",
2040        struct_name.to_case(Case::Snake),
2041        field_name.to_case(Case::Snake),
2042    )
2043}
2044
2045pub fn enum_variant_ident(enum_name: &String, variant_name: &String) -> TokenStream {
2046    format!(
2047        "{}::{}",
2048        enum_name.to_case(Case::UpperCamel),
2049        variant_name.to_case(Case::UpperCamel),
2050    )
2051    .parse()
2052    .unwrap()
2053}
2054
2055pub fn enum_variant_const_id_ident(enum_name: &String, variant_name: &String) -> Ident {
2056    format_ident!(
2057        "{}_{}_VARIANT_RAW_ID",
2058        enum_name.to_case(Case::UpperSnake),
2059        variant_name.to_case(Case::UpperSnake),
2060    )
2061}
2062
2063/// Generates the const declaration of the ID associated to the given name (and ident).
2064pub fn generate_const_id_declaration(
2065    name: &String,
2066    ident: &Ident,
2067    id: &Uuid,
2068    public: Public,
2069) -> TokenStream {
2070    let id_str = id.to_string();
2071    let id_bytes = RawUuidValue(id);
2072    let const_id_doc = format!("{}: {}", name, id_str);
2073    let maybe_pub = match public {
2074        Public::Yes => quote! { pub },
2075        Public::No => quote! {},
2076    };
2077    quote! {
2078      #[doc = #const_id_doc]
2079      #maybe_pub const #ident: [u8; 16] = #id_bytes;
2080    }
2081}
2082
2083pub fn function_const_id_ident(function_name: &String) -> Ident {
2084    format_ident!(
2085        "{}_FUNCTION_RAW_ID",
2086        function_name.to_case(Case::UpperSnake),
2087    )
2088}
2089
2090pub fn function_param_const_id_ident(function_name: &String, param_name: &String) -> Ident {
2091    format_ident!(
2092        "{}_{}_PARAMETER_RAW_ID",
2093        function_name.to_case(Case::UpperSnake),
2094        param_name.to_case(Case::UpperSnake),
2095    )
2096}
2097
2098fn param_ident(param_id: &Uuid, param: &Parameter) -> Ident {
2099    let param_id_sanitized = param_id.to_string().replace("-", "");
2100    format_ident!(
2101        "param_{}_{}",
2102        param.name.to_case(Case::Snake),
2103        param_id_sanitized
2104    )
2105}
2106
2107pub fn variable_ident(name: &String) -> Ident {
2108    format_ident!("{}", name.to_case(Case::Snake))
2109}
2110
2111async fn type_ident_from_frozen(
2112    ty: &FrozenTy,
2113    registry: &mut dyn ReadableRegistry,
2114    with_mod: PrefixWithMod,
2115) -> Result<TokenStream, GenerationError> {
2116    Ok(match ty {
2117        FrozenTy::Primitive(primitive) => match *primitive {
2118            Primitive::UNIT => quote! { () },
2119            Primitive::BOOLEAN => quote!(bool),
2120            Primitive::U8 => quote!(u8),
2121            Primitive::U16 => quote!(u16),
2122            Primitive::U32 => quote!(u32),
2123            Primitive::U64 => quote!(u64),
2124            Primitive::I8 => quote!(i8),
2125            Primitive::I16 => quote!(i16),
2126            Primitive::I32 => quote!(i32),
2127            Primitive::I64 => quote!(i64),
2128            Primitive::F32 => quote!(f32),
2129            Primitive::F64 => quote!(f64),
2130            Primitive::STRING => quote!(String),
2131            Primitive::ARRAY_BOOLEAN => quote!(Vec<bool>),
2132            Primitive::ARRAY_U8 => quote!(Vec<u8>),
2133            Primitive::ARRAY_U16 => quote!(Vec<u16>),
2134            Primitive::ARRAY_U32 => quote!(Vec<u32>),
2135            Primitive::ARRAY_U64 => quote!(Vec<u64>),
2136            Primitive::ARRAY_I8 => quote!(Vec<i8>),
2137            Primitive::ARRAY_I16 => quote!(Vec<i16>),
2138            Primitive::ARRAY_I32 => quote!(Vec<i32>),
2139            Primitive::ARRAY_I64 => quote!(Vec<i64>),
2140            Primitive::ARRAY_F32 => quote!(Vec<f32>),
2141            Primitive::ARRAY_F64 => quote!(Vec<f64>),
2142            Primitive::ARRAY_STRING => quote!(Vec<String>),
2143        },
2144        FrozenTy::FrozenScalar(scalar) => {
2145            type_ident_from_id(&scalar.reference.id, registry, with_mod).await?
2146        }
2147        FrozenTy::FrozenArray(array) => {
2148            let type_ident = type_ident_from_id(&array.reference.id, registry, with_mod).await?;
2149            quote! { Vec<#type_ident> }
2150        }
2151    })
2152}
2153
2154/// The well-known "dynamic" record ids. A field referencing one of these is a
2155/// deliberately open/recursive union: rather than a declared struct, it is kept
2156/// as the generic `arora_types::value::Value` escape hatch (see the module docs
2157/// / PR notes). This is how e.g. a Vizij keyframe `value` — whose leaves are a
2158/// mix of primitives, declared structs and further unions — is represented.
2159fn is_dynamic_value_id(id: &Uuid) -> bool {
2160    *id == *arora_types::ty::ARRAY_VALUE_ID || *id == *arora_types::ty::KEY_VALUE_ID
2161}
2162
2163/// The Rust type a dynamic-value field takes: the generic runtime `Value`.
2164fn dynamic_value_type() -> TokenStream {
2165    quote! { arora_types::value::Value }
2166}
2167
2168async fn type_ident_from_id(
2169    id: &Uuid,
2170    registry: &mut dyn ReadableRegistry,
2171    with_mod: PrefixWithMod,
2172) -> Result<TokenStream, GenerationError> {
2173    if is_dynamic_value_id(id) {
2174        return Ok(dynamic_value_type());
2175    }
2176    let type_def = registry
2177        .get_type(&Selector::Id(id.to_owned()), &VersionReq::STAR)
2178        .await
2179        .map_err(GenerationError::RegistryError)?;
2180    type_ident_from_definition(&type_def, id, registry, with_mod).await
2181}
2182
2183async fn type_ident_from_definition(
2184    type_def: &TypeDefinitionFrozen,
2185    id: &Uuid,
2186    registry: &mut dyn ReadableRegistry,
2187    with_mod: PrefixWithMod,
2188) -> Result<TokenStream, GenerationError> {
2189    Ok(match type_def {
2190        TypeDefinitionFrozen::Primitive(primitive) => match primitive {
2191            PrimitiveKind::Unit => quote! { () },
2192            PrimitiveKind::Boolean => quote!(bool),
2193            PrimitiveKind::U8 => quote!(u8),
2194            PrimitiveKind::U16 => quote!(u16),
2195            PrimitiveKind::U32 => quote!(u32),
2196            PrimitiveKind::U64 => quote!(u64),
2197            PrimitiveKind::I8 => quote!(i8),
2198            PrimitiveKind::I16 => quote!(i16),
2199            PrimitiveKind::I32 => quote!(i32),
2200            PrimitiveKind::I64 => quote!(i64),
2201            PrimitiveKind::F32 => quote!(f32),
2202            PrimitiveKind::F64 => quote!(f64),
2203            PrimitiveKind::String => quote!(String),
2204            PrimitiveKind::ArrayBoolean => quote!(Vec<bool>),
2205            PrimitiveKind::ArrayU8 => quote!(Vec<u8>),
2206            PrimitiveKind::ArrayU16 => quote!(Vec<u16>),
2207            PrimitiveKind::ArrayU32 => quote!(Vec<u32>),
2208            PrimitiveKind::ArrayU64 => quote!(Vec<u64>),
2209            PrimitiveKind::ArrayI8 => quote!(Vec<i8>),
2210            PrimitiveKind::ArrayI16 => quote!(Vec<i16>),
2211            PrimitiveKind::ArrayI32 => quote!(Vec<i32>),
2212            PrimitiveKind::ArrayI64 => quote!(Vec<i64>),
2213            PrimitiveKind::ArrayF32 => quote!(Vec<f32>),
2214            PrimitiveKind::ArrayF64 => quote!(Vec<f64>),
2215            PrimitiveKind::ArrayString => quote!(Vec<String>),
2216        },
2217        TypeDefinitionFrozen::Enumeration(enumeration) => {
2218            type_ident_from_name_and_id(&enumeration.name, id, registry, with_mod)
2219                .await
2220                .map_err(GenerationError::RegistryError)?
2221        }
2222        TypeDefinitionFrozen::Structure(structure) => {
2223            type_ident_from_name_and_id(&structure.name, id, registry, with_mod)
2224                .await
2225                .map_err(GenerationError::RegistryError)?
2226        }
2227    })
2228}
2229
2230async fn type_ident_from_name_and_id(
2231    name: &String,
2232    id: &Uuid,
2233    registry: &mut dyn ReadableRegistry,
2234    with_mod: PrefixWithMod,
2235) -> Result<TokenStream, RegistryError> {
2236    let mod_prefix = match with_mod {
2237        PrefixWithMod::Yes => generated_mod_ident_from_id(id, registry).await?,
2238        PrefixWithMod::No => quote! {},
2239    };
2240    let type_ident = type_ident(name);
2241    Ok(quote! { #mod_prefix #type_ident })
2242}
2243
2244fn type_kind_ident_from_primitive(primitive: &PrimitiveKind) -> TokenStream {
2245    match primitive {
2246        PrimitiveKind::Unit => quote! { TYPE_UNIT },
2247        PrimitiveKind::Boolean => quote! { TYPE_BOOLEAN },
2248        PrimitiveKind::U8 => quote! { TYPE_U8 },
2249        PrimitiveKind::U16 => quote! { TYPE_U16 },
2250        PrimitiveKind::U32 => quote! { TYPE_U32 },
2251        PrimitiveKind::U64 => quote! { TYPE_U64 },
2252        PrimitiveKind::I8 => quote! { TYPE_I8 },
2253        PrimitiveKind::I16 => quote! { TYPE_I16 },
2254        PrimitiveKind::I32 => quote! { TYPE_I32 },
2255        PrimitiveKind::I64 => quote! { TYPE_I64 },
2256        PrimitiveKind::F32 => quote! { TYPE_F32 },
2257        PrimitiveKind::F64 => quote! { TYPE_F64 },
2258        PrimitiveKind::String => quote! { TYPE_STRING },
2259        PrimitiveKind::ArrayBoolean
2260        | PrimitiveKind::ArrayU8
2261        | PrimitiveKind::ArrayU16
2262        | PrimitiveKind::ArrayU32
2263        | PrimitiveKind::ArrayU64
2264        | PrimitiveKind::ArrayI8
2265        | PrimitiveKind::ArrayI16
2266        | PrimitiveKind::ArrayI32
2267        | PrimitiveKind::ArrayI64
2268        | PrimitiveKind::ArrayF32
2269        | PrimitiveKind::ArrayF64
2270        | PrimitiveKind::ArrayString => quote! { TYPE_ARRAY },
2271    }
2272}
2273
2274/// Generates the identifier for the mod publishing the record of the given id.
2275async fn generated_mod_ident_from_id(
2276    id: &Uuid,
2277    registry: &mut dyn ReadableRegistry,
2278) -> Result<TokenStream, RegistryError> {
2279    let mod_path = registry.resolve_id(id).await?;
2280    let mod_ident = mod_ident_from_path(&mod_path);
2281    Ok(quote! { arora_generated::#mod_ident })
2282}
2283
2284fn mod_ident_from_path(path: &str) -> TokenStream {
2285    let path_parts = path.split(".").collect::<Vec<&str>>();
2286    let path_parts = path_parts
2287        .iter()
2288        .map(|part| format_ident!("{}", part.to_case(Case::Snake)));
2289    quote! { #(#path_parts ::)* }
2290}
2291
2292#[derive(Clone, Copy, Eq, PartialEq)]
2293pub enum CheckType {
2294    /// assert_eq!-based checks — panics on mismatch (struct/import deserialization contexts)
2295    Yes,
2296    /// return Err(String) — for export handler Result<Box<[u8]>, String> closure context
2297    YesResult,
2298    No,
2299}
2300
2301#[derive(Clone, Copy, Eq, PartialEq)]
2302pub enum PrefixWithMod {
2303    Yes,
2304    No,
2305}
2306
2307#[derive(Clone, Copy, Eq, PartialEq)]
2308pub enum Public {
2309    Yes,
2310    No,
2311}
2312
2313#[derive(Display, Debug)]
2314pub enum GenerationError {
2315    ModuleDeclarationError(ModuleDeclarationError),
2316    RegistryError(RegistryError),
2317    VfsError(VfsError),
2318    IoError(std::io::Error),
2319    Generic(String),
2320}
2321
2322impl std::error::Error for GenerationError {}
2323
2324/// A helper to format a Uuid into an inlined byte array.
2325pub struct RawUuidValue<'a>(pub &'a Uuid);
2326
2327impl<'a> Display for RawUuidValue<'a> {
2328    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2329        write!(f, "{:#04x?}", self.0.as_bytes())
2330    }
2331}
2332
2333impl<'a> ToTokens for RawUuidValue<'a> {
2334    fn to_tokens(&self, tokens: &mut TokenStream) {
2335        let new_tokens: TokenStream = self.to_string().parse().unwrap();
2336        tokens.extend(new_tokens);
2337    }
2338}