class Fastlane::SwiftFunction

Attributes

default_values_to_ignore[RW]
function_description[RW]
function_details[RW]
function_name[RW]
param_default_values[RW]
param_descriptions[RW]
param_is_strings[RW]
param_names[RW]
param_optionality_values[RW]
param_type_overrides[RW]
reserved_words[RW]
return_type[RW]
return_value[RW]
sample_return_value[RW]

Public Class Methods

new(action_name: nil, action_description: nil, action_details: nil, keys: nil, key_descriptions: nil, key_default_values: nil, key_optionality_values: nil, key_type_overrides: nil, key_is_strings: nil, return_type: nil, return_value: nil, sample_return_value: nil) click to toggle source
# File fastlane/lib/fastlane/swift_fastlane_function.rb, line 18
def initialize(action_name: nil, action_description: nil, action_details: nil, keys: nil, key_descriptions: nil, key_default_values: nil, key_optionality_values: nil, key_type_overrides: nil, key_is_strings: nil, return_type: nil, return_value: nil, sample_return_value: nil)
  @function_name = action_name
  @function_description = action_description
  @function_details = action_details
  @param_names = keys
  @param_descriptions = key_descriptions
  @param_default_values = key_default_values
  @param_optionality_values = key_optionality_values
  @param_is_strings = key_is_strings
  @return_type = return_type
  @return_value = non_empty(string: return_value)
  @sample_return_value = non_empty(string: sample_return_value)
  @param_type_overrides = key_type_overrides

  # rubocop:disable Layout/LineLength
  # class instance?
  @reserved_words = %w[actor associativity async await break case catch class continue convenience default deinit didSet do else enum extension fallthrough false final for func guard if in infix init inout internal lazy let mutating nil operator override precedence private public repeat required return self static struct subscript super switch throws true try var weak where while willSet].to_set
  # rubocop:enable Layout/LineLength
end

Public Instance Methods

build_argument_list() click to toggle source
# File fastlane/lib/fastlane/swift_fastlane_function.rb, line 278
def build_argument_list
  unless @param_names
    return "[]" # return empty list for argument
  end

  argument_object_strings = @param_names.zip(param_type_overrides, param_default_values, param_optionality_values, param_is_strings).map do |name, type_override, default_value, is_optional, is_string|
    type = get_type(param: name, default_value: default_value, optional: is_optional, param_type_override: type_override, is_string: is_string)
    sanitized_name = camel_case_lower(string: name)
    sanitized_name = sanitize_reserved_word(word: sanitized_name)
    type_string = type_override == :string_callback ? ".stringClosure" : "nil"

    if !(type_override == :string_callback || !(is_optional && default_value.nil? && !type.start_with?('Any') || type.start_with?('Bool')))
      { name: "#{sanitized_name.gsub('`', '')}Arg", arg: "let #{sanitized_name.gsub('`', '')}Arg = #{sanitized_name}.asRubyArgument(name: \"#{name}\", type: #{type_string})" }
    else
      { name: "#{sanitized_name.gsub('`', '')}Arg", arg: "let #{sanitized_name.gsub('`', '')}Arg = RubyCommand.Argument(name: \"#{name}\", value: #{sanitized_name}, type: #{type_string})" }
    end
  end
  return argument_object_strings
end
camel_case_lower(string: nil) click to toggle source
# File fastlane/lib/fastlane/swift_fastlane_function.rb, line 85
def camel_case_lower(string: nil)
  string.split('_').inject([]) { |buffer, e| buffer.push(buffer.empty? ? e : e.capitalize) }.join
end
determine_type_from_override(type_override: nil, default_type: nil) click to toggle source
# File fastlane/lib/fastlane/swift_fastlane_function.rb, line 89
def determine_type_from_override(type_override: nil, default_type: nil)
  if type_override == Array
    return "[String]"
  elsif type_override == Hash
    return "[String : Any]"
  elsif type_override == Integer
    return "Int"
  elsif type_override == Boolean
    return "Bool"
  elsif type_override == Float
    return "Float"
  elsif type_override == String
    return "String"
  elsif type_override == :string_callback
    # David Hart:
    # It doesn't make sense to add escaping annotations to optional closures because they aren't function types:
    # they are basically an enum (Optional) containing a function, the same way you would store a closure in any type:
    # it's implicitly escaping because it's owned by another type.
    return "((String) -> Void)?"
  else
    return default_type
  end
end
fix_documentation_indentation(string: nil) click to toggle source
# File fastlane/lib/fastlane/swift_fastlane_function.rb, line 273
def fix_documentation_indentation(string: nil)
  indent = ' '
  string.gsub("\n", "\n#{indent}")
