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/result_type.rs
Line
Count
Source
1
//! Result type support for Ruchy
2
//!
3
//! This module provides comprehensive `Result<T, E>` type support including:
4
//! - Result constructors (Ok/Err)
5
//! - Pattern matching on Results
6
//! - ? operator for error propagation
7
//! - Result combinators (map, `and_then`, etc.)
8
9
use super::Transpiler;
10
use crate::frontend::ast::Expr;
11
use anyhow::Result;
12
use proc_macro2::TokenStream;
13
use quote::{format_ident, quote};
14
15
impl Transpiler {
16
    /// Generates Result type helpers and combinators
17
    ///
18
    /// # Examples
19
    ///
20
    /// ```
21
    /// use ruchy::Transpiler;
22
    /// 
23
    /// let helpers = Transpiler::generate_result_helpers();
24
    /// let code = helpers.to_string();
25
    /// assert!(code.contains("trait ResultExt"));
26
    /// assert!(code.contains("map_err_with"));
27
    /// assert!(code.contains("unwrap_or_else_with"));
28
    /// assert!(code.contains("and_then_with"));
29
    /// assert!(code.contains("or_else_with"));
30
    /// ```
31
0
    pub fn generate_result_helpers() -> TokenStream {
32
0
        quote! {
33
            // Result extension trait for additional combinators
34
            trait ResultExt<T, E> {
35
                fn map_err_with<F, E2>(self, f: F) -> Result<T, E2>
36
                where
37
                    F: FnOnce(E) -> E2;
38
39
                fn unwrap_or_else_with<F>(self, f: F) -> T
40
                where
41
                    F: FnOnce(E) -> T;
42
43
                fn and_then_with<F, U>(self, f: F) -> Result<U, E>
44
                where
45
                    F: FnOnce(T) -> Result<U, E>;
46
47
                fn or_else_with<F, E2>(self, f: F) -> Result<T, E2>
48
                where
49
                    F: FnOnce(E) -> Result<T, E2>;
50
            }
51
52
            impl<T, E> ResultExt<T, E> for Result<T, E> {
53
                fn map_err_with<F, E2>(self, f: F) -> Result<T, E2>
54
                where
55
                    F: FnOnce(E) -> E2
56
                {
57
                    self.map_err(f)
58
                }
59
60
                fn unwrap_or_else_with<F>(self, f: F) -> T
61
                where
62
                    F: FnOnce(E) -> T
63
                {
64
                    self.unwrap_or_else(f)
65
                }
66
67
                fn and_then_with<F, U>(self, f: F) -> Result<U, E>
68
                where
69
                    F: FnOnce(T) -> Result<U, E>
70
                {
71
                    self.and_then(f)
72
                }
73
74
                fn or_else_with<F, E2>(self, f: F) -> Result<T, E2>
75
                where
76
                    F: FnOnce(E) -> Result<T, E2>
77
                {
78
                    self.or_else(f)
79
                }
80
            }
81
        }
82
0
    }
83
84
    /// Transpiles Result pattern matching
85
    ///
86
    /// # Examples
87
    ///
88
    /// ```
89
    /// use ruchy::{Transpiler, Parser};
90
    /// 
91
    /// let transpiler = Transpiler::new();
92
    /// let mut parser = Parser::new(r#"match result { Ok(val) => val, Err(e) => 0 }"#);
93
    /// let ast = parser.parse().unwrap();
94
    /// 
95
    /// let result = transpiler.transpile(&ast).unwrap();
96
    /// let code = result.to_string();
97
    /// assert!(code.contains("Ok"));
98
    /// assert!(code.contains("Err"));
99
    /// ```
100
0
    pub fn transpile_result_match(
101
0
        &self,
102
0
        expr: &Expr,
103
0
        arms: &[(String, Expr)],
104
0
    ) -> Result<TokenStream> {
105
0
        let expr_tokens = self.transpile_expr(expr)?;
106
0
        let mut match_arms = Vec::new();
107
108
0
        for (pattern, body) in arms {
109
0
            let body_tokens = self.transpile_expr(body)?;
110
0
            let arm_tokens = if pattern == "Ok" {
111
0
                quote! { Ok(value) => #body_tokens }
112
0
            } else if pattern == "Err" {
113
0
                quote! { Err(error) => #body_tokens }
114
            } else {
115
0
                quote! { _ => #body_tokens }
116
            };
117
0
            match_arms.push(arm_tokens);
118
        }
119
120
0
        Ok(quote! {
121
            match #expr_tokens {
122
                #(#match_arms,)*
123
            }
124
        })
125
0
    }
126
127
    /// Transpiles Result chaining with ? operator
128
    ///
129
    /// # Examples
130
    ///
131
    /// ```
132
    /// use ruchy::{Transpiler, Parser};
133
    /// 
134
    /// let transpiler = Transpiler::new();
135
    /// let mut parser = Parser::new("result?");
136
    /// let ast = parser.parse().unwrap();
137
    /// 
138
    /// let result = transpiler.transpile(&ast).unwrap();
139
    /// let code = result.to_string();
140
    /// assert!(code.contains("?"));
141
    /// ```
142
0
    pub fn transpile_result_chain(&self, operations: &[Expr]) -> Result<TokenStream> {
143
0
        if operations.is_empty() {
144
0
            return Ok(quote! { Ok(()) });
145
0
        }
146
147
0
        let mut chain = self.transpile_expr(&operations[0])?;
148
149
0
        for op in &operations[1..] {
150
0
            let op_tokens = self.transpile_expr(op)?;
151
0
            chain = quote! { #chain.and_then(|_| #op_tokens) };
152
        }
153
154
0
        Ok(chain)
155
0
    }
156
157
    /// Transpiles Result unwrapping with default
158
0
    pub fn transpile_result_unwrap_or(&self, result: &Expr, default: &Expr) -> Result<TokenStream> {
159
0
        let result_tokens = self.transpile_expr(result)?;
160
0
        let default_tokens = self.transpile_expr(default)?;
161
162
0
        Ok(quote! {
163
0
            #result_tokens.unwrap_or(#default_tokens)
164
0
        })
165
0
    }
166
167
    /// Transpiles Result mapping
168
0
    pub fn transpile_result_map(&self, result: &Expr, mapper: &Expr) -> Result<TokenStream> {
169
0
        let result_tokens = self.transpile_expr(result)?;
170
0
        let mapper_tokens = self.transpile_expr(mapper)?;
171
172
0
        Ok(quote! {
173
0
            #result_tokens.map(#mapper_tokens)
174
0
        })
175
0
    }
176
177
    /// Transpiles custom error types
178
0
    pub fn transpile_error_type(
179
0
        &self,
180
0
        name: &str,
181
0
        variants: &[(String, Option<String>)],
182
0
    ) -> TokenStream {
183
0
        let error_name = format_ident!("{}", name);
184
0
        let mut variant_tokens = Vec::new();
185
186
0
        for (variant, data) in variants {
187
0
            let variant_ident = format_ident!("{}", variant);
188
0
            let variant_token = if let Some(data_type) = data {
189
                // Parse the type string to handle paths like std::io::Error
190
0
                let data_type_tokens: TokenStream = data_type.parse().unwrap_or_else(|_| {
191
                    // If parsing fails, fall back to String
192
0
                    quote! { String }
193
0
                });
194
0
                quote! { #variant_ident(#data_type_tokens) }
195
            } else {
196
0
                quote! { #variant_ident }
197
            };
198
0
            variant_tokens.push(variant_token);
199
        }
200
201
0
        quote! {
202
            #[derive(Debug, Clone)]
203
            enum #error_name {
204
                #(#variant_tokens,)*
205
            }
206
207
            impl std::fmt::Display for #error_name {
208
                fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
209
                    write!(f, "{:?}", self)
210
                }
211
            }
212
213
            impl std::error::Error for #error_name {}
214
        }
215
0
    }
216
}
217
218
/// Generate Result type test cases
219
#[cfg(test)]
220
mod tests {
221
    use super::*;
222
223
    #[test]
224
    fn test_result_helpers_generation() {
225
        let helpers = Transpiler::generate_result_helpers();
226
        let code = helpers.to_string();
227
        assert!(code.contains("ResultExt"));
228
        assert!(code.contains("map_err_with"));
229
        assert!(code.contains("unwrap_or_else_with"));
230
        assert!(code.contains("and_then_with"));
231
        assert!(code.contains("or_else_with"));
232
    }
233
234
    #[test]
235
    fn test_transpile_error_type() {
236
        let transpiler = Transpiler::new();
237
        let variants = vec![
238
            ("NotFound".to_string(), None),
239
            ("InvalidInput".to_string(), Some("String".to_string())),
240
            ("NetworkError".to_string(), Some("std::io::Error".to_string())),
241
        ];
242
        
243
        let error_type = transpiler.transpile_error_type("AppError", &variants);
244
        let code = error_type.to_string();
245
        
246
        // Check enum definition
247
        assert!(code.contains("enum AppError"));
248
        assert!(code.contains("NotFound"));
249
        assert!(code.contains("InvalidInput") && code.contains("String"));
250
        assert!(code.contains("NetworkError") && code.contains("std") && code.contains("io") && code.contains("Error"));
251
        
252
        // Check trait implementations
253
        assert!(code.contains("derive") && code.contains("Debug") && code.contains("Clone"));
254
        assert!(code.contains("impl") && code.contains("std") && code.contains("fmt") && code.contains("Display"));
255
        assert!(code.contains("impl") && code.contains("std") && code.contains("error") && code.contains("Error"));
256
    }
257
}