Skip to main content

arora_module_rust/
lib.rs

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