class ApiDiff::SwiftInterfaceParser

Public Instance Methods

parse(content) click to toggle source
# File lib/api_diff/swift_interface_parser.rb, line 3
def parse(content)
  Interface.type_name = "protocol" unless @options[:normalize]

  module_name_match = content.match(/@_exported import (\w+)/)
  container_types = module_name_match ? module_name_match[1] : []
  parse_blocks(content, container_types)
  parse_one_line_extensions(content)
end

Private Instance Methods

parse_blocks(content, container_types = []) click to toggle source
# File lib/api_diff/swift_interface_parser.rb, line 14
def parse_blocks(content, container_types = [])
  sections = content.scan(/^[^\n]+?{$.*?^}$/m)
  sections.each do |section|
    first_line = section.split("\n")[0]
    if first_line.include? "public class"
      parse_class(section, container_types)
    elsif first_line.include? "public struct"
      parse_struct(section, container_types)
    elsif first_line.include? "public protocol"
      parse_ptotocol(section, container_types)
    elsif first_line.start_with? "extension"
      parse_extension(section, container_types)
    elsif first_line.include? "public enum"
      parse_enum(section, container_types)
    end
  end
end
parse_class(class_content, container_types) click to toggle source
# File lib/api_diff/swift_interface_parser.rb, line 39
def parse_class(class_content, container_types)
  name = class_content.match(/public class (\w+)/)[1]

  cls = Class.new(name, qualified_name(name, container_types))
  cls.parents = parse_parents(class_content)
  cls.properties = parse_properties(class_content)
  cls.functions = parse_functions(class_content)
  api.classes << cls

  parse_nested_types(class_content, [*container_types, name])
end
parse_enum(enum_content, container_types) click to toggle source
# File lib/api_diff/swift_interface_parser.rb, line 90
def parse_enum(enum_content, container_types)
  name = enum_content.match(/public enum (\w+)/)[1]

  enum = Enum.new(name, qualified_name(name, container_types))
  enum.cases = parse_enum_cases(enum_content)
  enum.parents = parse_parents(enum_content)
  enum.properties = parse_properties(enum_content)
  enum.functions = parse_functions(enum_content)
  api.enums << enum

  parse_nested_types(enum_content, [*container_types, name])
end
parse_enum_cases(content) click to toggle source
# File lib/api_diff/swift_interface_parser.rb, line 141
def parse_enum_cases(content)
  case_regexp = /case (?<name>.+)$/
  all_matches(content, case_regexp).map do |match|
    unqualify(match[:name])
  end
end
parse_extension(content, container_types) click to toggle source
# File lib/api_diff/swift_interface_parser.rb, line 75
def parse_extension(content, container_types)
  name = content.match(/extension ([\w\.]+)/)[1]

  base_type = api.class(fully_qualified_name: qualified_name(name, container_types))
  base_type ||= api.struct(fully_qualified_name: qualified_name(name, container_types))
  base_type ||= api.interface(fully_qualified_name: qualified_name(name, container_types))
  base_type ||= api.enum(fully_qualified_name: qualified_name(name, container_types))
  raise Error.new "Unable to find base type for extension `#{name}`" if base_type.nil?
  base_type.parents.append(*parse_parents(content)).uniq!
  base_type.properties.append(*parse_properties(content)).uniq!
  base_type.functions.append(*parse_functions(content)).uniq!

  parse_nested_types(content, [*container_types, name])
end
parse_functions(content) click to toggle source
# File lib/api_diff/swift_interface_parser.rb, line 128
def parse_functions(content)
  method_regexp = /(?<signature>(?<static>static )?((func (?<name>[^\s\(]+))|(?<init>init))\s?\((?<params>.*)\).*?)(-> (?<return_type>.+))?$/
  all_matches(content, method_regexp).map do |match|
    Function.new(
      name: (match[:name] || match[:init]),
      signature: strip_internal_parameter_names(unqualify(match[:signature])).strip,
      return_type: unqualify(match[:return_type]),
      static: !match[:static].nil?,
      constructor: (not match[:init].nil?)
    )
  end
end
parse_nested_types(outer_content, container_types) click to toggle source
# File lib/api_diff/swift_interface_parser.rb, line 103
def parse_nested_types(outer_content, container_types)
  # remove first and last line and un-indent the inner lines
  inner_content = outer_content.split("\n")[1..-2].map { |l| l.gsub(/^\s{,2}/, "") }.join("\n")

  parse_blocks(inner_content, container_types)
end
parse_one_line_extensions(content) click to toggle source
# File lib/api_diff/swift_interface_parser.rb, line 32
def parse_one_line_extensions(content)
  one_line_extensions = content.scan(/^extension.+\{\}$/)
  one_line_extensions.each do |extension|
    parse_extension(extension, [])
  end
end
parse_parents(content) click to toggle source
# File lib/api_diff/swift_interface_parser.rb, line 110
def parse_parents(content)
  parents_match = content.match(/\A.+?: (.+?) \{\}?$/)
  return [] if parents_match.nil?
  parents_match[1].split(",").map { |p| unqualify(p.strip) }
end
parse_properties(content) click to toggle source
# File lib/api_diff/swift_interface_parser.rb, line 116
def parse_properties(content)
  property_regexp = /(public )?(?<static>static )?(?<varlet>var|let) (?<name>\w+): (?<type>[^\s]+)( {\s+(?<get>get)?\s+(?<set>set)?\s*})?/m
  all_matches(content, property_regexp).map do |match|
    Property.new(
      name: match[:name],
      type: unqualify(match[:type]),
      writable: (match[:varlet] == "var" && (match[:get] == nil || match[:set] != nil)),
      static: !match[:static].nil?
    )
  end
end
parse_ptotocol(protocol_content, container_types) click to toggle source
# File lib/api_diff/swift_interface_parser.rb, line 63
def parse_ptotocol(protocol_content, container_types)
  name = protocol_content.match(/public protocol (\w+)/)[1]

  protocol = Interface.new(name, qualified_name(name, container_types))
  protocol.parents = parse_parents(protocol_content)
  protocol.properties = parse_properties(protocol_content)
  protocol.functions = parse_functions(protocol_content)
  api.interfaces << protocol

  parse_nested_types(protocol_content, [*container_types, name])
end
parse_struct(struct_content, container_types) click to toggle source
# File lib/api_diff/swift_interface_parser.rb, line 51
def parse_struct(struct_content, container_types)
  name = struct_content.match(/public struct (\w+)/)[1]

  struct = Struct.new(name, qualified_name(name, container_types))
  struct.parents = parse_parents(struct_content)
  struct.properties = parse_properties(struct_content)
  struct.functions = parse_functions(struct_content)
  api.structs << struct

  parse_nested_types(struct_content, [*container_types, name])
end
qualified_name(name, container_types) click to toggle source
# File lib/api_diff/swift_interface_parser.rb, line 148
def qualified_name(name, container_types)
  [*container_types, name].join(".")
end
strip_internal_parameter_names(signature) click to toggle source
# File lib/api_diff/swift_interface_parser.rb, line 152
def strip_internal_parameter_names(signature)
  signature.gsub(/(\w+)\s\w+:/, "\\1:")
end