// NOTE: This file is generated by the templates/template.rb script and should not
// be modified manually. See /home/runner/work/herb/herb/templates/rust/src/ast/nodes.rs.erb

use crate::bindings::*;
use crate::convert::{token_from_c, string_from_hb_string};
use crate::errors::*;
use crate::nodes::*;
use crate::union_types::*;
use crate::{Location, Position};
use std::os::raw::c_void;

unsafe fn convert_location(c_location: location_T) -> Location {
  Location::new(
    Position::new(c_location.start.line, c_location.start.column),
    Position::new(c_location.end.line, c_location.end.column),
  )
}

unsafe fn convert_position(c_position: position_T) -> Position {
  Position::new(c_position.line, c_position.column)
}

unsafe fn convert_unexpected_error(error_ptr: *const UNEXPECTED_ERROR_T) -> UnexpectedError {
  let error_ref = &*error_ptr;
  let message = string_from_hb_string(error_ref.base.message);
  let location = convert_location(error_ref.base.location);

  UnexpectedError::new(
    message,
    location,
    get_string_field(error_ref.description),
    get_string_field(error_ref.expected),
    get_string_field(error_ref.found),
  )
}

unsafe fn convert_unexpected_token_error(error_ptr: *const UNEXPECTED_TOKEN_ERROR_T) -> UnexpectedTokenError {
  let error_ref = &*error_ptr;
  let message = string_from_hb_string(error_ref.base.message);
  let location = convert_location(error_ref.base.location);

  UnexpectedTokenError::new(
    message,
    location,
    if error_ref.expected_type == u32::MAX {
      None
    } else {
      Some(string_from_hb_string(crate::ffi::token_type_to_string(error_ref.expected_type)))
    },
    convert_token_field(error_ref.found),
  )
}

unsafe fn convert_missing_opening_tag_error(error_ptr: *const MISSING_OPENING_TAG_ERROR_T) -> MissingOpeningTagError {
  let error_ref = &*error_ptr;
  let message = string_from_hb_string(error_ref.base.message);
  let location = convert_location(error_ref.base.location);

  MissingOpeningTagError::new(
    message,
    location,
    convert_token_field(error_ref.closing_tag),
  )
}

unsafe fn convert_missing_closing_tag_error(error_ptr: *const MISSING_CLOSING_TAG_ERROR_T) -> MissingClosingTagError {
  let error_ref = &*error_ptr;
  let message = string_from_hb_string(error_ref.base.message);
  let location = convert_location(error_ref.base.location);

  MissingClosingTagError::new(
    message,
    location,
    convert_token_field(error_ref.opening_tag),
  )
}

unsafe fn convert_tag_names_mismatch_error(error_ptr: *const TAG_NAMES_MISMATCH_ERROR_T) -> TagNamesMismatchError {
  let error_ref = &*error_ptr;
  let message = string_from_hb_string(error_ref.base.message);
  let location = convert_location(error_ref.base.location);

  TagNamesMismatchError::new(
    message,
    location,
    convert_token_field(error_ref.opening_tag),
    convert_token_field(error_ref.closing_tag),
  )
}

unsafe fn convert_void_element_closing_tag_error(error_ptr: *const VOID_ELEMENT_CLOSING_TAG_ERROR_T) -> VoidElementClosingTagError {
  let error_ref = &*error_ptr;
  let message = string_from_hb_string(error_ref.base.message);
  let location = convert_location(error_ref.base.location);

  VoidElementClosingTagError::new(
    message,
    location,
    convert_token_field(error_ref.tag_name),
    get_string_field(error_ref.expected),
    get_string_field(error_ref.found),
  )
}

unsafe fn convert_unclosed_element_error(error_ptr: *const UNCLOSED_ELEMENT_ERROR_T) -> UnclosedElementError {
  let error_ref = &*error_ptr;
  let message = string_from_hb_string(error_ref.base.message);
  let location = convert_location(error_ref.base.location);

  UnclosedElementError::new(
    message,
    location,
    convert_token_field(error_ref.opening_tag),
  )
}

unsafe fn convert_ruby_parse_error(error_ptr: *const RUBY_PARSE_ERROR_T) -> RubyParseError {
  let error_ref = &*error_ptr;
  let message = string_from_hb_string(error_ref.base.message);
  let location = convert_location(error_ref.base.location);

  RubyParseError::new(
    message,
    location,
    get_string_field(error_ref.error_message),
    get_string_field(error_ref.diagnostic_id),
    get_string_field(error_ref.level),
  )
}

unsafe fn convert_erb_control_flow_scope_error(error_ptr: *const ERB_CONTROL_FLOW_SCOPE_ERROR_T) -> ERBControlFlowScopeError {
  let error_ref = &*error_ptr;
  let message = string_from_hb_string(error_ref.base.message);
  let location = convert_location(error_ref.base.location);

  ERBControlFlowScopeError::new(
    message,
    location,
    get_string_field(error_ref.keyword),
  )
}

unsafe fn convert_missing_erb_end_tag_error(error_ptr: *const MISSING_ERB_END_TAG_ERROR_T) -> MissingERBEndTagError {
  let error_ref = &*error_ptr;
  let message = string_from_hb_string(error_ref.base.message);
  let location = convert_location(error_ref.base.location);

  MissingERBEndTagError::new(
    message,
    location,
    get_string_field(error_ref.keyword),
  )
}

unsafe fn convert_erb_multiple_blocks_in_tag_error(error_ptr: *const ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR_T) -> ERBMultipleBlocksInTagError {
  let error_ref = &*error_ptr;
  let message = string_from_hb_string(error_ref.base.message);
  let location = convert_location(error_ref.base.location);

  ERBMultipleBlocksInTagError::new(
    message,
    location,
  )
}

unsafe fn convert_erb_case_with_conditions_error(error_ptr: *const ERB_CASE_WITH_CONDITIONS_ERROR_T) -> ERBCaseWithConditionsError {
  let error_ref = &*error_ptr;
  let message = string_from_hb_string(error_ref.base.message);
  let location = convert_location(error_ref.base.location);

  ERBCaseWithConditionsError::new(
    message,
    location,
  )
}

unsafe fn convert_conditional_element_multiple_tags_error(error_ptr: *const CONDITIONAL_ELEMENT_MULTIPLE_TAGS_ERROR_T) -> ConditionalElementMultipleTagsError {
  let error_ref = &*error_ptr;
  let message = string_from_hb_string(error_ref.base.message);
  let location = convert_location(error_ref.base.location);

  ConditionalElementMultipleTagsError::new(
    message,
    location,
    error_ref.line,
    error_ref.column,
  )
}

unsafe fn convert_conditional_element_condition_mismatch_error(error_ptr: *const CONDITIONAL_ELEMENT_CONDITION_MISMATCH_ERROR_T) -> ConditionalElementConditionMismatchError {
  let error_ref = &*error_ptr;
  let message = string_from_hb_string(error_ref.base.message);
  let location = convert_location(error_ref.base.location);

  ConditionalElementConditionMismatchError::new(
    message,
    location,
    get_string_field(error_ref.tag_name),
    get_string_field(error_ref.open_condition),
    error_ref.open_line,
    error_ref.open_column,
    get_string_field(error_ref.close_condition),
    error_ref.close_line,
    error_ref.close_column,
  )
}

unsafe fn convert_invalid_comment_closing_tag_error(error_ptr: *const INVALID_COMMENT_CLOSING_TAG_ERROR_T) -> InvalidCommentClosingTagError {
  let error_ref = &*error_ptr;
  let message = string_from_hb_string(error_ref.base.message);
  let location = convert_location(error_ref.base.location);

  InvalidCommentClosingTagError::new(
    message,
    location,
    convert_token_field(error_ref.closing_tag),
  )
}

unsafe fn convert_omitted_closing_tag_error(error_ptr: *const OMITTED_CLOSING_TAG_ERROR_T) -> OmittedClosingTagError {
  let error_ref = &*error_ptr;
  let message = string_from_hb_string(error_ref.base.message);
  let location = convert_location(error_ref.base.location);

  OmittedClosingTagError::new(
    message,
    location,
    convert_token_field(error_ref.opening_tag),
    Some(convert_position(error_ref.insertion_point)),
  )
}

unsafe fn convert_unclosed_open_tag_error(error_ptr: *const UNCLOSED_OPEN_TAG_ERROR_T) -> UnclosedOpenTagError {
  let error_ref = &*error_ptr;
  let message = string_from_hb_string(error_ref.base.message);
  let location = convert_location(error_ref.base.location);

  UnclosedOpenTagError::new(
    message,
    location,
    convert_token_field(error_ref.tag_name),
  )
}

unsafe fn convert_unclosed_close_tag_error(error_ptr: *const UNCLOSED_CLOSE_TAG_ERROR_T) -> UnclosedCloseTagError {
  let error_ref = &*error_ptr;
  let message = string_from_hb_string(error_ref.base.message);
  let location = convert_location(error_ref.base.location);

  UnclosedCloseTagError::new(
    message,
    location,
    convert_token_field(error_ref.tag_name),
  )
}

unsafe fn convert_unclosed_quote_error(error_ptr: *const UNCLOSED_QUOTE_ERROR_T) -> UnclosedQuoteError {
  let error_ref = &*error_ptr;
  let message = string_from_hb_string(error_ref.base.message);
  let location = convert_location(error_ref.base.location);

  UnclosedQuoteError::new(
    message,
    location,
    convert_token_field(error_ref.opening_quote),
  )
}

unsafe fn convert_missing_attribute_value_error(error_ptr: *const MISSING_ATTRIBUTE_VALUE_ERROR_T) -> MissingAttributeValueError {
  let error_ref = &*error_ptr;
  let message = string_from_hb_string(error_ref.base.message);
  let location = convert_location(error_ref.base.location);

  MissingAttributeValueError::new(
    message,
    location,
    get_string_field(error_ref.attribute_name),
  )
}

