//! Rust code generation backend

use crate::frame_c::compiler::codegen::ast::*;
use crate::frame_c::compiler::codegen::backend::*;
use crate::frame_c::visitors::TargetLanguage;

/// Rust backend for code generation
pub struct RustBackend;

impl LanguageBackend for RustBackend {
    fn emit(&self, node: &CodegenNode, ctx: &mut EmitContext) -> String {
        match node {
            CodegenNode::Module { imports, items } => {
                let mut result = String::new();
                for import in imports {
                    result.push_str(&self.emit(import, ctx));
                    result.push('\n');
                }
                if !imports.is_empty() && !items.is_empty() {
                    result.push('\n');
                }
                for (i, item) in items.iter().enumerate() {
                    if i > 0 {
                        result.push_str("\n\n");
                    }
                    result.push_str(&self.emit(item, ctx));
                }
                result
            }

            CodegenNode::Import {
                module,
                items,
                alias: _,
            } => {
                if items.is_empty() {
                    format!("use {};", module)
                } else {
                    format!("use {}::{{{}}};", module, items.join(", "))
                }
            }

            CodegenNode::Class {
                name,
                fields,
                methods,
                base_classes: _,
                is_abstract: _,
                derives,
                visibility,
            } => {
                let mut result = String::new();

                // Suppress warnings for generated Frame infrastructure
                result.push_str(&format!("{}#[allow(dead_code)]\n", ctx.get_indent()));

                // Derive attributes (for serde, etc.)
                if !derives.is_empty() {
                    result.push_str(&format!(
                        "{}#[derive({})]\n",
                        ctx.get_indent(),
                        derives.join(", ")
                    ));
                }

                // Struct definition
                let vis_kw = match visibility {
                    Visibility::Public => "pub ",
                    _ => "",
                };
                result.push_str(&format!(
                    "{}{}struct {} {{\n",
                    ctx.get_indent(),
                    vis_kw,
                    name
                ));
                ctx.push_indent();
                for field in fields {
                    result.push_str(&self.emit_field(field, ctx));
                }
                ctx.pop_indent();
                result.push_str(&format!("{}}}\n\n", ctx.get_indent()));

                // Impl block - suppress non_snake_case for Frame-generated method names
                result.push_str(&format!("{}#[allow(non_snake_case)]\n", ctx.get_indent()));
                result.push_str(&format!("{}impl {} {{\n", ctx.get_indent(), name));
                ctx.push_indent();
                for (i, method) in methods.iter().enumerate() {
                    if i > 0 {
                        result.push('\n');
                    }
                    result.push_str(&self.emit(method, ctx));
                }
                ctx.pop_indent();
                result.push_str(&format!("{}}}\n", ctx.get_indent()));

                result
            }

            CodegenNode::Enum { name, variants } => {
                let mut result = format!("{}#[allow(dead_code)]\n", ctx.get_indent());
                result.push_str(&format!("{}pub enum {} {{\n", ctx.get_indent(), name));
                ctx.push_indent();
                for variant in variants {
                    result.push_str(&format!("{}{},\n", ctx.get_indent(), variant.name));
                }
                ctx.pop_indent();
                result.push_str(&format!("{}}}\n", ctx.get_indent()));
                result
            }

            CodegenNode::Method {
                name,
                params,
                return_type,
                body,
                is_async,
                is_static,
                visibility,
                decorators: _,
            } => {
                let mut result = String::new();
                let vis = if matches!(visibility, Visibility::Public) {
                    "pub "
                } else {
                    ""
                };

                let params_str = self.emit_params(params, !*is_static);

                // For async Rust kernel/router: use boxed future to break recursion
                let use_boxed_future = *is_async && (name == "__kernel" || name == "__router");
                if use_boxed_future {
                    // Use named lifetime 'a to tie &mut self and parameter references together
                    let boxed_params = params_str
                        .replace("&mut self", "&'a mut self")
                        .replace(", __e: &", ", __e: &'a ");
                    let return_str =
                        " -> std::pin::Pin<Box<dyn std::future::Future<Output = ()> + 'a>>";
                    result.push_str(&format!(
                        "{}{}fn {}<'a>({}){} {{\n",
                        ctx.get_indent(),
                        vis,
                        name,
                        boxed_params,
                        return_str
                    ));
                    ctx.push_indent();
                    result.push_str(&format!("{}Box::pin(async move {{\n", ctx.get_indent()));
                    ctx.push_indent();
                    for stmt in body {
                        result.push_str(&self.emit(stmt, ctx));
                        if self.needs_semicolon(stmt) {
                            result.push_str(";\n");
                        } else {
                            result.push('\n');
                        }
                    }
                    ctx.pop_indent();
                    result.push_str(&format!("{}}})\n", ctx.get_indent()));
                    ctx.pop_indent();
                    result.push_str(&format!("{}}}\n", ctx.get_indent()));
                    return result;
                }

                let async_kw = if *is_async { "async " } else { "" };
                let return_str = return_type
                    .as_ref()
                    .map(|rt| format!(" -> {}", self.convert_type(rt)))
                    .unwrap_or_default();

                result.push_str(&format!(
                    "{}{}{}fn {}({}){} {{\n",
                    ctx.get_indent(),
                    vis,
                    async_kw,
                    name,
                    params_str,
                    return_str
                ));

                ctx.push_indent();
                let has_return_type = return_type.is_some();
                let stmt_count = body.len();
                for (i, stmt) in body.iter().enumerate() {
                    let is_last = i == stmt_count - 1;
                    result.push_str(&self.emit(stmt, ctx));
                    // Don't add semicolon to last statement if method has return type
                    // (the last expression IS the return value in Rust)
                    if is_last && has_return_type {
                        result.push('\n');
                    } else if self.needs_semicolon(stmt) {
                        result.push_str(";\n");
                    } else {
                        result.push('\n');
                    }
                }
                ctx.pop_indent();
                result.push_str(&format!("{}}}\n", ctx.get_indent()));
                result
            }

            CodegenNode::Constructor {
                params,
                body,
                super_call: _,
            } => {
                // RFC-0020: two construction artifacts (was three under RFC-0017):
                //   pub fn new() -> Self            — bare framework; IS @@!Foo()
                //   pub fn __create(<params>) -> Self — factory + start-$>; IS @@Foo(args)
                //
                // The intermediate `__frame_init` method is gone; its
                // body is absorbed into `__create` after the `Self::new()`
                // call, with a `self.` → `c.` rewrite to retarget the
                // local instance.
                //
                // Call-site lowering:
                //   - `@@Counter(7)` → `Counter::__create(7)`
                //   - `@@!Counter()` → `Counter::new()`
                //
                // Body classification:
                //   - Field assignments whose value references a ctor
                //     param → placed in `__frame_init` (`self.f = expr`);
                //     in `new()` the field is initialised with
                //     `<Type as Default>::default()`.
                //   - All other field assignments → `new()` only.
                //   - Non-assignment statements (cascade triggers,
                //     state setup) → `__frame_init` only, with `self.`
                //     references kept (no replacement to `this.`).
                let mut framework_fields: Vec<(String, String)> = Vec::new();
                let mut param_bound_fields: Vec<(String, String, String)> = Vec::new(); // (field, value, default)
                let mut cascade_stmts: Vec<&CodegenNode> = Vec::new();

                for stmt in body {
                    if let CodegenNode::Assignment { target, value } = stmt {
                        if let CodegenNode::FieldAccess { object, field } = target.as_ref() {
                            if matches!(object.as_ref(), CodegenNode::SelfRef) {
                                let value_str = if let CodegenNode::Literal(Literal::String(s)) =
                                    value.as_ref()
                                {
                                    format!(
                                        "String::from(\"{}\")",
                                        s.replace("\\", "\\\\").replace("\"", "\\\"")
                                    )
                                } else {
                                    self.emit(value, ctx)
                                };
                                let mentions_param = params.iter().any(|p| {
                                    value_str
                                        .split(|c: char| !c.is_alphanumeric() && c != '_')
                                        .any(|w| w == p.name)
                                });
                                if mentions_param {
                                    // Find the param this field most directly
                                    // references and use its type for the
                                    // `Default::default()` placeholder. If the
                                    // value is a direct param ident, easy;
                                    // otherwise fall back to a generic Default.
                                    let direct = params.iter().find(|p| p.name == value_str.trim());
                                    let default_expr = if let Some(p) = direct {
                                        let ty = p
                                            .type_annotation
                                            .as_ref()
                                            .map(|t| self.convert_type(t))
                                            .unwrap_or_else(|| "()".to_string());
                                        format!("<{} as Default>::default()", ty)
                                    } else {
                                        "Default::default()".to_string()
                                    };
                                    param_bound_fields.push((
                                        field.clone(),
                                        value_str,
                                        default_expr,
                                    ));
                                } else {
                                    framework_fields.push((field.clone(), value_str));
                                }
                                continue;
                            }
                        }
                    }
                    cascade_stmts.push(stmt);
                }

                // Emit `pub fn new() -> Self` — bare framework
                let mut result = format!("{}pub fn new() -> Self {{\n", ctx.get_indent());
                ctx.push_indent();
                result.push_str(&format!("{}Self {{\n", ctx.get_indent()));
                ctx.push_indent();
                for (field, value) in &framework_fields {
                    result.push_str(&format!("{}{}: {},\n", ctx.get_indent(), field, value));
                }
                for (field, _value, default) in &param_bound_fields {
                    result.push_str(&format!("{}{}: {},\n", ctx.get_indent(), field, default));
                }
                ctx.pop_indent();
                result.push_str(&format!("{}}}\n", ctx.get_indent()));
                ctx.pop_indent();
                result.push_str(&format!("{}}}\n", ctx.get_indent()));

                // Emit `pub fn __create(<params>) -> Self` — factory +
                // start-$>. Per RFC-0020 the body that used to live in
                // `__frame_init` is absorbed inline here, with `self.`
                // → `c.` rewrite to retarget the local instance.
                result.push('\n');
                let create_params = self.emit_params(params, false);
                result.push_str(&format!(
                    "{}pub fn __create({}) -> Self {{\n",
                    ctx.get_indent(),
                    create_params
                ));
                ctx.push_indent();
                result.push_str(&format!("{}let mut c = Self::new();\n", ctx.get_indent()));
                for (field, value, _default) in &param_bound_fields {
                    result.push_str(&format!("{}c.{} = {};\n", ctx.get_indent(), field, value));
                }
                for stmt in &cascade_stmts {
                    let stmt_str = self.emit(stmt, ctx);
                    let rewritten = stmt_str.replace("self.", "c.");
                    result.push_str(&rewritten);
                    if self.needs_semicolon(stmt) {
                        result.push_str(";\n");
                    } else {
                        result.push('\n');
                    }
                }
                result.push_str(&format!("{}c\n", ctx.get_indent()));
                ctx.pop_indent();
                result.push_str(&format!("{}}}\n", ctx.get_indent()));

                result
            }

            CodegenNode::VarDecl {
                name,
                type_annotation,
                init,
                is_const,
            } => {
                let kw = if *is_const { "let" } else { "let mut" };
                let type_ann = type_annotation
                    .as_ref()
                    .map(|t| format!(": {}", self.convert_type(t)))
                    .unwrap_or_default();
                if let Some(init_expr) = init {
                    format!(
                        "{}{} {}{} = {}",
                        ctx.get_indent(),
                        kw,
                        name,
                        type_ann,
                        self.emit(init_expr, ctx)
                    )
                } else {
                    format!("{}{} {}{}", ctx.get_indent(), kw, name, type_ann)
                }
            }

            CodegenNode::Assignment { target, value } => {
                format!(
                    "{}{} = {}",
                    ctx.get_indent(),
                    self.emit(target, ctx),
                    self.emit(value, ctx)
                )
            }

            CodegenNode::Return { value } => {
                if let Some(val) = value {
                    format!("{}{}", ctx.get_indent(), self.emit(val, ctx))
                } else {
                    format!("{}return", ctx.get_indent())
                }
            }

            CodegenNode::If {
                condition,
                then_block,
                else_block,
            } => {
                let mut result =
                    format!("{}if {} {{\n", ctx.get_indent(), self.emit(condition, ctx));
                ctx.push_indent();
                for stmt in then_block {
                    result.push_str(&self.emit(stmt, ctx));
                    if self.needs_semicolon(stmt) {
                        result.push_str(";\n");
                    } else {
                        result.push('\n');
                    }
                }
                ctx.pop_indent();

                if let Some(else_stmts) = else_block {
                    result.push_str(&format!("{}}} else {{\n", ctx.get_indent()));
                    ctx.push_indent();
                    for stmt in else_stmts {
                        result.push_str(&self.emit(stmt, ctx));
                        if self.needs_semicolon(stmt) {
                            result.push_str(";\n");
                        } else {
                            result.push('\n');
                        }
                    }
                    ctx.pop_indent();
                }
                result.push_str(&format!("{}}}", ctx.get_indent()));
                result
            }

            CodegenNode::Match { scrutinee, arms } => {
                let mut result = format!(
                    "{}match {} {{\n",
                    ctx.get_indent(),
                    self.emit(scrutinee, ctx)
                );
                ctx.push_indent();
                for arm in arms {
                    result.push_str(&format!(
                        "{}{} => {{\n",
                        ctx.get_indent(),
                        self.emit(&arm.pattern, ctx)
                    ));
                    ctx.push_indent();
                    for stmt in &arm.body {
                        result.push_str(&self.emit(stmt, ctx));
                        if self.needs_semicolon(stmt) {
                            result.push_str(";\n");
                        } else {
                            result.push('\n');
                        }
                    }
                    ctx.pop_indent();
                    result.push_str(&format!("{}}}\n", ctx.get_indent()));
                }
                ctx.pop_indent();
                result.push_str(&format!("{}}}", ctx.get_indent()));
                result
            }

            CodegenNode::While { condition, body } => {
                let mut result = format!(
                    "{}while {} {{\n",
                    ctx.get_indent(),
                    self.emit(condition, ctx)
                );
                ctx.push_indent();
                for stmt in body {
                    result.push_str(&self.emit(stmt, ctx));
                    if self.needs_semicolon(stmt) {
                        result.push_str(";\n");
                    } else {
                        result.push('\n');
                    }
                }
                ctx.pop_indent();
                result.push_str(&format!("{}}}", ctx.get_indent()));
                result
            }

            CodegenNode::For {
                var,
                iterable,
                body,
            } => {
                let mut result = format!(
                    "{}for {} in {} {{\n",
                    ctx.get_indent(),
                    var,
                    self.emit(iterable, ctx)
                );
                ctx.push_indent();
                for stmt in body {
                    result.push_str(&self.emit(stmt, ctx));
                    if self.needs_semicolon(stmt) {
                        result.push_str(";\n");
                    } else {
                        result.push('\n');
                    }
                }
                ctx.pop_indent();
                result.push_str(&format!("{}}}", ctx.get_indent()));
                result
            }

            CodegenNode::Break => format!("{}break", ctx.get_indent()),
            CodegenNode::Continue => format!("{}continue", ctx.get_indent()),

            CodegenNode::ExprStmt(expr) => format!("{}{}", ctx.get_indent(), self.emit(expr, ctx)),
            CodegenNode::Await(expr) => {
                format!("{}.await", self.emit(expr, ctx))
            }

            CodegenNode::Comment { text, is_doc } => {
                if *is_doc {
                    format!("{}/// {}", ctx.get_indent(), text)
                } else {
                    format!("{}// {}", ctx.get_indent(), text)
                }
            }

            CodegenNode::Empty => String::new(),

            CodegenNode::Ident(name) => name.clone(),
            CodegenNode::Literal(lit) => self.emit_literal(lit, ctx),
            CodegenNode::BinaryOp { op, left, right } => self.emit_binary_op(op, left, right, ctx),
            CodegenNode::UnaryOp { op, operand } => self.emit_unary_op(op, operand, ctx),

            CodegenNode::Call { target, args } => {
                let args_str: Vec<String> = args.iter().map(|a| self.emit(a, ctx)).collect();
                format!("{}({})", self.emit(target, ctx), args_str.join(", "))
            }

            CodegenNode::MethodCall {
                object,
                method,
                args,
            } => {
                let args_str: Vec<String> = args.iter().map(|a| self.emit(a, ctx)).collect();
                format!(
                    "{}.{}({})",
                    self.emit(object, ctx),
                    method,
                    args_str.join(", ")
                )
            }

            CodegenNode::FieldAccess { object, field } => {
                format!("{}.{}", self.emit(object, ctx), field)
            }

            CodegenNode::IndexAccess { object, index } => {
                format!("{}[{}]", self.emit(object, ctx), self.emit(index, ctx))
            }

            CodegenNode::SelfRef => "self".to_string(),

            CodegenNode::Array(elements) => {
                let elems: Vec<String> = elements.iter().map(|e| self.emit(e, ctx)).collect();
                format!("vec![{}]", elems.join(", "))
            }

            CodegenNode::Dict(pairs) => {
                let pairs_str: Vec<String> = pairs
                    .iter()
                    .map(|(k, v)| format!("({}, {})", self.emit(k, ctx), self.emit(v, ctx)))
                    .collect();
                format!("HashMap::from([{}])", pairs_str.join(", "))
            }

            CodegenNode::Ternary {
                condition,
                then_expr,
                else_expr,
            } => {
                format!(
                    "if {} {{ {} }} else {{ {} }}",
                    self.emit(condition, ctx),
                    self.emit(then_expr, ctx),
                    self.emit(else_expr, ctx)
                )
            }

            CodegenNode::Lambda { params, body } => {
                let params_str = params
                    .iter()
                    .map(|p| p.name.clone())
                    .collect::<Vec<_>>()
                    .join(", ");
                format!("|{}| {}", params_str, self.emit(body, ctx))
            }

            CodegenNode::Cast { expr, target_type } => {
                format!("{} as {}", self.emit(expr, ctx), target_type)
            }

            CodegenNode::New { class, args } => {
                let args_str: Vec<String> = args.iter().map(|a| self.emit(a, ctx)).collect();
                format!("{}::new({})", class, args_str.join(", "))
            }

            // Frame-specific
            CodegenNode::Transition {
                target_state,
                exit_args: _,
                enter_args: _,
                state_args: _,
                indent,
            } => {
                let ind = format!("{}{}", ctx.get_indent(), " ".repeat(*indent));
                // Use compartment-based transition
                let compartment_type = if let Some(ref sys_name) = ctx.system_name {
                    format!("{}Compartment", sys_name)
                } else {
                    "Compartment".to_string()
                };
                format!(
                    "{}self.__transition({}::new(\"{}\"))",
                    ind, compartment_type, target_state
                )
            }

            CodegenNode::ChangeState {
                target_state,
                state_args: _,
                indent,
            } => {
                let ind = format!("{}{}", ctx.get_indent(), " ".repeat(*indent));
                // Use string-based state change
                let compartment_type = if let Some(ref sys_name) = ctx.system_name {
                    format!("{}Compartment", sys_name)
                } else {
                    "Compartment".to_string()
                };
                format!(
                    "{}self.__compartment = {}::new(\"{}\")",
                    ind, compartment_type, target_state
                )
            }

            CodegenNode::Forward {
                to_parent: _,
                indent,
            } => {
                let ind = format!("{}{}", ctx.get_indent(), " ".repeat(*indent));
                format!("{}return", ind)
            }
            CodegenNode::StackPush { indent } => {
                let ind = format!("{}{}", ctx.get_indent(), " ".repeat(*indent));
                format!("{}self._state_stack_push()", ind)
            }
            CodegenNode::StackPop { indent } => {
                let ind = format!("{}{}", ctx.get_indent(), " ".repeat(*indent));
                format!("{}self._state_stack_pop()", ind)
            }

            CodegenNode::StateContext { state_name } => {
                format!(
                    "self._state_context.get(\"{}\").expect(\"invariant: state context populated on entry to {}\")",
                    state_name, state_name
                )
            }

            CodegenNode::SendEvent { event, args } => {
                let args_str: Vec<String> = args.iter().map(|a| self.emit(a, ctx)).collect();
                if args_str.is_empty() {
                    format!("{}self.{}()", ctx.get_indent(), event)
                } else {
                    format!(
                        "{}self.{}({})",
                        ctx.get_indent(),
                        event,
                        args_str.join(", ")
                    )
                }
            }

            CodegenNode::NativeBlock { code, span: _ } => {
                // Indent each line of the native block to match current context
                let indent = ctx.get_indent();
                code.lines()
                    .map(|line| format!("{}{}", indent, line))
                    .collect::<Vec<_>>()
                    .join("\n")
            }
            CodegenNode::SplicePoint { id } => format!("// SPLICE_POINT: {}", id),
        }
    }