end
get_type(param: nil, default_value: nil, optional: nil, param_type_override: nil, is_string: true) click to toggle source
# File fastlane/lib/fastlane/swift_fastlane_function.rb, line 120
def get_type(param: nil, default_value: nil, optional: nil, param_type_override: nil, is_string: true)
  require 'bigdecimal'
  unless param_type_override.nil?
    type = determine_type_from_override(type_override: param_type_override)
  end

  # defaulting type to Any if is_string is false so users are allowed to input all allowed types
  type ||= is_string ? "String" : "Any"

  optional_specifier = ""
  # if we are optional and don't have a default value, we'll need to use ?
  optional_specifier = "?" if (optional && default_value.nil?) && type != "((String) -> Void)?"

  # If we have a default value of true or false, we can infer it is a Bool
  if default_value.class == FalseClass
    type = "Bool"
  elsif default_value.class == TrueClass
    type = "Bool"
  elsif default_value.kind_of?(Array)
    type = "[String]"
  elsif default_value.kind_of?(Hash)
    type = "[String : Any]"
  # Although we can have a default value of Integer type, if param_type_override overridden that value, respect it.
  elsif default_value.kind_of?(Integer)
    if type == "Double" || type == "Float"
      begin
        # If we're not able to instantiate
        _ = BigDecimal(default_value)
      rescue
        # We set it as a Int
        type = "Int"
      end
    else
      type = "Int"
    end
  end
  return "#{type}#{optional_specifier}"
end
implementation() click to toggle source
# File fastlane/lib/fastlane/swift_fastlane_function.rb, line 322
def implementation
  args = build_argument_list
  implm = "#{args.group_by { |h| h[:arg] }.keys.join("\n")}\n"
  if args.empty?
    implm += "let args: [RubyCommand.Argument] = []\n"
  else
    implm += "let array: [RubyCommand.Argument?] = [#{args.group_by { |h| h[:name] }.keys.join(",\n")}]\n"
    implm += "let args: [RubyCommand.Argument] = array\n"
    implm += ".filter { $0?.value != nil }\n"
    implm += ".compactMap { $0 }\n"
  end
  implm += "let command = RubyCommand(commandID: \"\", methodName: \"#{@function_name}\", className: nil, args: args)\n"

  return implm + "  #{return_statement}"
end
non_empty(string: nil) click to toggle source
# File fastlane/lib/fastlane/swift_fastlane_function.rb, line 45
def non_empty(string: nil)
  if string.nil? || string.to_s.empty?
    return nil
  else
    return string
  end
end
override_default_value_if_not_correct_type(param_name: nil, param_type: nil, default_value: nil) click to toggle source
# File fastlane/lib/fastlane/swift_fastlane_function.rb, line 113
def override_default_value_if_not_correct_type(param_name: nil, param_type: nil, default_value: nil)
  return "[]" if param_type == "[String]" && default_value == ""
  return "nil" if param_type == "((String) -> Void)?"

  return default_value
end
parameters() click to toggle source

rubocop:disable Metrics/PerceivedComplexity

# File fastlane/lib/fastlane/swift_fastlane_function.rb, line 160
def parameters
  unless @param_names
    return ""
  end

  param_names_and_types = @param_names.zip(param_default_values, param_optionality_values, param_type_overrides, param_is_strings).map do |param, default_value, optional, param_type_override, is_string|
    type = get_type(param: param, default_value: default_value, optional: optional, param_type_override: param_type_override, is_string: is_string)

    unless default_value.nil?
      if type == "[String : Any]"
        # we can't handle default values for Hashes, yet
        # see method swift_default_implementations for similar behavior
        default_value = "[:]"
      elsif type != "Bool" && type != "[String]" && type != "Int" && type != "@escaping ((String) -> Void)" && type != "Float" && type != "Double"
        default_value = "\"#{default_value}\""
      elsif type == "Float" || type == "Double"
        require 'bigdecimal'
        default_value = BigDecimal(default_value).to_s
      end
    end

    # if we don't have a default value, but the param is optional, set a default value in Swift to be nil
    if optional && default_value.nil?
      default_value = "nil"
    end

    # sometimes we get to the point where we have a default value but its type is wrong
    # so we need to correct that because [String] = "" is not valid swift
    default_value = override_default_value_if_not_correct_type(param_type: type, param_name: param, default_value: default_value)

    param = camel_case_lower(string: param)
    param = sanitize_reserved_word(word: param)

    if default_value.nil?
      "#{param}: #{type}"
    else
      if type == "((String) -> Void)?"
        "#{param}: #{type} = nil"
      elsif optional && type.end_with?('?') && !type.start_with?('Any') || type.start_with?('Bool')
        "#{param}: OptionalConfigValue<#{type}> = .fastlaneDefault(#{default_value})"
      else
        "#{param}: #{type} = #{default_value}"
      end
    end
  end

  return param_names_and_types