unsafe fn convert_unclosed_erb_tag_error(error_ptr: *const UNCLOSED_ERB_TAG_ERROR_T) -> UnclosedERBTagError {
  let error_ref = &*error_ptr;
  let message = string_from_hb_string(error_ref.base.message);
  let location = convert_location(error_ref.base.location);

  UnclosedERBTagError::new(
    message,
    location,
    convert_token_field(error_ref.opening_tag),
  )
}

unsafe fn convert_stray_erb_closing_tag_error(error_ptr: *const STRAY_ERB_CLOSING_TAG_ERROR_T) -> StrayERBClosingTagError {
  let error_ref = &*error_ptr;
  let message = string_from_hb_string(error_ref.base.message);
  let location = convert_location(error_ref.base.location);

  StrayERBClosingTagError::new(
    message,
    location,
  )
}

unsafe fn convert_nested_erb_tag_error(error_ptr: *const NESTED_ERB_TAG_ERROR_T) -> NestedERBTagError {
  let error_ref = &*error_ptr;
  let message = string_from_hb_string(error_ref.base.message);
  let location = convert_location(error_ref.base.location);

  NestedERBTagError::new(
    message,
    location,
    convert_token_field(error_ref.opening_tag),
    error_ref.nested_tag_line,
    error_ref.nested_tag_column,
  )
}

unsafe fn convert_render_ambiguous_locals_error(error_ptr: *const RENDER_AMBIGUOUS_LOCALS_ERROR_T) -> RenderAmbiguousLocalsError {
  let error_ref = &*error_ptr;
  let message = string_from_hb_string(error_ref.base.message);
  let location = convert_location(error_ref.base.location);

  RenderAmbiguousLocalsError::new(
    message,
    location,
    get_string_field(error_ref.partial),
  )
}

unsafe fn convert_render_missing_locals_error(error_ptr: *const RENDER_MISSING_LOCALS_ERROR_T) -> RenderMissingLocalsError {
  let error_ref = &*error_ptr;
  let message = string_from_hb_string(error_ref.base.message);
  let location = convert_location(error_ref.base.location);

  RenderMissingLocalsError::new(
    message,
    location,
    get_string_field(error_ref.partial),
    get_string_field(error_ref.keywords),
  )
}

unsafe fn convert_render_no_arguments_error(error_ptr: *const RENDER_NO_ARGUMENTS_ERROR_T) -> RenderNoArgumentsError {
  let error_ref = &*error_ptr;
  let message = string_from_hb_string(error_ref.base.message);
  let location = convert_location(error_ref.base.location);

  RenderNoArgumentsError::new(
    message,
    location,
  )
}

unsafe fn convert_render_conflicting_partial_error(error_ptr: *const RENDER_CONFLICTING_PARTIAL_ERROR_T) -> RenderConflictingPartialError {
  let error_ref = &*error_ptr;
  let message = string_from_hb_string(error_ref.base.message);
  let location = convert_location(error_ref.base.location);

  RenderConflictingPartialError::new(
    message,
    location,
    get_string_field(error_ref.positional_partial),
    get_string_field(error_ref.keyword_partial),
  )
}

unsafe fn convert_render_invalid_as_option_error(error_ptr: *const RENDER_INVALID_AS_OPTION_ERROR_T) -> RenderInvalidAsOptionError {
  let error_ref = &*error_ptr;
  let message = string_from_hb_string(error_ref.base.message);
  let location = convert_location(error_ref.base.location);

  RenderInvalidAsOptionError::new(
    message,
    location,
    get_string_field(error_ref.as_value),
  )
}

unsafe fn convert_render_object_and_collection_error(error_ptr: *const RENDER_OBJECT_AND_COLLECTION_ERROR_T) -> RenderObjectAndCollectionError {
  let error_ref = &*error_ptr;
  let message = string_from_hb_string(error_ref.base.message);
  let location = convert_location(error_ref.base.location);

  RenderObjectAndCollectionError::new(
    message,
    location,
  )
}

unsafe fn convert_render_layout_without_block_error(error_ptr: *const RENDER_LAYOUT_WITHOUT_BLOCK_ERROR_T) -> RenderLayoutWithoutBlockError {
  let error_ref = &*error_ptr;
  let message = string_from_hb_string(error_ref.base.message);
  let location = convert_location(error_ref.base.location);

  RenderLayoutWithoutBlockError::new(
    message,
    location,
    get_string_field(error_ref.layout),
  )
}

unsafe fn convert_strict_locals_positional_argument_error(error_ptr: *const STRICT_LOCALS_POSITIONAL_ARGUMENT_ERROR_T) -> StrictLocalsPositionalArgumentError {
  let error_ref = &*error_ptr;
  let message = string_from_hb_string(error_ref.base.message);
  let location = convert_location(error_ref.base.location);

  StrictLocalsPositionalArgumentError::new(
    message,
    location,
    get_string_field(error_ref.name),
  )
}

unsafe fn convert_strict_locals_block_argument_error(error_ptr: *const STRICT_LOCALS_BLOCK_ARGUMENT_ERROR_T) -> StrictLocalsBlockArgumentError {
  let error_ref = &*error_ptr;
  let message = string_from_hb_string(error_ref.base.message);
  let location = convert_location(error_ref.base.location);

  StrictLocalsBlockArgumentError::new(
    message,
    location,
    get_string_field(error_ref.name),
  )
}

unsafe fn convert_strict_locals_splat_argument_error(error_ptr: *const STRICT_LOCALS_SPLAT_ARGUMENT_ERROR_T) -> StrictLocalsSplatArgumentError {
  let error_ref = &*error_ptr;
  let message = string_from_hb_string(error_ref.base.message);
  let location = convert_location(error_ref.base.location);

  StrictLocalsSplatArgumentError::new(
    message,
    location,
    get_string_field(error_ref.name),
  )
}

unsafe fn convert_strict_locals_missing_parenthesis_error(error_ptr: *const STRICT_LOCALS_MISSING_PARENTHESIS_ERROR_T) -> StrictLocalsMissingParenthesisError {
  let error_ref = &*error_ptr;
  let message = string_from_hb_string(error_ref.base.message);
  let location = convert_location(error_ref.base.location);

  StrictLocalsMissingParenthesisError::new(
    message,
    location,
    get_string_field(error_ref.rest),
  )
}

unsafe fn convert_strict_locals_duplicate_declaration_error(error_ptr: *const STRICT_LOCALS_DUPLICATE_DECLARATION_ERROR_T) -> StrictLocalsDuplicateDeclarationError {
  let error_ref = &*error_ptr;
  let message = string_from_hb_string(error_ref.base.message);
  let location = convert_location(error_ref.base.location);

  StrictLocalsDuplicateDeclarationError::new(
    message,
    location,
  )
}

unsafe fn convert_void_element_content_error(error_ptr: *const VOID_ELEMENT_CONTENT_ERROR_T) -> VoidElementContentError {
  let error_ref = &*error_ptr;
  let message = string_from_hb_string(error_ref.base.message);
  let location = convert_location(error_ref.base.location);

  VoidElementContentError::new(
    message,
    location,
    convert_token_field(error_ref.tag_name),
    get_string_field(error_ref.helper_name),
    get_string_field(error_ref.content_type),
  )
}

unsafe fn convert_dot_notation_casing_error(error_ptr: *const DOT_NOTATION_CASING_ERROR_T) -> DotNotationCasingError {
  let error_ref = &*error_ptr;
  let message = string_from_hb_string(error_ref.base.message);
  let location = convert_location(error_ref.base.location);

  DotNotationCasingError::new(
    message,
    location,
    convert_token_field(error_ref.segment),
  )
}


