## 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
    }