Skip to main content

abi_loader/
flatten.rs

1use crate::file::{AbiFile, AbiMetadata};
2use crate::resolver::ImportResolver;
3use abi_types::{TypeDef, TypeKind};
4use std::path::{Path, PathBuf};
5
6/// Flatten an ABI file by resolving all imports and inlining types.
7/// Returns the flattened AbiFile.
8pub fn flatten(file_path: &Path, include_dirs: &[PathBuf]) -> anyhow::Result<AbiFile> {
9    flatten_with_options(file_path, include_dirs, false)
10}
11
12/// Flatten an ABI file with verbose output option.
13pub fn flatten_with_options(
14    file_path: &Path,
15    include_dirs: &[PathBuf],
16    verbose: bool,
17) -> anyhow::Result<AbiFile> {
18    /* Load root file and all imports */
19    let mut resolver = ImportResolver::new(include_dirs.to_vec());
20    resolver.load_file_with_imports(file_path, verbose)?;
21
22    let all_files = resolver.get_all_files();
23    if all_files.is_empty() {
24        anyhow::bail!("No ABI files loaded");
25    }
26
27    /* The last file is the root file (it's added after its imports) */
28    let root_file = all_files.last().unwrap();
29
30    /* Collect all types from all files */
31    let mut all_types: Vec<TypeDef> = resolver.get_all_types().to_vec();
32
33    /* Normalize FQDN type references to simple names */
34    normalize_type_refs(&mut all_types, &resolver);
35
36    /* Create the flattened ABI file */
37    let flattened = AbiFile {
38        abi: AbiMetadata {
39            package: root_file.abi.package.clone(),
40            name: root_file.abi.name.clone(),
41            abi_version: root_file.abi.abi_version,
42            package_version: root_file.abi.package_version.clone(),
43            description: root_file.abi.description.clone(),
44            imports: Vec::new(), /* No imports in flattened output */
45            options: root_file.abi.options.clone(),
46        },
47        types: all_types,
48    };
49
50    if verbose {
51        println!(
52            "[~] Flattened {} files into {} types",
53            resolver.loaded_file_count(),
54            flattened.types.len()
55        );
56    }
57
58    Ok(flattened)
59}
60
61/// Flatten an ABI file and return the result as a YAML string.
62pub fn flatten_to_yaml(file_path: &Path, include_dirs: &[PathBuf]) -> anyhow::Result<String> {
63    let flattened = flatten(file_path, include_dirs)?;
64    let yaml = serde_yml::to_string(&flattened)?;
65    Ok(yaml)
66}
67
68/// Normalize FQDN type references to simple names using the import resolver.
69pub fn normalize_type_refs(typedefs: &mut [TypeDef], resolver: &ImportResolver) {
70    for typedef in typedefs.iter_mut() {
71        normalize_type_kind(&mut typedef.kind, resolver);
72    }
73}
74
75fn normalize_type_kind(kind: &mut TypeKind, resolver: &ImportResolver) {
76    match kind {
77        TypeKind::TypeRef(type_ref) => {
78            /* Resolve FQDN to simple name */
79            if let Some(simple_name) = resolver.resolve_type_name(&type_ref.name) {
80                type_ref.name = simple_name;
81            }
82            /* Strip package qualifier — types are now inlined */
83            type_ref.package = None;
84        }
85        TypeKind::Struct(struct_type) => {
86            for field in &mut struct_type.fields {
87                normalize_type_kind(&mut field.field_type, resolver);
88            }
89        }
90        TypeKind::Union(union_type) => {
91            for variant in &mut union_type.variants {
92                normalize_type_kind(&mut variant.variant_type, resolver);
93            }
94        }
95        TypeKind::Enum(enum_type) => {
96            for variant in &mut enum_type.variants {
97                normalize_type_kind(&mut variant.variant_type, resolver);
98            }
99        }
100        TypeKind::Array(array_type) => {
101            normalize_type_kind(&mut array_type.element_type, resolver);
102        }
103        TypeKind::SizeDiscriminatedUnion(sdu_type) => {
104            for variant in &mut sdu_type.variants {
105                normalize_type_kind(&mut variant.variant_type, resolver);
106            }
107        }
108        TypeKind::Primitive(_) => { /* No normalization needed */ }
109    }
110}