unsafe fn convert_errors(errors_array: *mut hb_array_T) -> Vec<AnyError> {
  if errors_array.is_null() {
    return Vec::new();
  }

  let count = hb_array_size(errors_array);
  let mut errors = Vec::with_capacity(count);

  for index in 0..count {
    let error_base_ptr = hb_array_get(errors_array, index) as *const ERROR_T;
    if error_base_ptr.is_null() {
      continue;
    }

    let error_base = &*error_base_ptr;

    let error = match error_base.type_ {
        UNEXPECTED_ERROR => {
          let error_ptr = error_base_ptr as *const UNEXPECTED_ERROR_T;
          AnyError::UnexpectedError(convert_unexpected_error(error_ptr))
        }
        UNEXPECTED_TOKEN_ERROR => {
          let error_ptr = error_base_ptr as *const UNEXPECTED_TOKEN_ERROR_T;
          AnyError::UnexpectedTokenError(convert_unexpected_token_error(error_ptr))
        }
        MISSING_OPENING_TAG_ERROR => {
          let error_ptr = error_base_ptr as *const MISSING_OPENING_TAG_ERROR_T;
          AnyError::MissingOpeningTagError(convert_missing_opening_tag_error(error_ptr))
        }
        MISSING_CLOSING_TAG_ERROR => {
          let error_ptr = error_base_ptr as *const MISSING_CLOSING_TAG_ERROR_T;
          AnyError::MissingClosingTagError(convert_missing_closing_tag_error(error_ptr))
        }
        TAG_NAMES_MISMATCH_ERROR => {
          let error_ptr = error_base_ptr as *const TAG_NAMES_MISMATCH_ERROR_T;
          AnyError::TagNamesMismatchError(convert_tag_names_mismatch_error(error_ptr))
        }
        VOID_ELEMENT_CLOSING_TAG_ERROR => {
          let error_ptr = error_base_ptr as *const VOID_ELEMENT_CLOSING_TAG_ERROR_T;
          AnyError::VoidElementClosingTagError(convert_void_element_closing_tag_error(error_ptr))
        }
        UNCLOSED_ELEMENT_ERROR => {
          let error_ptr = error_base_ptr as *const UNCLOSED_ELEMENT_ERROR_T;
          AnyError::UnclosedElementError(convert_unclosed_element_error(error_ptr))
        }
        RUBY_PARSE_ERROR => {
          let error_ptr = error_base_ptr as *const RUBY_PARSE_ERROR_T;
          AnyError::RubyParseError(convert_ruby_parse_error(error_ptr))
        }
        ERB_CONTROL_FLOW_SCOPE_ERROR => {
          let error_ptr = error_base_ptr as *const ERB_CONTROL_FLOW_SCOPE_ERROR_T;
          AnyError::ERBControlFlowScopeError(convert_erb_control_flow_scope_error(error_ptr))
        }
        MISSING_ERB_END_TAG_ERROR => {
          let error_ptr = error_base_ptr as *const MISSING_ERB_END_TAG_ERROR_T;
          AnyError::MissingERBEndTagError(convert_missing_erb_end_tag_error(error_ptr))
        }
        ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR => {
          let error_ptr = error_base_ptr as *const ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR_T;
          AnyError::ERBMultipleBlocksInTagError(convert_erb_multiple_blocks_in_tag_error(error_ptr))
        }
        ERB_CASE_WITH_CONDITIONS_ERROR => {
          let error_ptr = error_base_ptr as *const ERB_CASE_WITH_CONDITIONS_ERROR_T;
          AnyError::ERBCaseWithConditionsError(convert_erb_case_with_conditions_error(error_ptr))
        }
        CONDITIONAL_ELEMENT_MULTIPLE_TAGS_ERROR => {
          let error_ptr = error_base_ptr as *const CONDITIONAL_ELEMENT_MULTIPLE_TAGS_ERROR_T;
          AnyError::ConditionalElementMultipleTagsError(convert_conditional_element_multiple_tags_error(error_ptr))
        }
        CONDITIONAL_ELEMENT_CONDITION_MISMATCH_ERROR => {
          let error_ptr = error_base_ptr as *const CONDITIONAL_ELEMENT_CONDITION_MISMATCH_ERROR_T;
          AnyError::ConditionalElementConditionMismatchError(convert_conditional_element_condition_mismatch_error(error_ptr))
        }
        INVALID_COMMENT_CLOSING_TAG_ERROR => {
          let error_ptr = error_base_ptr as *const INVALID_COMMENT_CLOSING_TAG_ERROR_T;
          AnyError::InvalidCommentClosingTagError(convert_invalid_comment_closing_tag_error(error_ptr))
        }
        OMITTED_CLOSING_TAG_ERROR => {
          let error_ptr = error_base_ptr as *const OMITTED_CLOSING_TAG_ERROR_T;
          AnyError::OmittedClosingTagError(convert_omitted_closing_tag_error(error_ptr))
        }
        UNCLOSED_OPEN_TAG_ERROR => {
          let error_ptr = error_base_ptr as *const UNCLOSED_OPEN_TAG_ERROR_T;
          AnyError::UnclosedOpenTagError(convert_unclosed_open_tag_error(error_ptr))
        }
        UNCLOSED_CLOSE_TAG_ERROR => {
          let error_ptr = error_base_ptr as *const UNCLOSED_CLOSE_TAG_ERROR_T;
          AnyError::UnclosedCloseTagError(convert_unclosed_close_tag_error(error_ptr))
        }
        UNCLOSED_QUOTE_ERROR => {
          let error_ptr = error_base_ptr as *const UNCLOSED_QUOTE_ERROR_T;
          AnyError::UnclosedQuoteError(convert_unclosed_quote_error(error_ptr))
        }
        MISSING_ATTRIBUTE_VALUE_ERROR => {
          let error_ptr = error_base_ptr as *const MISSING_ATTRIBUTE_VALUE_ERROR_T;
          AnyError::MissingAttributeValueError(convert_missing_attribute_value_error(error_ptr))
        }
        UNCLOSED_ERB_TAG_ERROR => {
          let error_ptr = error_base_ptr as *const UNCLOSED_ERB_TAG_ERROR_T;
          AnyError::UnclosedERBTagError(convert_unclosed_erb_tag_error(error_ptr))
        }
        STRAY_ERB_CLOSING_TAG_ERROR => {
          let error_ptr = error_base_ptr as *const STRAY_ERB_CLOSING_TAG_ERROR_T;
          AnyError::StrayERBClosingTagError(convert_stray_erb_closing_tag_error(error_ptr))
        }
        NESTED_ERB_TAG_ERROR => {
          let error_ptr = error_base_ptr as *const NESTED_ERB_TAG_ERROR_T;
          AnyError::NestedERBTagError(convert_nested_erb_tag_error(error_ptr))
        }
        RENDER_AMBIGUOUS_LOCALS_ERROR => {
          let error_ptr = error_base_ptr as *const RENDER_AMBIGUOUS_LOCALS_ERROR_T;
          AnyError::RenderAmbiguousLocalsError(convert_render_ambiguous_locals_error(error_ptr))
        }
        RENDER_MISSING_LOCALS_ERROR => {
          let error_ptr = error_base_ptr as *const RENDER_MISSING_LOCALS_ERROR_T;
          AnyError::RenderMissingLocalsError(convert_render_missing_locals_error(error_ptr))
        }
        RENDER_NO_ARGUMENTS_ERROR => {
          let error_ptr = error_base_ptr as *const RENDER_NO_ARGUMENTS_ERROR_T;
          AnyError::RenderNoArgumentsError(convert_render_no_arguments_error(error_ptr))
        }
        RENDER_CONFLICTING_PARTIAL_ERROR => {
          let error_ptr = error_base_ptr as *const RENDER_CONFLICTING_PARTIAL_ERROR_T;
          AnyError::RenderConflictingPartialError(convert_render_conflicting_partial_error(error_ptr))
        }
        RENDER_INVALID_AS_OPTION_ERROR => {
          let error_ptr = error_base_ptr as *const RENDER_INVALID_AS_OPTION_ERROR_T;
          AnyError::RenderInvalidAsOptionError(convert_render_invalid_as_option_error(error_ptr))
        }
        RENDER_OBJECT_AND_COLLECTION_ERROR => {
          let error_ptr = error_base_ptr as *const RENDER_OBJECT_AND_COLLECTION_ERROR_T;
          AnyError::RenderObjectAndCollectionError(convert_render_object_and_collection_error(error_ptr))
        }
        RENDER_LAYOUT_WITHOUT_BLOCK_ERROR => {
          let error_ptr = error_base_ptr as *const RENDER_LAYOUT_WITHOUT_BLOCK_ERROR_T;
          AnyError::RenderLayoutWithoutBlockError(convert_render_layout_without_block_error(error_ptr))
        }
        STRICT_LOCALS_POSITIONAL_ARGUMENT_ERROR => {
          let error_ptr = error_base_ptr as *const STRICT_LOCALS_POSITIONAL_ARGUMENT_ERROR_T;
          AnyError::StrictLocalsPositionalArgumentError(convert_strict_locals_positional_argument_error(error_ptr))
        }
        STRICT_LOCALS_BLOCK_ARGUMENT_ERROR => {
          let error_ptr = error_base_ptr as *const STRICT_LOCALS_BLOCK_ARGUMENT_ERROR_T;
          AnyError::StrictLocalsBlockArgumentError(convert_strict_locals_block_argument_error(error_ptr))
        }
        STRICT_LOCALS_SPLAT_ARGUMENT_ERROR => {
          let error_ptr = error_base_ptr as *const STRICT_LOCALS_SPLAT_ARGUMENT_ERROR_T;
          AnyError::StrictLocalsSplatArgumentError(convert_strict_locals_splat_argument_error(error_ptr))
        }
        STRICT_LOCALS_MISSING_PARENTHESIS_ERROR => {
          let error_ptr = error_base_ptr as *const STRICT_LOCALS_MISSING_PARENTHESIS_ERROR_T;
          AnyError::StrictLocalsMissingParenthesisError(convert_strict_locals_missing_parenthesis_error(error_ptr))
        }
        STRICT_LOCALS_DUPLICATE_DECLARATION_ERROR => {
          let error_ptr = error_base_ptr as *const STRICT_LOCALS_DUPLICATE_DECLARATION_ERROR_T;
          AnyError::StrictLocalsDuplicateDeclarationError(convert_strict_locals_duplicate_declaration_error(error_ptr))
        }
        VOID_ELEMENT_CONTENT_ERROR => {
          let error_ptr = error_base_ptr as *const VOID_ELEMENT_CONTENT_ERROR_T;
          AnyError::VoidElementContentError(convert_void_element_content_error(error_ptr))
        }
        DOT_NOTATION_CASING_ERROR => {
          let error_ptr = error_base_ptr as *const DOT_NOTATION_CASING_ERROR_T;
          AnyError::DotNotationCasingError(convert_dot_notation_casing_error(error_ptr))
        }
        _ => continue,
    };

    errors.push(error);
  }

  errors
}

unsafe fn get_string_field(hb_string: hb_string_T) -> String {
  string_from_hb_string(hb_string)
}

unsafe fn convert_element_source(hb_string: hb_string_T) -> String {
  if hb_string.data.is_null() {
    String::new()
  } else {
    let slice = std::slice::from_raw_parts(hb_string.data as *const u8, hb_string.length as usize);
    String::from_utf8_lossy(slice).into_owned()
  }
}

unsafe fn convert_token_field(token_ptr: *mut token_T) -> Option<crate::Token> {
  if token_ptr.is_null() {
    None
  } else {
    Some(token_from_c(token_ptr))
  }
}

unsafe fn convert_location_field(location_ptr: *const location_T) -> Option<Location> {
  if location_ptr.is_null() {
    None
  } else {
    Some(convert_location(*location_ptr))
  }
}

unsafe fn convert_children(children_array: *mut hb_array_T) -> Vec<AnyNode> {
  if children_array.is_null() {
    return Vec::new();
  }

  let count = hb_array_size(children_array);
  let mut children = Vec::with_capacity(count);

  for index in 0..count {
    let child_ptr = hb_array_get(children_array, index);
    if !child_ptr.is_null() {
      if let Some(node) = convert_node(child_ptr as *const c_void) {
        children.push(node);
      }
    }
  }

  children
}

#[allow(dead_code)]
unsafe fn convert_node_field(node_pointer: *mut c_void) -> Option<Box<AnyNode>> {
  if node_pointer.is_null() {
    None
  } else {
    convert_node(node_pointer).map(Box::new)
  }
}

