class GraphQLSwiftGen

Constants

BUILTIN_SCALARS
DEFAULT_SCALAR
RESERVED_WORDS

From: developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html

SCHEMA_ERB
TYPE_ERB
VERSION

Attributes

import_graphql_support[R]
scalars[R]
schema[R]
schema_name[R]
script_name[R]

Public Class Methods

new(schema, custom_scalars: [], nest_under:, script_name: 'graphql_swift_gen gem', import_graphql_support: false) click to toggle source
# File codegen/lib/graphql_swift_gen.rb, line 9
def initialize(schema, custom_scalars: [], nest_under:, script_name: 'graphql_swift_gen gem', import_graphql_support: false)
  @schema = schema
  @schema_name = nest_under
  @script_name = script_name
  @scalars = (BUILTIN_SCALARS + custom_scalars).reduce({}) { |hash, scalar| hash[scalar.type_name] = scalar; hash }
  @scalars.default_proc = ->(hash, key) { DEFAULT_SCALAR }
  @import_graphql_support = import_graphql_support
end

Private Class Methods

erb_for(template_filename) click to toggle source
# File codegen/lib/graphql_swift_gen.rb, line 43
def erb_for(template_filename)
  path = File.expand_path("../graphql_swift_gen/templates/#{template_filename}", __FILE__)
  erb = ERB.new(File.read(path))
  erb.filename = path
  erb
end

Public Instance Methods

generate() click to toggle source
# File codegen/lib/graphql_swift_gen.rb, line 29
def generate
  output = {}
  output["#{schema_name}.swift"] = generate_schema_file
  schema.types.reject{ |type| type.name.start_with?('__') || type.scalar? }.each do |type|
    output["#{schema_name}/#{type.name}.swift"] = generate_type(type)
  end
  output
end
save(path) click to toggle source
# File codegen/lib/graphql_swift_gen.rb, line 18
def save(path)
  output = generate
  begin
    Dir.mkdir("#{path}/#{schema_name}")
  rescue Errno::EEXIST
  end
  output.each do |relative_path, file_contents|
    File.write("#{path}/#{relative_path}", file_contents)
  end
end

Private Instance Methods

deserialize_value_code(field_name, expr, type, untyped: true) click to toggle source
# File codegen/lib/graphql_swift_gen.rb, line 190
  def deserialize_value_code(field_name, expr, type, untyped: true)
    statements = ""

    if untyped
      json_type = swift_json_type(type.unwrap_non_null, non_null: true)
      statements << "if #{expr} is NSNull { return nil }\n" unless type.non_null?
      statements << <<-SWIFT
        guard let value = #{expr} as? #{json_type} else {
          throw SchemaViolationError(type: type(of: self), field: fieldName, value: fieldValue)
        }
      SWIFT
      expr = 'value'
    end
    type = type.unwrap_non_null

    statements << "return " + case type.kind
    when 'SCALAR'
      scalars[type.name].deserialize_expr(expr)
    when 'LIST'
      rethrow = "try " if %w(OBJECT INTERFACE UNION).include?(type.unwrap.kind)
      "#{rethrow}#{expr}.map { #{deserialize_value_code(field_name, '$0', type.of_type, untyped: !type.of_type.non_null?)} }"
    when 'OBJECT'
      "try #{type.name}(fields: #{expr})"
    when 'INTERFACE', 'UNION'
      "try Unknown#{type.name}.create(fields: #{expr})"
    when 'ENUM'
      "#{escape_reserved_word(type.name)}(rawValue: #{expr}) ?? .unknownValue"
    else
      raise NotImplementedError, "Unexpected #{type.kind} argument type"
    end
  end
escape_reserved_word(word) click to toggle source
# File codegen/lib/graphql_swift_gen.rb, line 99
def escape_reserved_word(word)
  return word unless RESERVED_WORDS.include?(word)
  return "`#{word}`"
end
generate_append_objects_code(expr, type, non_null: false) click to toggle source
# File codegen/lib/graphql_swift_gen.rb, line 235
def generate_append_objects_code(expr, type, non_null: false)
  if type.non_null?
    non_null = true
    type = type.of_type
  end
  unless non_null
    return "if let value = #{expr} {\n#{generate_append_objects_code('value', type, non_null: true)}\n}"
  end
  return "#{expr}.forEach {\n#{generate_append_objects_code('$0', type.of_type)}\n}" if type.list?

  abstract_response = type.object? ? expr : "#{expr} as! GraphQL.AbstractResponse"
  "response.append(#{abstract_response})\n" \
    "response.append(contentsOf: #{expr}.childResponseObjectMap())"
