class Result

def self.failure(error: {})

def self.failure(error: {})
  Result.new(success: false, error: OpenStruct.new(error))
end

def self.success(data: {})

def self.success(data: {})
  Result.new(success: true, data: OpenStruct.new(data))
end

def self.try

def self.try
  try_result = yield
  unless try_result.is_a?(Result)
    raise StandardError, "Try result must be a Result object, got: #{try_result.class}"
  end
  try_result
rescue StandardError => e
  Result.failure(error: { message: e.message, error_class: e.class, backtrace: e.backtrace })
end

def failure?

def failure?
  !@success
end

def fold(success: nil, failure: nil)

def fold(success: nil, failure: nil)
  fold_result = if success?
    if success.respond_to?(:call)
      success.call(@data)
    else
      if block_given?
        yield(@data)
      else
        self
      end
    end
  else
    if failure.respond_to?(:call)
      failure.call(@error)
    else
      self
    end
  end
  unless fold_result.is_a?(Result)
    raise StandardError, "Fold result must be a Result object, got: #{fold_result.class}"
  end
  fold_result
end

def initialize(success:, data: nil, error: nil)

def initialize(success:, data: nil, error: nil)
  @success = success
  @data = data
  @error = error
end

def raise_error

def raise_error
  if failure?
    err_class = error.error_class || StandardError
    e = err_class.new(error.message)
    if error.backtrace
      e.set_backtrace(error.backtrace)
    end
    raise e
  end
end

def slice(*keys)

def slice(*keys)
  if success?
    sliced_data = @data.to_h.slice(*keys)
    Result.success(data: sliced_data)
  else
    sliced_error = @error.to_h.slice(*keys)
    Result.failure(error: sliced_error)
  end
end

def success?

def success?
  @success
end

def to_h

def to_h
  {
    success: @success,
    data: @data,
    error: @error,
  }
end