macro_rules! convert_specific_node_field {
  ($node_pointer:expr, $expected_type:expr, $convert_fn:ident, $node_type:ty) => {
    if $node_pointer.is_null() {
      None
    } else {
      let ast_base_ptr = $node_pointer as *const AST_NODE_T;
      let ast_base_ref = &*ast_base_ptr;
      let node_type = ast_base_ref.type_;

      if node_type != $expected_type {
        eprintln!("Warning: Expected node type {} but got {}", $expected_type, node_type);
        None
      } else {
        $convert_fn($node_pointer as *const c_void).map(Box::new)
      }
    }
  };
}

unsafe fn convert_node(node_pointer: *const c_void) -> Option<AnyNode> {
  if node_pointer.is_null() {
    return None;
  }

  let ast_base_ptr = node_pointer as *const AST_NODE_T;
  let ast_base_ref = &*ast_base_ptr;
  let node_type = ast_base_ref.type_;

  match node_type {
    AST_DOCUMENT_NODE => convert_document_node(node_pointer).map(|n| AnyNode::DocumentNode(Box::new(n))),
    AST_LITERAL_NODE => convert_literal_node(node_pointer).map(|n| AnyNode::LiteralNode(Box::new(n))),
    AST_HTML_OPEN_TAG_NODE => convert_html_open_tag_node(node_pointer).map(|n| AnyNode::HTMLOpenTagNode(Box::new(n))),
    AST_HTML_CONDITIONAL_OPEN_TAG_NODE => convert_html_conditional_open_tag_node(node_pointer).map(|n| AnyNode::HTMLConditionalOpenTagNode(Box::new(n))),
    AST_HTML_CLOSE_TAG_NODE => convert_html_close_tag_node(node_pointer).map(|n| AnyNode::HTMLCloseTagNode(Box::new(n))),
    AST_HTML_OMITTED_CLOSE_TAG_NODE => convert_html_omitted_close_tag_node(node_pointer).map(|n| AnyNode::HTMLOmittedCloseTagNode(Box::new(n))),
    AST_HTML_VIRTUAL_CLOSE_TAG_NODE => convert_html_virtual_close_tag_node(node_pointer).map(|n| AnyNode::HTMLVirtualCloseTagNode(Box::new(n))),
    AST_HTML_ELEMENT_NODE => convert_html_element_node(node_pointer).map(|n| AnyNode::HTMLElementNode(Box::new(n))),
    AST_HTML_CONDITIONAL_ELEMENT_NODE => convert_html_conditional_element_node(node_pointer).map(|n| AnyNode::HTMLConditionalElementNode(Box::new(n))),
    AST_HTML_ATTRIBUTE_VALUE_NODE => convert_html_attribute_value_node(node_pointer).map(|n| AnyNode::HTMLAttributeValueNode(Box::new(n))),
    AST_HTML_ATTRIBUTE_NAME_NODE => convert_html_attribute_name_node(node_pointer).map(|n| AnyNode::HTMLAttributeNameNode(Box::new(n))),
    AST_HTML_ATTRIBUTE_NODE => convert_html_attribute_node(node_pointer).map(|n| AnyNode::HTMLAttributeNode(Box::new(n))),
    AST_RUBY_LITERAL_NODE => convert_ruby_literal_node(node_pointer).map(|n| AnyNode::RubyLiteralNode(Box::new(n))),
    AST_RUBY_HTML_ATTRIBUTES_SPLAT_NODE => convert_ruby_html_attributes_splat_node(node_pointer).map(|n| AnyNode::RubyHTMLAttributesSplatNode(Box::new(n))),
    AST_ERB_OPEN_TAG_NODE => convert_erb_open_tag_node(node_pointer).map(|n| AnyNode::ERBOpenTagNode(Box::new(n))),
    AST_HTML_TEXT_NODE => convert_html_text_node(node_pointer).map(|n| AnyNode::HTMLTextNode(Box::new(n))),
    AST_HTML_COMMENT_NODE => convert_html_comment_node(node_pointer).map(|n| AnyNode::HTMLCommentNode(Box::new(n))),
    AST_HTML_DOCTYPE_NODE => convert_html_doctype_node(node_pointer).map(|n| AnyNode::HTMLDoctypeNode(Box::new(n))),
    AST_XML_DECLARATION_NODE => convert_xml_declaration_node(node_pointer).map(|n| AnyNode::XMLDeclarationNode(Box::new(n))),
    AST_CDATA_NODE => convert_cdata_node(node_pointer).map(|n| AnyNode::CDATANode(Box::new(n))),
    AST_WHITESPACE_NODE => convert_whitespace_node(node_pointer).map(|n| AnyNode::WhitespaceNode(Box::new(n))),
    AST_ERB_CONTENT_NODE => convert_erb_content_node(node_pointer).map(|n| AnyNode::ERBContentNode(Box::new(n))),
    AST_ERB_END_NODE => convert_erb_end_node(node_pointer).map(|n| AnyNode::ERBEndNode(Box::new(n))),
    AST_ERB_ELSE_NODE => convert_erb_else_node(node_pointer).map(|n| AnyNode::ERBElseNode(Box::new(n))),
    AST_ERB_IF_NODE => convert_erb_if_node(node_pointer).map(|n| AnyNode::ERBIfNode(Box::new(n))),
    AST_ERB_BLOCK_NODE => convert_erb_block_node(node_pointer).map(|n| AnyNode::ERBBlockNode(Box::new(n))),
    AST_ERB_WHEN_NODE => convert_erb_when_node(node_pointer).map(|n| AnyNode::ERBWhenNode(Box::new(n))),
    AST_ERB_CASE_NODE => convert_erb_case_node(node_pointer).map(|n| AnyNode::ERBCaseNode(Box::new(n))),
    AST_ERB_CASE_MATCH_NODE => convert_erb_case_match_node(node_pointer).map(|n| AnyNode::ERBCaseMatchNode(Box::new(n))),
    AST_ERB_WHILE_NODE => convert_erb_while_node(node_pointer).map(|n| AnyNode::ERBWhileNode(Box::new(n))),
    AST_ERB_UNTIL_NODE => convert_erb_until_node(node_pointer).map(|n| AnyNode::ERBUntilNode(Box::new(n))),
    AST_ERB_FOR_NODE => convert_erb_for_node(node_pointer).map(|n| AnyNode::ERBForNode(Box::new(n))),
    AST_ERB_RESCUE_NODE => convert_erb_rescue_node(node_pointer).map(|n| AnyNode::ERBRescueNode(Box::new(n))),
    AST_ERB_ENSURE_NODE => convert_erb_ensure_node(node_pointer).map(|n| AnyNode::ERBEnsureNode(Box::new(n))),
    AST_ERB_BEGIN_NODE => convert_erb_begin_node(node_pointer).map(|n| AnyNode::ERBBeginNode(Box::new(n))),
    AST_ERB_UNLESS_NODE => convert_erb_unless_node(node_pointer).map(|n| AnyNode::ERBUnlessNode(Box::new(n))),
    AST_RUBY_RENDER_LOCAL_NODE => convert_ruby_render_local_node(node_pointer).map(|n| AnyNode::RubyRenderLocalNode(Box::new(n))),
    AST_ERB_RENDER_NODE => convert_erb_render_node(node_pointer).map(|n| AnyNode::ERBRenderNode(Box::new(n))),
    AST_RUBY_STRICT_LOCAL_NODE => convert_ruby_strict_local_node(node_pointer).map(|n| AnyNode::RubyStrictLocalNode(Box::new(n))),
    AST_ERB_STRICT_LOCALS_NODE => convert_erb_strict_locals_node(node_pointer).map(|n| AnyNode::ERBStrictLocalsNode(Box::new(n))),
    AST_ERB_YIELD_NODE => convert_erb_yield_node(node_pointer).map(|n| AnyNode::ERBYieldNode(Box::new(n))),
    AST_ERB_IN_NODE => convert_erb_in_node(node_pointer).map(|n| AnyNode::ERBInNode(Box::new(n))),
    _ => {
      eprintln!("Warning: Unknown node type {}", node_type);
      None
    }
  }
}

  /// Converts a C document node pointer to a Rust DocumentNode.
  ///
  /// # Safety
  ///
  /// The caller must ensure that `node_pointer` is a valid pointer to a C document node
  /// structure with properly initialized fields.
  pub unsafe fn convert_document_node(node_pointer: *const c_void) -> Option<DocumentNode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_DOCUMENT_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(DocumentNode {
      node_type: "DocumentNode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      children: convert_children((*c_node_pointer).children),
      // skip internal field: prism_context
      prism_node: if (*c_node_pointer).prism_node.node.is_null() || (*c_node_pointer).prism_node.parser.is_null() {
        None
      } else {
        let mut pm_buffer: crate::bindings::pm_buffer_t = std::mem::zeroed();
        crate::bindings::pm_prettyprint(&mut pm_buffer, (*c_node_pointer).prism_node.parser, (*c_node_pointer).prism_node.node);

        if pm_buffer.length > 0 && !pm_buffer.value.is_null() {
          let s = std::str::from_utf8(std::slice::from_raw_parts(pm_buffer.value as *const u8, pm_buffer.length)).unwrap_or("").to_string();
          crate::bindings::pm_buffer_free(&mut pm_buffer);
          Some(s)
        } else {
          crate::bindings::pm_buffer_free(&mut pm_buffer);
          None
        }
      },
    })
  }

  unsafe fn convert_literal_node(node_pointer: *const c_void) -> Option<LiteralNode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_LITERAL_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(LiteralNode {
      node_type: "LiteralNode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      content: get_string_field((*c_node_pointer).content),
    })
  }

  unsafe fn convert_html_open_tag_node(node_pointer: *const c_void) -> Option<HTMLOpenTagNode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_HTML_OPEN_TAG_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(HTMLOpenTagNode {
      node_type: "HTMLOpenTagNode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      tag_opening: convert_token_field((*c_node_pointer).tag_opening),
      tag_name: convert_token_field((*c_node_pointer).tag_name),
      tag_closing: convert_token_field((*c_node_pointer).tag_closing),
      children: convert_children((*c_node_pointer).children),
      is_void: (*c_node_pointer).is_void,
    })
  }

  unsafe fn convert_html_conditional_open_tag_node(node_pointer: *const c_void) -> Option<HTMLConditionalOpenTagNode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_HTML_CONDITIONAL_OPEN_TAG_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(HTMLConditionalOpenTagNode {
      node_type: "HTMLConditionalOpenTagNode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      conditional: convert_erb_if_node_or_erb_unless_node_field((*c_node_pointer).conditional as *mut c_void),
      tag_name: convert_token_field((*c_node_pointer).tag_name),
      is_void: (*c_node_pointer).is_void,
    })
  }

  unsafe fn convert_html_close_tag_node(node_pointer: *const c_void) -> Option<HTMLCloseTagNode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_HTML_CLOSE_TAG_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(HTMLCloseTagNode {
      node_type: "HTMLCloseTagNode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      tag_opening: convert_token_field((*c_node_pointer).tag_opening),
      tag_name: convert_token_field((*c_node_pointer).tag_name),
      children: convert_children((*c_node_pointer).children),
      tag_closing: convert_token_field((*c_node_pointer).tag_closing),
    })
  }

  unsafe fn convert_html_omitted_close_tag_node(node_pointer: *const c_void) -> Option<HTMLOmittedCloseTagNode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_HTML_OMITTED_CLOSE_TAG_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(HTMLOmittedCloseTagNode {
      node_type: "HTMLOmittedCloseTagNode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      tag_name: convert_token_field((*c_node_pointer).tag_name),
    })
  }

  unsafe fn convert_html_virtual_close_tag_node(node_pointer: *const c_void) -> Option<HTMLVirtualCloseTagNode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_HTML_VIRTUAL_CLOSE_TAG_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(HTMLVirtualCloseTagNode {
      node_type: "HTMLVirtualCloseTagNode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      tag_name: convert_token_field((*c_node_pointer).tag_name),
    })
  }

  unsafe fn convert_html_element_node(node_pointer: *const c_void) -> Option<HTMLElementNode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_HTML_ELEMENT_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(HTMLElementNode {
      node_type: "HTMLElementNode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      open_tag: convert_erb_open_tag_node_or_html_conditional_open_tag_node_or_html_open_tag_node_field((*c_node_pointer).open_tag as *mut c_void),
      tag_name: convert_token_field((*c_node_pointer).tag_name),
      body: convert_children((*c_node_pointer).body),
      close_tag: convert_erb_end_node_or_html_close_tag_node_or_html_omitted_close_tag_node_or_html_virtual_close_tag_node_field((*c_node_pointer).close_tag as *mut c_void),
      is_void: (*c_node_pointer).is_void,
      element_source: convert_element_source((*c_node_pointer).element_source),
    })
  }

  unsafe fn convert_html_conditional_element_node(node_pointer: *const c_void) -> Option<HTMLConditionalElementNode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_HTML_CONDITIONAL_ELEMENT_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(HTMLConditionalElementNode {
      node_type: "HTMLConditionalElementNode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      condition: get_string_field((*c_node_pointer).condition),
      open_conditional: convert_erb_if_node_or_erb_unless_node_field((*c_node_pointer).open_conditional as *mut c_void),
      open_tag: convert_specific_node_field!((*c_node_pointer).open_tag, AST_HTML_OPEN_TAG_NODE, convert_html_open_tag_node, HTMLOpenTagNode),
      body: convert_children((*c_node_pointer).body),
      close_tag: convert_html_close_tag_node_or_html_omitted_close_tag_node_field((*c_node_pointer).close_tag as *mut c_void),
      close_conditional: convert_erb_if_node_or_erb_unless_node_field((*c_node_pointer).close_conditional as *mut c_void),
      tag_name: convert_token_field((*c_node_pointer).tag_name),
      element_source: convert_element_source((*c_node_pointer).element_source),
    })
  }

  unsafe fn convert_html_attribute_value_node(node_pointer: *const c_void) -> Option<HTMLAttributeValueNode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_HTML_ATTRIBUTE_VALUE_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(HTMLAttributeValueNode {
      node_type: "HTMLAttributeValueNode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      open_quote: convert_token_field((*c_node_pointer).open_quote),
      children: convert_children((*c_node_pointer).children),
      close_quote: convert_token_field((*c_node_pointer).close_quote),
      quoted: (*c_node_pointer).quoted,
    })
  }

  unsafe fn convert_html_attribute_name_node(node_pointer: *const c_void) -> Option<HTMLAttributeNameNode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_HTML_ATTRIBUTE_NAME_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(HTMLAttributeNameNode {
      node_type: "HTMLAttributeNameNode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      children: convert_children((*c_node_pointer).children),
    })
  }

  unsafe fn convert_html_attribute_node(node_pointer: *const c_void) -> Option<HTMLAttributeNode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_HTML_ATTRIBUTE_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(HTMLAttributeNode {
      node_type: "HTMLAttributeNode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      name: convert_specific_node_field!((*c_node_pointer).name, AST_HTML_ATTRIBUTE_NAME_NODE, convert_html_attribute_name_node, HTMLAttributeNameNode),
      equals: convert_token_field((*c_node_pointer).equals),
      value: convert_specific_node_field!((*c_node_pointer).value, AST_HTML_ATTRIBUTE_VALUE_NODE, convert_html_attribute_value_node, HTMLAttributeValueNode),
    })
  }

  unsafe fn convert_ruby_literal_node(node_pointer: *const c_void) -> Option<RubyLiteralNode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_RUBY_LITERAL_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(RubyLiteralNode {
      node_type: "RubyLiteralNode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      content: get_string_field((*c_node_pointer).content),
    })
  }

  unsafe fn convert_ruby_html_attributes_splat_node(node_pointer: *const c_void) -> Option<RubyHTMLAttributesSplatNode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_RUBY_HTML_ATTRIBUTES_SPLAT_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(RubyHTMLAttributesSplatNode {
      node_type: "RubyHTMLAttributesSplatNode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      content: get_string_field((*c_node_pointer).content),
      prefix: get_string_field((*c_node_pointer).prefix),
    })
  }

  unsafe fn convert_erb_open_tag_node(node_pointer: *const c_void) -> Option<ERBOpenTagNode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_ERB_OPEN_TAG_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(ERBOpenTagNode {
      node_type: "ERBOpenTagNode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      tag_opening: convert_token_field((*c_node_pointer).tag_opening),
      content: convert_token_field((*c_node_pointer).content),
      tag_closing: convert_token_field((*c_node_pointer).tag_closing),
      tag_name: convert_token_field((*c_node_pointer).tag_name),
      children: convert_children((*c_node_pointer).children),
    })
  }

  unsafe fn convert_html_text_node(node_pointer: *const c_void) -> Option<HTMLTextNode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_HTML_TEXT_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(HTMLTextNode {
      node_type: "HTMLTextNode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      content: get_string_field((*c_node_pointer).content),
    })
  }

  unsafe fn convert_html_comment_node(node_pointer: *const c_void) -> Option<HTMLCommentNode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_HTML_COMMENT_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(HTMLCommentNode {
      node_type: "HTMLCommentNode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      comment_start: convert_token_field((*c_node_pointer).comment_start),
      children: convert_children((*c_node_pointer).children),
      comment_end: convert_token_field((*c_node_pointer).comment_end),
    })
  }

  unsafe fn convert_html_doctype_node(node_pointer: *const c_void) -> Option<HTMLDoctypeNode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_HTML_DOCTYPE_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(HTMLDoctypeNode {
      node_type: "HTMLDoctypeNode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      tag_opening: convert_token_field((*c_node_pointer).tag_opening),
      children: convert_children((*c_node_pointer).children),
      tag_closing: convert_token_field((*c_node_pointer).tag_closing),
    })
  }

  unsafe fn convert_xml_declaration_node(node_pointer: *const c_void) -> Option<XMLDeclarationNode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_XML_DECLARATION_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(XMLDeclarationNode {
      node_type: "XMLDeclarationNode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      tag_opening: convert_token_field((*c_node_pointer).tag_opening),
      children: convert_children((*c_node_pointer).children),
      tag_closing: convert_token_field((*c_node_pointer).tag_closing),
    })
  }

  unsafe fn convert_cdata_node(node_pointer: *const c_void) -> Option<CDATANode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_CDATA_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(CDATANode {
      node_type: "CDATANode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      tag_opening: convert_token_field((*c_node_pointer).tag_opening),
      children: convert_children((*c_node_pointer).children),
      tag_closing: convert_token_field((*c_node_pointer).tag_closing),
    })
  }

  unsafe fn convert_whitespace_node(node_pointer: *const c_void) -> Option<WhitespaceNode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_WHITESPACE_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(WhitespaceNode {
      node_type: "WhitespaceNode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      value: convert_token_field((*c_node_pointer).value),
    })
  }

  unsafe fn convert_erb_content_node(node_pointer: *const c_void) -> Option<ERBContentNode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_ERB_CONTENT_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(ERBContentNode {
      node_type: "ERBContentNode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      tag_opening: convert_token_field((*c_node_pointer).tag_opening),
      content: convert_token_field((*c_node_pointer).content),
      tag_closing: convert_token_field((*c_node_pointer).tag_closing),
      // skip internal field: analyzed_ruby
      parsed: (*c_node_pointer).parsed,
      valid: (*c_node_pointer).valid,
      prism_node: if (*c_node_pointer).prism_node.node.is_null() || (*c_node_pointer).prism_node.parser.is_null() {
        None
      } else {
        let mut pm_buffer: crate::bindings::pm_buffer_t = std::mem::zeroed();
        crate::bindings::pm_prettyprint(&mut pm_buffer, (*c_node_pointer).prism_node.parser, (*c_node_pointer).prism_node.node);

        if pm_buffer.length > 0 && !pm_buffer.value.is_null() {
          let s = std::str::from_utf8(std::slice::from_raw_parts(pm_buffer.value as *const u8, pm_buffer.length)).unwrap_or("").to_string();
          crate::bindings::pm_buffer_free(&mut pm_buffer);
          Some(s)
        } else {
          crate::bindings::pm_buffer_free(&mut pm_buffer);
          None
        }
      },
    })
  }

  unsafe fn convert_erb_end_node(node_pointer: *const c_void) -> Option<ERBEndNode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_ERB_END_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(ERBEndNode {
      node_type: "ERBEndNode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      tag_opening: convert_token_field((*c_node_pointer).tag_opening),
      content: convert_token_field((*c_node_pointer).content),
      tag_closing: convert_token_field((*c_node_pointer).tag_closing),
    })
  }

  unsafe fn convert_erb_else_node(node_pointer: *const c_void) -> Option<ERBElseNode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_ERB_ELSE_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(ERBElseNode {
      node_type: "ERBElseNode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      tag_opening: convert_token_field((*c_node_pointer).tag_opening),
      content: convert_token_field((*c_node_pointer).content),
      tag_closing: convert_token_field((*c_node_pointer).tag_closing),
      statements: convert_children((*c_node_pointer).statements),
    })
  }

  unsafe fn convert_erb_if_node(node_pointer: *const c_void) -> Option<ERBIfNode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_ERB_IF_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(ERBIfNode {
      node_type: "ERBIfNode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      tag_opening: convert_token_field((*c_node_pointer).tag_opening),
      content: convert_token_field((*c_node_pointer).content),
      tag_closing: convert_token_field((*c_node_pointer).tag_closing),
      then_keyword: convert_location_field((*c_node_pointer).then_keyword),
      prism_node: if (*c_node_pointer).prism_node.node.is_null() || (*c_node_pointer).prism_node.parser.is_null() {
        None
      } else {
        let mut pm_buffer: crate::bindings::pm_buffer_t = std::mem::zeroed();
        crate::bindings::pm_prettyprint(&mut pm_buffer, (*c_node_pointer).prism_node.parser, (*c_node_pointer).prism_node.node);

        if pm_buffer.length > 0 && !pm_buffer.value.is_null() {
          let s = std::str::from_utf8(std::slice::from_raw_parts(pm_buffer.value as *const u8, pm_buffer.length)).unwrap_or("").to_string();
          crate::bindings::pm_buffer_free(&mut pm_buffer);
          Some(s)
        } else {
          crate::bindings::pm_buffer_free(&mut pm_buffer);
          None
        }
      },
      statements: convert_children((*c_node_pointer).statements),
      subsequent: convert_erb_else_node_or_erb_if_node_field((*c_node_pointer).subsequent as *mut c_void),
      end_node: convert_specific_node_field!((*c_node_pointer).end_node, AST_ERB_END_NODE, convert_erb_end_node, ERBEndNode),
    })
  }

  unsafe fn convert_erb_block_node(node_pointer: *const c_void) -> Option<ERBBlockNode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_ERB_BLOCK_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(ERBBlockNode {
      node_type: "ERBBlockNode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      tag_opening: convert_token_field((*c_node_pointer).tag_opening),
      content: convert_token_field((*c_node_pointer).content),
      tag_closing: convert_token_field((*c_node_pointer).tag_closing),
      prism_node: if (*c_node_pointer).prism_node.node.is_null() || (*c_node_pointer).prism_node.parser.is_null() {
        None
      } else {
        let mut pm_buffer: crate::bindings::pm_buffer_t = std::mem::zeroed();
        crate::bindings::pm_prettyprint(&mut pm_buffer, (*c_node_pointer).prism_node.parser, (*c_node_pointer).prism_node.node);

        if pm_buffer.length > 0 && !pm_buffer.value.is_null() {
          let s = std::str::from_utf8(std::slice::from_raw_parts(pm_buffer.value as *const u8, pm_buffer.length)).unwrap_or("").to_string();
          crate::bindings::pm_buffer_free(&mut pm_buffer);
          Some(s)
        } else {
          crate::bindings::pm_buffer_free(&mut pm_buffer);
          None
        }
      },
      body: convert_children((*c_node_pointer).body),
      rescue_clause: convert_specific_node_field!((*c_node_pointer).rescue_clause, AST_ERB_RESCUE_NODE, convert_erb_rescue_node, ERBRescueNode),
      else_clause: convert_specific_node_field!((*c_node_pointer).else_clause, AST_ERB_ELSE_NODE, convert_erb_else_node, ERBElseNode),
      ensure_clause: convert_specific_node_field!((*c_node_pointer).ensure_clause, AST_ERB_ENSURE_NODE, convert_erb_ensure_node, ERBEnsureNode),
      end_node: convert_specific_node_field!((*c_node_pointer).end_node, AST_ERB_END_NODE, convert_erb_end_node, ERBEndNode),
    })
  }

  unsafe fn convert_erb_when_node(node_pointer: *const c_void) -> Option<ERBWhenNode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_ERB_WHEN_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(ERBWhenNode {
      node_type: "ERBWhenNode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      tag_opening: convert_token_field((*c_node_pointer).tag_opening),
      content: convert_token_field((*c_node_pointer).content),
      tag_closing: convert_token_field((*c_node_pointer).tag_closing),
      then_keyword: convert_location_field((*c_node_pointer).then_keyword),
      statements: convert_children((*c_node_pointer).statements),
    })
  }

  unsafe fn convert_erb_case_node(node_pointer: *const c_void) -> Option<ERBCaseNode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_ERB_CASE_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(ERBCaseNode {
      node_type: "ERBCaseNode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      tag_opening: convert_token_field((*c_node_pointer).tag_opening),
      content: convert_token_field((*c_node_pointer).content),
      tag_closing: convert_token_field((*c_node_pointer).tag_closing),
      children: convert_children((*c_node_pointer).children),
      prism_node: if (*c_node_pointer).prism_node.node.is_null() || (*c_node_pointer).prism_node.parser.is_null() {
        None
      } else {
        let mut pm_buffer: crate::bindings::pm_buffer_t = std::mem::zeroed();
        crate::bindings::pm_prettyprint(&mut pm_buffer, (*c_node_pointer).prism_node.parser, (*c_node_pointer).prism_node.node);

        if pm_buffer.length > 0 && !pm_buffer.value.is_null() {
          let s = std::str::from_utf8(std::slice::from_raw_parts(pm_buffer.value as *const u8, pm_buffer.length)).unwrap_or("").to_string();
          crate::bindings::pm_buffer_free(&mut pm_buffer);
          Some(s)
        } else {
          crate::bindings::pm_buffer_free(&mut pm_buffer);
          None
        }
      },
      conditions: convert_children((*c_node_pointer).conditions),
      else_clause: convert_specific_node_field!((*c_node_pointer).else_clause, AST_ERB_ELSE_NODE, convert_erb_else_node, ERBElseNode),
      end_node: convert_specific_node_field!((*c_node_pointer).end_node, AST_ERB_END_NODE, convert_erb_end_node, ERBEndNode),
    })
  }

  unsafe fn convert_erb_case_match_node(node_pointer: *const c_void) -> Option<ERBCaseMatchNode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_ERB_CASE_MATCH_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(ERBCaseMatchNode {
      node_type: "ERBCaseMatchNode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      tag_opening: convert_token_field((*c_node_pointer).tag_opening),
      content: convert_token_field((*c_node_pointer).content),
      tag_closing: convert_token_field((*c_node_pointer).tag_closing),
      children: convert_children((*c_node_pointer).children),
      prism_node: if (*c_node_pointer).prism_node.node.is_null() || (*c_node_pointer).prism_node.parser.is_null() {
        None
      } else {
        let mut pm_buffer: crate::bindings::pm_buffer_t = std::mem::zeroed();
        crate::bindings::pm_prettyprint(&mut pm_buffer, (*c_node_pointer).prism_node.parser, (*c_node_pointer).prism_node.node);

        if pm_buffer.length > 0 && !pm_buffer.value.is_null() {
          let s = std::str::from_utf8(std::slice::from_raw_parts(pm_buffer.value as *const u8, pm_buffer.length)).unwrap_or("").to_string();
          crate::bindings::pm_buffer_free(&mut pm_buffer);
          Some(s)
        } else {
          crate::bindings::pm_buffer_free(&mut pm_buffer);
          None
        }
      },
      conditions: convert_children((*c_node_pointer).conditions),
      else_clause: convert_specific_node_field!((*c_node_pointer).else_clause, AST_ERB_ELSE_NODE, convert_erb_else_node, ERBElseNode),
      end_node: convert_specific_node_field!((*c_node_pointer).end_node, AST_ERB_END_NODE, convert_erb_end_node, ERBEndNode),
    })
  }

  unsafe fn convert_erb_while_node(node_pointer: *const c_void) -> Option<ERBWhileNode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_ERB_WHILE_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(ERBWhileNode {
      node_type: "ERBWhileNode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      tag_opening: convert_token_field((*c_node_pointer).tag_opening),
      content: convert_token_field((*c_node_pointer).content),
      tag_closing: convert_token_field((*c_node_pointer).tag_closing),
      prism_node: if (*c_node_pointer).prism_node.node.is_null() || (*c_node_pointer).prism_node.parser.is_null() {
        None
      } else {
        let mut pm_buffer: crate::bindings::pm_buffer_t = std::mem::zeroed();
        crate::bindings::pm_prettyprint(&mut pm_buffer, (*c_node_pointer).prism_node.parser, (*c_node_pointer).prism_node.node);

        if pm_buffer.length > 0 && !pm_buffer.value.is_null() {
          let s = std::str::from_utf8(std::slice::from_raw_parts(pm_buffer.value as *const u8, pm_buffer.length)).unwrap_or("").to_string();
          crate::bindings::pm_buffer_free(&mut pm_buffer);
          Some(s)
        } else {
          crate::bindings::pm_buffer_free(&mut pm_buffer);
          None
        }
      },
      statements: convert_children((*c_node_pointer).statements),
      end_node: convert_specific_node_field!((*c_node_pointer).end_node, AST_ERB_END_NODE, convert_erb_end_node, ERBEndNode),
    })
  }

  unsafe fn convert_erb_until_node(node_pointer: *const c_void) -> Option<ERBUntilNode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_ERB_UNTIL_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(ERBUntilNode {
      node_type: "ERBUntilNode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      tag_opening: convert_token_field((*c_node_pointer).tag_opening),
      content: convert_token_field((*c_node_pointer).content),
      tag_closing: convert_token_field((*c_node_pointer).tag_closing),
      prism_node: if (*c_node_pointer).prism_node.node.is_null() || (*c_node_pointer).prism_node.parser.is_null() {
        None
      } else {
        let mut pm_buffer: crate::bindings::pm_buffer_t = std::mem::zeroed();
        crate::bindings::pm_prettyprint(&mut pm_buffer, (*c_node_pointer).prism_node.parser, (*c_node_pointer).prism_node.node);

        if pm_buffer.length > 0 && !pm_buffer.value.is_null() {
          let s = std::str::from_utf8(std::slice::from_raw_parts(pm_buffer.value as *const u8, pm_buffer.length)).unwrap_or("").to_string();
          crate::bindings::pm_buffer_free(&mut pm_buffer);
          Some(s)
        } else {
          crate::bindings::pm_buffer_free(&mut pm_buffer);
          None
        }
      },
      statements: convert_children((*c_node_pointer).statements),
      end_node: convert_specific_node_field!((*c_node_pointer).end_node, AST_ERB_END_NODE, convert_erb_end_node, ERBEndNode),
    })
  }

  unsafe fn convert_erb_for_node(node_pointer: *const c_void) -> Option<ERBForNode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_ERB_FOR_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(ERBForNode {
      node_type: "ERBForNode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      tag_opening: convert_token_field((*c_node_pointer).tag_opening),
      content: convert_token_field((*c_node_pointer).content),
      tag_closing: convert_token_field((*c_node_pointer).tag_closing),
      prism_node: if (*c_node_pointer).prism_node.node.is_null() || (*c_node_pointer).prism_node.parser.is_null() {
        None
      } else {
        let mut pm_buffer: crate::bindings::pm_buffer_t = std::mem::zeroed();
        crate::bindings::pm_prettyprint(&mut pm_buffer, (*c_node_pointer).prism_node.parser, (*c_node_pointer).prism_node.node);

        if pm_buffer.length > 0 && !pm_buffer.value.is_null() {
          let s = std::str::from_utf8(std::slice::from_raw_parts(pm_buffer.value as *const u8, pm_buffer.length)).unwrap_or("").to_string();
          crate::bindings::pm_buffer_free(&mut pm_buffer);
          Some(s)
        } else {
          crate::bindings::pm_buffer_free(&mut pm_buffer);
          None
        }
      },
      statements: convert_children((*c_node_pointer).statements),
      end_node: convert_specific_node_field!((*c_node_pointer).end_node, AST_ERB_END_NODE, convert_erb_end_node, ERBEndNode),
    })
  }

  unsafe fn convert_erb_rescue_node(node_pointer: *const c_void) -> Option<ERBRescueNode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_ERB_RESCUE_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(ERBRescueNode {
      node_type: "ERBRescueNode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      tag_opening: convert_token_field((*c_node_pointer).tag_opening),
      content: convert_token_field((*c_node_pointer).content),
      tag_closing: convert_token_field((*c_node_pointer).tag_closing),
      statements: convert_children((*c_node_pointer).statements),
      subsequent: convert_specific_node_field!((*c_node_pointer).subsequent, AST_ERB_RESCUE_NODE, convert_erb_rescue_node, ERBRescueNode),
    })
  }

  unsafe fn convert_erb_ensure_node(node_pointer: *const c_void) -> Option<ERBEnsureNode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_ERB_ENSURE_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(ERBEnsureNode {
      node_type: "ERBEnsureNode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      tag_opening: convert_token_field((*c_node_pointer).tag_opening),
      content: convert_token_field((*c_node_pointer).content),
      tag_closing: convert_token_field((*c_node_pointer).tag_closing),
      statements: convert_children((*c_node_pointer).statements),
    })
  }

  unsafe fn convert_erb_begin_node(node_pointer: *const c_void) -> Option<ERBBeginNode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_ERB_BEGIN_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(ERBBeginNode {
      node_type: "ERBBeginNode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      tag_opening: convert_token_field((*c_node_pointer).tag_opening),
      content: convert_token_field((*c_node_pointer).content),
      tag_closing: convert_token_field((*c_node_pointer).tag_closing),
      prism_node: if (*c_node_pointer).prism_node.node.is_null() || (*c_node_pointer).prism_node.parser.is_null() {
        None
      } else {
        let mut pm_buffer: crate::bindings::pm_buffer_t = std::mem::zeroed();
        crate::bindings::pm_prettyprint(&mut pm_buffer, (*c_node_pointer).prism_node.parser, (*c_node_pointer).prism_node.node);

        if pm_buffer.length > 0 && !pm_buffer.value.is_null() {
          let s = std::str::from_utf8(std::slice::from_raw_parts(pm_buffer.value as *const u8, pm_buffer.length)).unwrap_or("").to_string();
          crate::bindings::pm_buffer_free(&mut pm_buffer);
          Some(s)
        } else {
          crate::bindings::pm_buffer_free(&mut pm_buffer);
          None
        }
      },
      statements: convert_children((*c_node_pointer).statements),
      rescue_clause: convert_specific_node_field!((*c_node_pointer).rescue_clause, AST_ERB_RESCUE_NODE, convert_erb_rescue_node, ERBRescueNode),
      else_clause: convert_specific_node_field!((*c_node_pointer).else_clause, AST_ERB_ELSE_NODE, convert_erb_else_node, ERBElseNode),
      ensure_clause: convert_specific_node_field!((*c_node_pointer).ensure_clause, AST_ERB_ENSURE_NODE, convert_erb_ensure_node, ERBEnsureNode),
      end_node: convert_specific_node_field!((*c_node_pointer).end_node, AST_ERB_END_NODE, convert_erb_end_node, ERBEndNode),
    })
  }

  unsafe fn convert_erb_unless_node(node_pointer: *const c_void) -> Option<ERBUnlessNode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_ERB_UNLESS_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(ERBUnlessNode {
      node_type: "ERBUnlessNode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      tag_opening: convert_token_field((*c_node_pointer).tag_opening),
      content: convert_token_field((*c_node_pointer).content),
      tag_closing: convert_token_field((*c_node_pointer).tag_closing),
      then_keyword: convert_location_field((*c_node_pointer).then_keyword),
      prism_node: if (*c_node_pointer).prism_node.node.is_null() || (*c_node_pointer).prism_node.parser.is_null() {
        None
      } else {
        let mut pm_buffer: crate::bindings::pm_buffer_t = std::mem::zeroed();
        crate::bindings::pm_prettyprint(&mut pm_buffer, (*c_node_pointer).prism_node.parser, (*c_node_pointer).prism_node.node);

        if pm_buffer.length > 0 && !pm_buffer.value.is_null() {
          let s = std::str::from_utf8(std::slice::from_raw_parts(pm_buffer.value as *const u8, pm_buffer.length)).unwrap_or("").to_string();
          crate::bindings::pm_buffer_free(&mut pm_buffer);
          Some(s)
        } else {
          crate::bindings::pm_buffer_free(&mut pm_buffer);
          None
        }
      },
      statements: convert_children((*c_node_pointer).statements),
      else_clause: convert_specific_node_field!((*c_node_pointer).else_clause, AST_ERB_ELSE_NODE, convert_erb_else_node, ERBElseNode),
      end_node: convert_specific_node_field!((*c_node_pointer).end_node, AST_ERB_END_NODE, convert_erb_end_node, ERBEndNode),
    })
  }

  unsafe fn convert_ruby_render_local_node(node_pointer: *const c_void) -> Option<RubyRenderLocalNode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_RUBY_RENDER_LOCAL_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(RubyRenderLocalNode {
      node_type: "RubyRenderLocalNode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      name: convert_token_field((*c_node_pointer).name),
      value: convert_specific_node_field!((*c_node_pointer).value, AST_RUBY_LITERAL_NODE, convert_ruby_literal_node, RubyLiteralNode),
    })
  }

  unsafe fn convert_erb_render_node(node_pointer: *const c_void) -> Option<ERBRenderNode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_ERB_RENDER_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(ERBRenderNode {
      node_type: "ERBRenderNode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      tag_opening: convert_token_field((*c_node_pointer).tag_opening),
      content: convert_token_field((*c_node_pointer).content),
      tag_closing: convert_token_field((*c_node_pointer).tag_closing),
      // skip internal field: analyzed_ruby
      prism_node: if (*c_node_pointer).prism_node.node.is_null() || (*c_node_pointer).prism_node.parser.is_null() {
        None
      } else {
        let mut pm_buffer: crate::bindings::pm_buffer_t = std::mem::zeroed();
        crate::bindings::pm_prettyprint(&mut pm_buffer, (*c_node_pointer).prism_node.parser, (*c_node_pointer).prism_node.node);

        if pm_buffer.length > 0 && !pm_buffer.value.is_null() {
          let s = std::str::from_utf8(std::slice::from_raw_parts(pm_buffer.value as *const u8, pm_buffer.length)).unwrap_or("").to_string();
          crate::bindings::pm_buffer_free(&mut pm_buffer);
          Some(s)
        } else {
          crate::bindings::pm_buffer_free(&mut pm_buffer);
          None
        }
      },
      partial: convert_token_field((*c_node_pointer).partial),
      template_path: convert_token_field((*c_node_pointer).template_path),
      layout: convert_token_field((*c_node_pointer).layout),
      file: convert_token_field((*c_node_pointer).file),
      inline_template: convert_token_field((*c_node_pointer).inline_template),
      body: convert_token_field((*c_node_pointer).body),
      plain: convert_token_field((*c_node_pointer).plain),
      html: convert_token_field((*c_node_pointer).html),
      renderable: convert_token_field((*c_node_pointer).renderable),
      collection: convert_token_field((*c_node_pointer).collection),
      object: convert_token_field((*c_node_pointer).object),
      as_name: convert_token_field((*c_node_pointer).as_name),
      spacer_template: convert_token_field((*c_node_pointer).spacer_template),
      formats: convert_token_field((*c_node_pointer).formats),
      variants: convert_token_field((*c_node_pointer).variants),
      handlers: convert_token_field((*c_node_pointer).handlers),
      content_type: convert_token_field((*c_node_pointer).content_type),
      locals: convert_children((*c_node_pointer).locals),
    })
  }

  unsafe fn convert_ruby_strict_local_node(node_pointer: *const c_void) -> Option<RubyStrictLocalNode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_RUBY_STRICT_LOCAL_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(RubyStrictLocalNode {
      node_type: "RubyStrictLocalNode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      name: convert_token_field((*c_node_pointer).name),
      default_value: convert_specific_node_field!((*c_node_pointer).default_value, AST_RUBY_LITERAL_NODE, convert_ruby_literal_node, RubyLiteralNode),
      required: (*c_node_pointer).required,
      double_splat: (*c_node_pointer).double_splat,
    })
  }

  unsafe fn convert_erb_strict_locals_node(node_pointer: *const c_void) -> Option<ERBStrictLocalsNode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_ERB_STRICT_LOCALS_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(ERBStrictLocalsNode {
      node_type: "ERBStrictLocalsNode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      tag_opening: convert_token_field((*c_node_pointer).tag_opening),
      content: convert_token_field((*c_node_pointer).content),
      tag_closing: convert_token_field((*c_node_pointer).tag_closing),
      // skip internal field: analyzed_ruby
      prism_node: if (*c_node_pointer).prism_node.node.is_null() || (*c_node_pointer).prism_node.parser.is_null() {
        None
      } else {
        let mut pm_buffer: crate::bindings::pm_buffer_t = std::mem::zeroed();
        crate::bindings::pm_prettyprint(&mut pm_buffer, (*c_node_pointer).prism_node.parser, (*c_node_pointer).prism_node.node);

        if pm_buffer.length > 0 && !pm_buffer.value.is_null() {
          let s = std::str::from_utf8(std::slice::from_raw_parts(pm_buffer.value as *const u8, pm_buffer.length)).unwrap_or("").to_string();
          crate::bindings::pm_buffer_free(&mut pm_buffer);
          Some(s)
        } else {
          crate::bindings::pm_buffer_free(&mut pm_buffer);
          None
        }
      },
      locals: convert_children((*c_node_pointer).locals),
    })
  }

  unsafe fn convert_erb_yield_node(node_pointer: *const c_void) -> Option<ERBYieldNode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_ERB_YIELD_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(ERBYieldNode {
      node_type: "ERBYieldNode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      tag_opening: convert_token_field((*c_node_pointer).tag_opening),
      content: convert_token_field((*c_node_pointer).content),
      tag_closing: convert_token_field((*c_node_pointer).tag_closing),
    })
  }

  unsafe fn convert_erb_in_node(node_pointer: *const c_void) -> Option<ERBInNode> {
    if node_pointer.is_null() {
      return None;
    }

    let c_node_pointer = node_pointer as *const AST_ERB_IN_NODE_T;
    let node_base_ref = &(*c_node_pointer).base;

    Some(ERBInNode {
      node_type: "ERBInNode".to_string(),
      location: convert_location(node_base_ref.location),
      errors: convert_errors(node_base_ref.errors),
      tag_opening: convert_token_field((*c_node_pointer).tag_opening),
      content: convert_token_field((*c_node_pointer).content),
      tag_closing: convert_token_field((*c_node_pointer).tag_closing),
      then_keyword: convert_location_field((*c_node_pointer).then_keyword),
      statements: convert_children((*c_node_pointer).statements),
    })
  }