end
generate_build_input_code(expr, type, wrap: true) click to toggle source
# File codegen/lib/graphql_swift_gen.rb, line 172
def generate_build_input_code(expr, type, wrap: true)
  case type.kind
  when 'SCALAR'
    scalars[type.name].serialize_expr(expr)
  when 'ENUM'
    "\\(#{expr}.rawValue)"
  when 'LIST'
    map_block = generate_build_input_code('$0', type.of_type.unwrap_non_null)
    map_code = map_block == '$0' ? expr : "#{expr}.map{ \"#{map_block}\" }"
    elements = "#{map_code}.joined(separator: \",\")"
    "[\\(#{elements})]"
  when 'INPUT_OBJECT'
    "\\(#{expr}.serialize())"
  else
    raise NotImplementedError, "Unexpected #{type.kind} argument type"
  end
end
generate_schema_file() click to toggle source
# File codegen/lib/graphql_swift_gen.rb, line 104
def generate_schema_file
  reformat(SCHEMA_ERB.result(binding))
end
generate_type(type) click to toggle source
# File codegen/lib/graphql_swift_gen.rb, line 108
def generate_type(type)
  reformat(TYPE_ERB.result(binding))
end
reformat(code) click to toggle source
# File codegen/lib/graphql_swift_gen.rb, line 112
def reformat(code)
  Reformatter.new(indent: "\t").reformat(code)
end
swift_arg_defs(field) click to toggle source
# File codegen/lib/graphql_swift_gen.rb, line 222
def swift_arg_defs(field)
  defs = ["aliasSuffix: String? = nil"]
  field.args.each do |arg|
    arg_def = "#{escape_reserved_word(arg.name)}: #{swift_input_type(arg.type)}"
    arg_def << " = nil" unless arg.type.non_null?
    defs << arg_def
  end
  if field.subfields?
    defs << "_ subfields: (#{field.type.unwrap.name}Query) -> Void"
  end
  defs.join(', ')
end
swift_attributes(deprecatable) click to toggle source
# File codegen/lib/graphql_swift_gen.rb, line 250
def swift_attributes(deprecatable)
  return unless deprecatable.deprecated?
  if deprecatable.deprecation_reason
    message_argument = ", message:#{deprecatable.deprecation_reason.inspect}"
  end
  "@available(*, deprecated#{message_argument})"
end
swift_input_type(type, non_null: false) click to toggle source
# File codegen/lib/graphql_swift_gen.rb, line 116
def swift_input_type(type, non_null: false)
  code = case type.kind
  when 'NON_NULL'
    return swift_input_type(type.of_type, non_null: true)
  when 'SCALAR'
    scalars[type.name].swift_type
  when 'LIST'
    "[#{swift_input_type(type.of_type, non_null: true)}]"
  when 'INPUT_OBJECT', 'ENUM'
    type.name
  else
    raise NotImplementedError, "Unhandled #{type.kind} input type"
  end
  code += "?" unless non_null
  code
end
swift_json_type(type, non_null: false) click to toggle source
# File codegen/lib/graphql_swift_gen.rb, line 133
def swift_json_type(type, non_null: false)
  if !non_null && !type.non_null?
    return 'Any'
  end
  case type.kind
  when "NON_NULL"
    swift_json_type(type.of_type, non_null: true)
  when 'SCALAR'
    scalars[type.name].json_type
  when 'OBJECT', 'INTERFACE', 'UNION'
    "[String: Any]"
  when 'LIST'
    "[#{swift_json_type(type.of_type)}]"
  when 'ENUM'
    'String'
  else
    raise NotImplementedError, "Unexpected #{type.kind} response type"
  end
end
swift_output_type(type, non_null: false) click to toggle source
# File codegen/lib/graphql_swift_gen.rb, line 153
def swift_output_type(type, non_null: false)
  code = case type.kind
  when 'NON_NULL'
    return swift_output_type(type.of_type, non_null: true)
  when 'SCALAR'
    scalars[type.name].swift_type
  when 'LIST'
    "[#{swift_output_type(type.of_type)}]"
  when 'OBJECT', 'ENUM'
    "#{schema_name}.#{type.name}"
  when 'INTERFACE', 'UNION'
    type.name
  else
    raise NotImplementedError, "Unhandled #{type.kind} response type"
  end
  code += "?" unless non_null
  code
end