    fn runtime_imports(&self) -> Vec<String> {
        // No map import needed — the runtime uses fully-qualified
        // portable paths (`alloc::collections::BTreeMap`) so it resolves
        // in both hosted and no_std builds, and a user's native prolog
        // may bring its own map type into scope.
        vec![]
    }

    fn emit_module_imports(
        &self,
        imports: &[crate::frame_c::compiler::frame_ast::Import],
    ) -> Vec<String> {
        // RFC-0022 — emit `use crate::x::{Counter, Helper};` when the
        // importer's peek surfaced `@@system` names; fall back to
        // `use crate::x::*;` (Phase 1 lax behavior) when the peek
        // found nothing.
        imports
            .iter()
            .filter_map(|imp| {
                let path = imp.module.as_str();
                if path.is_empty() {
                    return None;
                }
                let stem = match path.rfind('.') {
                    Some(idx) => &path[..idx],
                    None => path,
                };
                let module_path = stem.trim_start_matches("./").replace('/', "::");
                if module_path.is_empty() {
                    return None;
                }
                if imp.symbols.is_empty() {
                    Some(format!("use crate::{}::*;", module_path))
                } else {
                    Some(format!(
                        "use crate::{}::{{{}}};",
                        module_path,
                        imp.symbols.join(", ")
                    ))
                }
            })
            .collect()
    }