#[allow(dead_code)]
unsafe fn convert_erb_content_node_or_literal_node_field(node_pointer: *mut c_void) -> Option<ERBContentNodeOrLiteralNode> {
  if node_pointer.is_null() {
    return None;
  }

  let base_node = node_pointer as *const AST_NODE_STRUCT;
  let node_type = (*base_node).type_;

  match node_type {
    AST_ERB_CONTENT_NODE => {
      Some(ERBContentNodeOrLiteralNode::ERBContentNode(Box::new(convert_erb_content_node(node_pointer as *const c_void)?)))
    }
    AST_LITERAL_NODE => {
      Some(ERBContentNodeOrLiteralNode::LiteralNode(Box::new(convert_literal_node(node_pointer as *const c_void)?)))
    }
    _ => None,
  }
}

#[allow(dead_code)]
unsafe fn convert_erb_else_node_or_erb_if_node_field(node_pointer: *mut c_void) -> Option<ERBElseNodeOrERBIfNode> {
  if node_pointer.is_null() {
    return None;
  }

  let base_node = node_pointer as *const AST_NODE_STRUCT;
  let node_type = (*base_node).type_;

  match node_type {
    AST_ERB_ELSE_NODE => {
      Some(ERBElseNodeOrERBIfNode::ERBElseNode(Box::new(convert_erb_else_node(node_pointer as *const c_void)?)))
    }
    AST_ERB_IF_NODE => {
      Some(ERBElseNodeOrERBIfNode::ERBIfNode(Box::new(convert_erb_if_node(node_pointer as *const c_void)?)))
    }
    _ => None,
  }
}