end
return_declaration() click to toggle source
# File fastlane/lib/fastlane/swift_fastlane_function.rb, line 53
def return_declaration
  expected_type = swift_type_for_return_type
  unless expected_type.to_s.length > 0
    return ""
  end

  return " -> #{expected_type}"
end
return_statement() click to toggle source
# File fastlane/lib/fastlane/swift_fastlane_function.rb, line 298
def return_statement
  returned_object = "runner.executeCommand(command)"
  case @return_type
  when :array_of_strings
    returned_object = "parseArray(fromString: #{returned_object})"
  when :hash_of_strings
    returned_object = "parseDictionary(fromString: #{returned_object})"
  when :hash
    returned_object = "parseDictionary(fromString: #{returned_object})"
  when :bool
    returned_object = "parseBool(fromString: #{returned_object})"
  when :int
    returned_object = "parseInt(fromString: #{returned_object})"
  end

  expected_type = swift_type_for_return_type

  return_string = "_ = "
  if expected_type.length > 0
    return_string = "return "
  end
  return "#{return_string}#{returned_object}"
end
sanitize_reserved_word(word: nil) click to toggle source
# File fastlane/lib/fastlane/swift_fastlane_function.rb, line 38
def sanitize_reserved_word(word: nil)
  unless @reserved_words.include?(word)
    return word
  end
  return "`#{word}`"
end
swift_code() click to toggle source

rubocop:enable Metrics/PerceivedComplexity

# File fastlane/lib/fastlane/swift_fastlane_function.rb, line 210
def swift_code
  function_name = camel_case_lower(string: self.function_name)
  function_return_declaration = self.return_declaration
  discardable_result = function_return_declaration.length > 0 ? "@discardableResult " : ''

  # Calculate the necessary indent to line up parameter names on new lines
  # with the first parameter after the opening paren following the function name.
  # i.e.: @discardableResult func someFunctionName(firstParameter: T
  #                                                secondParameter: T)
  # This just creates a string with as many spaces are necessary given whether or not
  # the function has a 'discardableResult' annotation, the 'func' keyword, function name
  # and the opening paren.
  function_keyword_definition = 'public func '
  open_paren = '('
  closed_paren = ')'
  indent = ' ' * (discardable_result.length + function_name.length + function_keyword_definition.length + open_paren.length)
  params = self.parameters.join(",\n#{indent}")

  return "#{swift_documentation}#{discardable_result}#{function_keyword_definition}#{function_name}#{open_paren}#{params}#{closed_paren}#{function_return_declaration} {\n#{self.implementation}\n}\n"
end
swift_documentation() click to toggle source
# File fastlane/lib/fastlane/swift_fastlane_function.rb, line 231
def swift_documentation
  has_parameters = @param_names && @param_names.length > 0
  unless @function_description || @function_details || has_parameters
    return ''
  end

  description = " #{fix_documentation_indentation(string: @function_description)}" if @function_description
  details = " #{fix_documentation_indentation(string: @function_details)}" if @function_details
  separator = ''
  documentation_elements = [description, swift_parameter_documentation, swift_return_value_documentation, details].compact
  # Adds newlines between each documentation element.
  documentation = documentation_elements.flat_map { |element| [element, separator] }.tap(&:pop).join("\n")

  return "/**\n#{documentation.gsub('/*', '/\\*')}\n*/\n"
end
swift_parameter_documentation() click to toggle source
# File fastlane/lib/fastlane/swift_fastlane_function.rb, line 247
def swift_parameter_documentation
  unless @param_names && @param_names.length > 0
    return nil
  end

  names_and_descriptions = @param_names.zip(@param_descriptions)

  if @param_names.length == 1
    detail_strings = names_and_descriptions.map { |name, description| " - parameter #{camel_case_lower(string: name)}: #{description}" }
    return detail_strings.first
  else
    detail_strings = names_and_descriptions.map { |name, description| "   - #{camel_case_lower(string: name)}: #{description}" }
    return " - parameters:\n#{detail_strings.join("\n")}"
  end
end
swift_return_value_documentation() click to toggle source
# File fastlane/lib/fastlane/swift_fastlane_function.rb, line 263
def swift_return_value_documentation
  unless @return_value
    return nil
  end

  sample = ". Example: #{@sample_return_value}" if @sample_return_value

  return " - returns: #{return_value}#{sample}"
end
swift_type_for_return_type() click to toggle source
# File fastlane/lib/fastlane/swift_fastlane_function.rb, line 62
def swift_type_for_return_type
  unless @return_type
    return ""
  end

  case @return_type
  when :string
    return "String"
  when :array_of_strings
    return "[String]"
  when :hash_of_strings
    return "[String : String]"
  when :hash
    return "[String : Any]"
  when :bool
    return "Bool"
  when :int
    return "Int"
  else
    return ""
  end
end