Coverage Report

Created: 2025-09-08 21:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/home/noah/src/ruchy/src/backend/transpiler/actors.rs
Line
Count
Source
1
//! Actor system transpilation
2
3
#![allow(clippy::missing_errors_doc)]
4
#![allow(clippy::wildcard_imports)]
5
6
use super::*;
7
use crate::frontend::ast::{ActorHandler, StructField};
8
use anyhow::Result;
9
use proc_macro2::TokenStream;
10
use quote::{format_ident, quote};
11
12
impl Transpiler {
13
    /// Transpiles actor definitions
14
0
    pub fn transpile_actor(
15
0
        &self,
16
0
        name: &str,
17
0
        state: &[StructField],
18
0
        handlers: &[ActorHandler],
19
0
    ) -> Result<TokenStream> {
20
0
        let actor_name = format_ident!("{}", name);
21
0
        let message_enum_name = format_ident!("{}Message", name);
22
23
        // Generate state fields
24
0
        let state_fields: Vec<TokenStream> = state
25
0
            .iter()
26
0
            .map(|field| {
27
0
                let field_name = format_ident!("{}", field.name);
28
0
                let field_type = self
29
0
                    .transpile_type(&field.ty)
30
0
                    .unwrap_or_else(|_| quote! { _ });
31
0
                quote! { #field_name: #field_type }
32
0
            })
33
0
            .collect();
34
35
        // Generate message enum variants
36
0
        let mut message_variants = Vec::new();
37
0
        let mut handler_arms = Vec::new();
38
39
0
        for handler in handlers {
40
0
            let variant_name = format_ident!("{}", handler.message_type);
41
42
0
            if handler.params.is_empty() {
43
                // Simple message without parameters
44
0
                message_variants.push(quote! { #variant_name });
45
46
0
                let body_tokens = self.transpile_expr(&handler.body)?;
47
0
                handler_arms.push(quote! {
48
                    #message_enum_name::#variant_name => {
49
                        #body_tokens
50
                    }
51
                });
52
            } else {
53
                // Message with parameters
54
0
                let param_types: Vec<TokenStream> = handler
55
0
                    .params
56
0
                    .iter()
57
0
                    .map(|p| {
58
0
                        self.transpile_type(&p.ty)
59
0
                            .unwrap_or_else(|_| quote! { String })
60
0
                    })
61
0
                    .collect();
62
63
0
                if param_types.len() == 1 {
64
0
                    message_variants.push(quote! { #variant_name(#(#param_types),*) });
65
                } else {
66
0
                    message_variants.push(quote! { #variant_name { #(#param_types),* } });
67
                }
68
69
                // Generate parameter bindings for the handler
70
0
                let param_names: Vec<_> = handler
71
0
                    .params
72
0
                    .iter()
73
0
                    .map(|p| format_ident!("{}", p.name()))
74
0
                    .collect();
75
76
0
                let body_tokens = self.transpile_expr(&handler.body)?;
77
78
0
                if param_names.len() == 1 {
79
0
                    let param = &param_names[0];
80
0
                    handler_arms.push(quote! {
81
0
                        #message_enum_name::#variant_name(#param) => {
82
0
                            #body_tokens
83
0
                        }
84
0
                    });
85
0
                } else {
86
0
                    handler_arms.push(quote! {
87
                        #message_enum_name::#variant_name { #(#param_names),* } => {
88
                            #body_tokens
89
                        }
90
                    });
91
                }
92
            }
93
        }
94
95
        // Generate the complete actor implementation
96
0
        Ok(quote! {
97
            // Message enum
98
            #[derive(Debug, Clone)]
99
            enum #message_enum_name {
100
                #(#message_variants,)*
101
            }
102
103
            // Actor struct
104
            struct #actor_name {
105
                #(#state_fields,)*
106
                receiver: tokio::sync::mpsc::Receiver<#message_enum_name>,
107
                sender: tokio::sync::mpsc::Sender<#message_enum_name>,
108
            }
109
110
            impl #actor_name {
111
                fn new() -> Self {
112
                    let (sender, receiver) = tokio::sync::mpsc::channel(100);
113
                    Self {
114
                        #(#state_fields: Default::default(),)*
115
                        receiver,
116
                        sender,
117
                    }
118
                }
119
120
                fn sender(&self) -> tokio::sync::mpsc::Sender<#message_enum_name> {
121
                    self.sender.clone()
122
                }
123
124
                async fn run(&mut self) {
125
                    while let Some(msg) = self.receiver.recv().await {
126
                        self.handle_message(msg).await;
127
                    }
128
                }
129
130
                async fn handle_message(&mut self, msg: #message_enum_name) {
131
                    match msg {
132
                        #(#handler_arms)*
133
                    }
134
                }
135
            }
136
        })
137
0
    }
138
139
    /// Transpiles send operations (actor ! message)
140
0
    pub fn transpile_send(&self, actor: &Expr, message: &Expr) -> Result<TokenStream> {
141
0
        let actor_tokens = self.transpile_expr(actor)?;
142
0
        let message_tokens = self.transpile_expr(message)?;
143
144
0
        Ok(quote! {
145
0
            #actor_tokens.send(#message_tokens).await
146
0
        })
147
0
    }
148
149
    /// Transpiles ask operations (actor ? message)
150
0
    pub fn transpile_ask(
151
0
        &self,
152
0
        actor: &Expr,
153
0
        message: &Expr,
154
0
        timeout: Option<&Expr>,
155
0
    ) -> Result<TokenStream> {
156
0
        let actor_tokens = self.transpile_expr(actor)?;
157
0
        let message_tokens = self.transpile_expr(message)?;
158
159
0
        if let Some(timeout_expr) = timeout {
160
0
            let timeout_tokens = self.transpile_expr(timeout_expr)?;
161
0
            Ok(quote! {
162
0
                #actor_tokens.ask(#message_tokens, #timeout_tokens).await
163
0
            })
164
        } else {
165
            // Default timeout of 5 seconds
166
0
            Ok(quote! {
167
0
                #actor_tokens.ask(#message_tokens, std::time::Duration::from_secs(5)).await
168
0
            })
169
        }
170
0
    }
171
172
    /// Transpiles command execution
173
0
    pub fn transpile_command(
174
0
        &self,
175
0
        program: &str,
176
0
        args: &[String],
177
0
        _env: &[(String, String)],
178
0
        _working_dir: &Option<String>,
179
0
    ) -> Result<TokenStream> {
180
0
        let prog_str = program;
181
0
        let args_tokens: Vec<_> = args.iter().map(|arg| quote! { #arg }).collect();
182
183
0
        Ok(quote! {
184
            std::process::Command::new(#prog_str)
185
                .args(&[#(#args_tokens),*])
186
                .output()
187
                .expect("Failed to execute command")
188
        })
189
0
    }
190
}