#[allow(dead_code)]
unsafe fn convert_erb_end_node_or_html_close_tag_node_or_html_omitted_close_tag_node_or_html_virtual_close_tag_node_field(node_pointer: *mut c_void) -> Option<ERBEndNodeOrHTMLCloseTagNodeOrHTMLOmittedCloseTagNodeOrHTMLVirtualCloseTagNode> {
  if node_pointer.is_null() {
    return None;
  }

  let base_node = node_pointer as *const AST_NODE_STRUCT;
  let node_type = (*base_node).type_;

  match node_type {
    AST_ERB_END_NODE => {
      Some(ERBEndNodeOrHTMLCloseTagNodeOrHTMLOmittedCloseTagNodeOrHTMLVirtualCloseTagNode::ERBEndNode(Box::new(convert_erb_end_node(node_pointer as *const c_void)?)))
    }
    AST_HTML_CLOSE_TAG_NODE => {
      Some(ERBEndNodeOrHTMLCloseTagNodeOrHTMLOmittedCloseTagNodeOrHTMLVirtualCloseTagNode::HTMLCloseTagNode(Box::new(convert_html_close_tag_node(node_pointer as *const c_void)?)))
    }
    AST_HTML_OMITTED_CLOSE_TAG_NODE => {
      Some(ERBEndNodeOrHTMLCloseTagNodeOrHTMLOmittedCloseTagNodeOrHTMLVirtualCloseTagNode::HTMLOmittedCloseTagNode(Box::new(convert_html_omitted_close_tag_node(node_pointer as *const c_void)?)))
    }
    AST_HTML_VIRTUAL_CLOSE_TAG_NODE => {
      Some(ERBEndNodeOrHTMLCloseTagNodeOrHTMLOmittedCloseTagNodeOrHTMLVirtualCloseTagNode::HTMLVirtualCloseTagNode(Box::new(convert_html_virtual_close_tag_node(node_pointer as *const c_void)?)))
    }
    _ => None,
  }
}

