Skip to main content

aver/codegen/rust/
mod.rs

1/// Rust backend for the Aver transpiler.
2///
3/// Transforms Aver AST → valid Rust source code.
4mod builtins;
5mod expr;
6mod liveness;
7mod pattern;
8mod policy;
9mod project;
10mod runtime;
11mod syntax;
12mod toplevel;
13mod types;
14
15use std::collections::HashSet;
16
17use crate::ast::TopLevel;
18use crate::codegen::{CodegenContext, ProjectOutput};
19use crate::types::Type;
20
21/// Transpile an Aver program to a Rust project.
22pub fn transpile(ctx: &CodegenContext) -> ProjectOutput {
23    let mut sections = vec![
24        "#![allow(unused_variables, unused_mut, dead_code, unused_imports, unused_parens, non_snake_case, non_camel_case_types, unreachable_patterns)]".to_string(),
25        "use std::collections::HashMap;".to_string(),
26        String::new(),
27        runtime::generate_runtime(),
28        String::new(),
29    ];
30
31    // Policy module (from aver.toml, if present)
32    if let Some(ref config) = ctx.policy {
33        sections.push(policy::generate_policy_runtime(config));
34        sections.push(String::new());
35    }
36
37    // Collect info about which services are used at runtime
38    let used_services = detect_used_services(ctx);
39    let needs_http_types = needs_named_type(ctx, "Header")
40        || needs_named_type(ctx, "HttpResponse")
41        || needs_named_type(ctx, "HttpRequest");
42    let needs_tcp_types = needs_named_type(ctx, "Tcp.Connection");
43
44    // Service runtimes and service type definitions (separately gated).
45    let has_tcp_runtime = used_services.contains("Tcp");
46    let has_http_runtime = used_services.contains("Http");
47    let has_http_server_runtime = used_services.contains("HttpServer");
48
49    let has_tcp_types = has_tcp_runtime || needs_tcp_types;
50    let has_http_types = has_http_runtime || has_http_server_runtime || needs_http_types;
51    let has_http_server_types = has_http_server_runtime || needs_named_type(ctx, "HttpRequest");
52
53    if has_tcp_types {
54        sections.push(runtime::generate_tcp_types());
55        sections.push(String::new());
56    }
57
58    if has_http_types {
59        sections.push(runtime::generate_http_types());
60        sections.push(String::new());
61    }
62
63    if has_http_server_types {
64        sections.push(runtime::generate_http_server_types());
65        sections.push(String::new());
66    }
67
68    // Module type definitions (inlined from depends)
69    for module in &ctx.modules {
70        for td in &module.type_defs {
71            if is_shared_runtime_type(td) {
72                continue;
73            }
74            sections.push(toplevel::emit_type_def(td));
75            sections.push(String::new());
76        }
77    }
78
79    // Module function definitions (inlined from depends)
80    for module in &ctx.modules {
81        for fd in &module.fn_defs {
82            let is_memo = ctx.memo_fns.contains(&fd.name);
83            sections.push(toplevel::emit_fn_def(fd, is_memo, ctx));
84            sections.push(String::new());
85        }
86    }
87
88    // Type definitions (structs and enums)
89    for td in &ctx.type_defs {
90        if is_shared_runtime_type(td) {
91            continue;
92        }
93        sections.push(toplevel::emit_type_def(td));
94        sections.push(String::new());
95    }
96
97    // Function definitions (excluding main)
98    for fd in &ctx.fn_defs {
99        if fd.name == "main" {
100            continue;
101        }
102        let is_memo = ctx.memo_fns.contains(&fd.name);
103        sections.push(toplevel::emit_fn_def(fd, is_memo, ctx));
104        sections.push(String::new());
105    }
106
107    // Main function
108    let main_fn = ctx.fn_defs.iter().find(|fd| fd.name == "main");
109    let top_level_stmts: Vec<_> = ctx
110        .items
111        .iter()
112        .filter_map(|item| {
113            if let TopLevel::Stmt(stmt) = item {
114                Some(stmt)
115            } else {
116                None
117            }
118        })
119        .collect();
120
121    sections.push(toplevel::emit_main(main_fn, &top_level_stmts, ctx));
122    sections.push(String::new());
123
124    // Verify blocks → #[cfg(test)]
125    let verify_blocks: Vec<_> = ctx
126        .items
127        .iter()
128        .filter_map(|item| {
129            if let TopLevel::Verify(vb) = item {
130                Some(vb)
131            } else {
132                None
133            }
134        })
135        .collect();
136
137    if !verify_blocks.is_empty() {
138        sections.push(toplevel::emit_verify_blocks(&verify_blocks, ctx));
139    }
140
141    let main_rs = sections.join("\n");
142    let cargo_toml = project::generate_cargo_toml(
143        &ctx.project_name,
144        &used_services,
145        ctx.policy.is_some(),
146        &std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("aver-rt"),
147    );
148
149    ProjectOutput {
150        files: vec![
151            ("Cargo.toml".to_string(), cargo_toml),
152            ("src/main.rs".to_string(), main_rs),
153        ],
154    }
155}
156
157/// Detect which effectful services are used in the program (including modules).
158fn detect_used_services(ctx: &CodegenContext) -> HashSet<String> {
159    let mut services = HashSet::new();
160    for item in &ctx.items {
161        if let TopLevel::FnDef(fd) = item {
162            for eff in &fd.effects {
163                services.insert(eff.clone());
164                if let Some((service, _)) = eff.split_once('.') {
165                    services.insert(service.to_string());
166                }
167            }
168        }
169    }
170    for module in &ctx.modules {
171        for fd in &module.fn_defs {
172            for eff in &fd.effects {
173                services.insert(eff.clone());
174                if let Some((service, _)) = eff.split_once('.') {
175                    services.insert(service.to_string());
176                }
177            }
178        }
179    }
180    services
181}
182
183fn is_shared_runtime_type(td: &crate::ast::TypeDef) -> bool {
184    matches!(
185        td,
186        crate::ast::TypeDef::Product { name, .. }
187            if matches!(name.as_str(), "Header" | "HttpResponse" | "HttpRequest")
188    )
189}
190
191fn needs_named_type(ctx: &CodegenContext, wanted: &str) -> bool {
192    ctx.fn_sigs.values().any(|(params, ret, _effects)| {
193        params.iter().any(|p| type_contains_named(p, wanted)) || type_contains_named(ret, wanted)
194    })
195}
196
197fn type_contains_named(ty: &Type, wanted: &str) -> bool {
198    match ty {
199        Type::Named(name) => name == wanted,
200        Type::Result(ok, err) => {
201            type_contains_named(ok, wanted) || type_contains_named(err, wanted)
202        }
203        Type::Option(inner) | Type::List(inner) => type_contains_named(inner, wanted),
204        Type::Tuple(items) => items.iter().any(|t| type_contains_named(t, wanted)),
205        Type::Map(k, v) => type_contains_named(k, wanted) || type_contains_named(v, wanted),
206        Type::Fn(params, ret, _effects) => {
207            params.iter().any(|t| type_contains_named(t, wanted))
208                || type_contains_named(ret, wanted)
209        }
210        Type::Int | Type::Float | Type::Str | Type::Bool | Type::Unit | Type::Unknown => false,
211    }
212}