    fn class_syntax(&self) -> ClassSyntax {
        ClassSyntax::rust()
    }

    fn target_language(&self) -> TargetLanguage {
        TargetLanguage::Rust
    }
}

impl RustBackend {
    /// Convert type annotation from generic/Python types to Rust types
    fn convert_type(&self, type_str: &str) -> String {
        match type_str {
            "Any" => "Box<dyn core::any::Any>".to_string(),
            "int" => "i64".to_string(),
            "float" => "f64".to_string(),
            "str" | "string" | "String" => "String".to_string(),
            "bool" => "bool".to_string(),
            "None" => "()".to_string(),
            "List" => "Vec<Box<dyn core::any::Any>>".to_string(),
            "Dict" => "alloc::collections::BTreeMap<String, Box<dyn core::any::Any>>".to_string(),
            // Keep Rust types as-is
            t if t.starts_with("Vec<") || t.starts_with("HashMap<") || t.starts_with("&") => {
                t.to_string()
            }
            other => other.to_string(),
        }
    }

    fn emit_params(&self, params: &[Param], include_self: bool) -> String {
        let mut all_params = Vec::new();
        if include_self {
            all_params.push("&mut self".to_string());
        }
        for param in params {
            let type_ann = param
                .type_annotation
                .as_ref()
                .map(|t| self.convert_type(t))
                .unwrap_or_else(|| "()".to_string());
            all_params.push(format!("{}: {}", param.name, type_ann));
        }
        all_params.join(", ")
    }