#[allow(dead_code)]
unsafe fn convert_erb_if_node_or_erb_unless_node_field(node_pointer: *mut c_void) -> Option<ERBIfNodeOrERBUnlessNode> {
  if node_pointer.is_null() {
    return None;
  }

  let base_node = node_pointer as *const AST_NODE_STRUCT;
  let node_type = (*base_node).type_;

  match node_type {
    AST_ERB_IF_NODE => {
      Some(ERBIfNodeOrERBUnlessNode::ERBIfNode(Box::new(convert_erb_if_node(node_pointer as *const c_void)?)))
    }
    AST_ERB_UNLESS_NODE => {
      Some(ERBIfNodeOrERBUnlessNode::ERBUnlessNode(Box::new(convert_erb_unless_node(node_pointer as *const c_void)?)))
    }
    _ => None,
  }
}

#[allow(dead_code)]
unsafe fn convert_erb_open_tag_node_or_html_conditional_open_tag_node_or_html_open_tag_node_field(node_pointer: *mut c_void) -> Option<ERBOpenTagNodeOrHTMLConditionalOpenTagNodeOrHTMLOpenTagNode> {
  if node_pointer.is_null() {
    return None;
  }

  let base_node = node_pointer as *const AST_NODE_STRUCT;
  let node_type = (*base_node).type_;

  match node_type {
    AST_ERB_OPEN_TAG_NODE => {
      Some(ERBOpenTagNodeOrHTMLConditionalOpenTagNodeOrHTMLOpenTagNode::ERBOpenTagNode(Box::new(convert_erb_open_tag_node(node_pointer as *const c_void)?)))
    }
    AST_HTML_CONDITIONAL_OPEN_TAG_NODE => {
      Some(ERBOpenTagNodeOrHTMLConditionalOpenTagNodeOrHTMLOpenTagNode::HTMLConditionalOpenTagNode(Box::new(convert_html_conditional_open_tag_node(node_pointer as *const c_void)?)))
    }
    AST_HTML_OPEN_TAG_NODE => {
      Some(ERBOpenTagNodeOrHTMLConditionalOpenTagNodeOrHTMLOpenTagNode::HTMLOpenTagNode(Box::new(convert_html_open_tag_node(node_pointer as *const c_void)?)))
    }
    _ => None,
  }
}

#[allow(dead_code)]
unsafe fn convert_html_close_tag_node_or_html_omitted_close_tag_node_field(node_pointer: *mut c_void) -> Option<HTMLCloseTagNodeOrHTMLOmittedCloseTagNode> {
  if node_pointer.is_null() {
    return None;
  }

  let base_node = node_pointer as *const AST_NODE_STRUCT;
  let node_type = (*base_node).type_;

  match node_type {
    AST_HTML_CLOSE_TAG_NODE => {
      Some(HTMLCloseTagNodeOrHTMLOmittedCloseTagNode::HTMLCloseTagNode(Box::new(convert_html_close_tag_node(node_pointer as *const c_void)?)))
    }
    AST_HTML_OMITTED_CLOSE_TAG_NODE => {
      Some(HTMLCloseTagNodeOrHTMLOmittedCloseTagNode::HTMLOmittedCloseTagNode(Box::new(convert_html_omitted_close_tag_node(node_pointer as *const c_void)?)))
    }
    _ => None,
  }
}

