rs
  1# frozen_string_literal: true
  2
  3module Net
  4  module Hippie
  5    # Rust backend integration and availability detection.
  6    #
  7    # The RustBackend module manages the optional high-performance Rust HTTP client
  8    # backend. It provides automatic detection of Rust extension availability and
  9    # environment-based enabling/disabling of the Rust backend.
 10    #
 11    # == Backend Selection Logic
 12    #
 13    # 1. Check if NET_HIPPIE_RUST environment variable is set to 'true'
 14    # 2. Verify that the Rust extension (net_hippie_ext) can be loaded
 15    # 3. If both conditions are met, use RustConnection
 16    # 4. Otherwise, fall back to RubyConnection
 17    #
 18    # == Performance Benefits
 19    #
 20    # When enabled, the Rust backend provides:
 21    # * Significantly faster HTTP requests using reqwest
 22    # * Better concurrency with Tokio async runtime
 23    # * Lower memory usage with zero-cost abstractions
 24    # * Type safety with compile-time guarantees
 25    #
 26    # @since 2.0.0
 27    #
 28    # == Environment Configuration
 29    #
 30    #   # Enable Rust backend
 31    #   ENV['NET_HIPPIE_RUST'] = 'true'
 32    #
 33    #   # Check availability and status
 34    #   puts "Rust available: #{Net::Hippie::RustBackend.available?}"
 35    #   puts "Rust enabled: #{Net::Hippie::RustBackend.enabled?}"
 36    #
 37    # @see RUST_BACKEND.md Detailed setup and usage documentation
 38    module RustBackend
 39      @rust_available = nil
 40
 41      # Checks if the Rust extension is available for loading.
 42      #
 43      # This method attempts to require the 'net_hippie_ext' native extension
 44      # and caches the result. The extension is built from Rust source code
 45      # using Magnus for Ruby-Rust integration.
 46      #
 47      # @return [Boolean] true if Rust extension loaded successfully
 48      # @since 2.0.0
 49      #
 50      # @example Check Rust availability
 51      #   if Net::Hippie::RustBackend.available?
 52      #     puts "Rust backend ready!"
 53      #   else
 54      #     puts "Using Ruby backend (Rust not available)"
 55      #   end
 56      def self.available?
 57        return @rust_available unless @rust_available.nil?
 58
 59        @rust_available = begin
 60          require 'net_hippie_ext'
 61          true
 62        rescue LoadError
 63          false
 64        end
 65      end
 66
 67      # Checks if the Rust backend is both available and enabled.
 68      #
 69      # Returns true only when:
 70      # 1. NET_HIPPIE_RUST environment variable is set to 'true'
 71      # 2. The Rust extension is available (compiled and loadable)
 72      #
 73      # @return [Boolean] true if Rust backend should be used
 74      # @since 2.0.0
 75      #
 76      # @example Check if Rust backend will be used
 77      #   ENV['NET_HIPPIE_RUST'] = 'true'
 78      #   if Net::Hippie::RustBackend.enabled?
 79      #     puts "All HTTP requests will use Rust backend"
 80      #   else
 81      #     puts "Falling back to Ruby backend"
 82      #   end
 83      def self.enabled?
 84        ENV['NET_HIPPIE_RUST'] == 'true' && available?
 85      end
 86
 87      # Adapter that makes Rust HTTP responses compatible with Net::HTTPResponse interface.
 88      #
 89      # The ResponseAdapter provides a compatibility layer between Rust HTTP responses
 90      # and Ruby's Net::HTTPResponse objects. This ensures that existing code works
 91      # unchanged when switching between Ruby and Rust backends.
 92      #
 93      # == Compatibility Features
 94      #
 95      # * Status code access via #code method
 96      # * Response body access via #body method
 97      # * Header access via #[] method
 98      # * Response class detection via #class method
 99      # * Type checking via #is_a? and #kind_of?
100      #
101      # @since 2.0.0
102      #
103      # == Supported Response Classes
104      #
105      # * Net::HTTPOK (200)
106      # * Net::HTTPCreated (201)
107      # * Net::HTTPRedirection (3xx)
108      # * Net::HTTPClientError (4xx)
109      # * Net::HTTPServerError (5xx)
110      #
111      # @see Net::HTTPResponse The Ruby standard library response interface
112      class ResponseAdapter
113        # Creates a new response adapter from a Rust HTTP response.
114        #
115        # @param rust_response [RustResponse] The Rust HTTP response object
116        # @since 2.0.0
117        def initialize(rust_response)
118          @rust_response = rust_response
119          @code = rust_response.code
120          @body = rust_response.body
121        end
122
123        # Returns the HTTP status code.
124        #
125        # @return [String] HTTP status code (e.g., "200", "404")
126        # @since 2.0.0
127        def code
128          @code
129        end
130
131        # Returns the response body content.
132        #
133        # @return [String] HTTP response body
134        # @since 2.0.0
135        def body
136          @body
137        end
138
139        # Retrieves a response header value by name.
140        #
141        # @param header_name [String, Symbol] Header name (case-insensitive)
142        # @return [String, nil] Header value or nil if not found
143        # @since 2.0.0
144        #
145        # @example Get content type
146        #   content_type = response['Content-Type']
147        #   location = response[:location]
148        def [](header_name)
149          @rust_response[header_name.to_s]
150        end
151
152        # Returns the appropriate Net::HTTP response class based on status code.
153        #
154        # Maps HTTP status codes to their corresponding Net::HTTP class constants
155        # to maintain compatibility with Ruby HTTP library expectations.
156        #
157        # @return [Class] Net::HTTP response class constant
158        # @since 2.0.0
159        #
160        # @example Check response type
161        #   response.class # => Net::HTTPOK (for 200 status)
162        #   response.class # => Net::HTTPNotFound (for 404 status)
163        def class
164          case @code.to_i
165          when 200
166            Net::HTTPOK
167          when 201
168            Net::HTTPCreated
169          when 300..399
170            Net::HTTPRedirection
171          when 400..499
172            Net::HTTPClientError
173          when 500..599
174            Net::HTTPServerError
175          else
176            Net::HTTPResponse
177          end
178        end
179
180        # Checks if this response is an instance of the given class.
181        #
182        # Provides compatibility with Ruby's type checking by delegating
183        # to the mapped response class while supporting normal inheritance.
184        #
185        # @param klass [Class] Class to check against
186        # @return [Boolean] true if response matches the class
187        # @since 2.0.0
188        #
189        # @example Type checking
190        #   response.is_a?(Net::HTTPOK)          # => true (for 200 status)
191        #   response.is_a?(Net::HTTPRedirection) # => true (for 3xx status)
192        def is_a?(klass)
193          self.class == klass || super
194        end
195
196        # Alias for #is_a? to maintain Ruby compatibility.
197        #
198        # @param klass [Class] Class to check against
199        # @return [Boolean] true if response matches the class
200        # @since 2.0.0
201        def kind_of?(klass)
202          is_a?(klass)
203        end
204      end
205    end
206  end
207end