    fn needs_semicolon(&self, node: &CodegenNode) -> bool {
        match node {
            CodegenNode::If { .. }
            | CodegenNode::While { .. }
            | CodegenNode::For { .. }
            | CodegenNode::Match { .. }
            | CodegenNode::Comment { .. }
            | CodegenNode::Empty => false,
            // NativeBlocks that end with } or ; don't need another
            // semicolon. Empty NativeBlocks (a handler whose body
            // is `{ }`) also don't need one — adding `;` to an
            // otherwise empty block produces a `redundant_semicolons`
            // lint warning.
            CodegenNode::NativeBlock { code, .. } => {
                let trimmed = code.trim();
                if trimmed.is_empty() {
                    false
                } else {
                    !trimmed.ends_with('}') && !trimmed.ends_with(';')
                }
            }
            _ => true,
        }
    }

    /// Emit a single Rust struct-field declaration line:
    ///   `<indent>[pub ]<name>: <type>,\n`
    ///
    /// Rust struct fields don't allow declaration-scope initializers
    /// (`pub value: i64 = 0,` is a parse error) — initializers go in
    /// the struct literal inside the constructor. So `field.initializer`
    /// is intentionally not consumed here.
    ///
    /// `convert_type` runs over the type slot to map Frame-canonical
    /// names (`int`, `Any`, `List`) to Rust forms (`i64`,
    /// `Box<dyn std::any::Any>`, `Vec<...>`). User-supplied
    /// Rust-canonical types (`i64`, `String`) pass through identity.
    fn emit_field(&self, field: &Field, ctx: &mut EmitContext) -> String {
        let vis = if matches!(field.visibility, Visibility::Public) {
            "pub "
        } else {
            ""
        };
        let type_str = match &field.type_annotation {
            Some(t) => self.convert_type(t),
            None => "()".to_string(),
        };
        let comments = field.format_leading_comments(&ctx.get_indent());
        format!(
            "{}{}{}{}: {},\n",
            comments,
            ctx.get_indent(),
            vis,
            field.name,
            type_str
        )
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_emit_self() {
        let backend = RustBackend;
        let mut ctx = EmitContext::new();
        assert_eq!(backend.emit(&CodegenNode::SelfRef, &mut ctx), "self");
    }

    #[test]
    fn test_emit_array() {
        let backend = RustBackend;
        let mut ctx = EmitContext::new();
        let node = CodegenNode::Array(vec![CodegenNode::int(1), CodegenNode::int(2)]);
        assert_eq!(backend.emit(&node, &mut ctx), "vec![1, 2]");
    }
}
