--- /dev/null
+#!/usr/bin/env ruby
+
+require 'optparse'
+
+CURRENT_PATH = File.expand_path(File.dirname(__FILE__))
+FALLBACK_PATH = File.join(CURRENT_PATH, "..", "build-xcode", "Debug", "include", "libMultiMarkdown", "libMultiMarkdown.h")
+
+options = {:mode => :nsenum}
+OptionParser.new do |parser|
+ parser.banner = "Usage: #{$0} [options] path/to/libMultiMarkdown.h"
+
+ parser.separator ""
+ parser.separator "Without an input path, the script uses this default relative project location:"
+ parser.separator "../build-xcode/Debug/include/libMultiMarkdown/libMultiMarkdown.h"
+ parser.separator ""
+
+ parser.on("-h", "--help", "Prints this help") do
+ puts parser
+ exit
+ end
+
+ parser.on("-m", "--mode [MODE]", [:swift, :nsenum],
+ "Select generator:",
+ " nsenum Generates Objective-C NS_ENUM wrappers. (Default)",
+ " swift Generates Swift enum descriptions.") do |mode|
+ options[:mode] = mode
+ end
+
+ parser.on("-o", "--output [PATH]", String,
+ "Write output to file instead of STDOUT. (Optional)") do |path|
+ options[:outpath] = path
+ end
+end.parse!
+
+
+################################################################################
+## Types to perform the conversion
+################################################################################
+
+
+module CustomStringConvertible
+ def extension
+ indentation = " "
+ cases = case_descriptions
+ .map { |line| indentation + line }
+ .join("\n")
+
+ return %Q{
+extension #{type_name}: CustomStringConvertible {
+ public var description: String {
+ switch self {
+#{cases}
+ }
+ }
+}}
+ end
+
+ private
+
+ def case_descriptions
+ cases.map { |c| CustomStringConvertible.description_for(self.type_name, c) }
+ end
+
+ def indented_case_descriptions(indent)
+ case_descriptions
+ .map { |line| indent + line }
+ .join("\n")
+ end
+
+ def self.description_for(type_name, enum_case)
+ case_name = case_only(enum_case)
+ swift_case_name = case_name.camelize(lowercase_first: true)
+ %Q{case .#{swift_case_name}: return "#{type_name}.#{swift_case_name}"}
+ end
+
+ def self.case_only(line)
+ line[/\w+/]
+ end
+end
+
+class String
+ def camelize(lowercase_first: false)
+ self
+ .split('_')
+ .map.with_index { |part, i|
+ if lowercase_first && i == 0
+ part.downcase
+ else
+ part.capitalize
+ end }
+ .join
+ end
+end
+
+module NSEnum
+ def type_name
+ type.camelize
+ end
+
+ def self.type_names(type)
+ return "MMD6#{type.camelize}", type.camelize
+ end
+
+ def ns_enum
+ type_name, swift_type_name = NSEnum.type_names(type)
+ ns_enum_cases = cases
+ .map { |line| NSEnum.case(type_name, line) }
+ .join("\n")
+
+ %Q{typedef NS_ENUM(NSUInteger, #{type_name}) {
+#{ns_enum_cases}
+} NS_SWIFT_NAME(#{swift_type_name});}
+ end
+
+ def self.case(type_name, line)
+ if /(?<indent>\s*)(?<casename>\w+)(?<remainder>.*)/ =~ line
+ return %Q{#{indent}#{type_name}#{casename.camelize} = #{casename},}
+ end
+ return line
+ end
+end
+
+class Enum
+ include CustomStringConvertible
+ include NSEnum
+
+ attr_accessor :type, :cases
+
+ def initialize(type)
+ @type = type
+ @cases = []
+ end
+
+ def <<(line)
+ return if !Enum.is_enum_case(line)
+ cases << line
+ end
+
+ def self.is_enum_case(line)
+ return false if line.strip.empty?
+ return false if line.include?("}")
+ return false if line.include?("{")
+ return true
+ end
+end
+
+class Enums
+ attr_reader :enums
+
+ def initialize(stream)
+ lines = stream.readlines
+ @enums = parse_enums(lines)
+ end
+
+ def ns_enums
+ @enums.map(&:ns_enum)
+ end
+
+ def swift_descriptions
+ @enums.map(&:extension)
+ end
+
+ private
+
+ def parse_enums(lines)
+ enums = []
+ enumbuffer = nil
+ lines.each do |line|
+ type = line[/^\s*enum (\w+)\s*\{/, 1]
+ if !type.nil?
+ enumbuffer = Enum.new(type)
+ elsif !enumbuffer.nil?
+ if line.start_with?("}")
+ enums << enumbuffer
+ enumbuffer = nil
+ else
+ enumbuffer << line
+ end
+ else
+ # nop; discard line
+ end
+ end
+ return enums
+ end
+end
+
+def file_stream_from_argv
+ path = ARGV.shift
+ return nil if path.nil?
+ File.open(path, "r")
+end
+
+def fallback_stream
+ return nil unless File.exists?(FALLBACK_PATH)
+ File.open(FALLBACK_PATH, "r")
+end
+
+
+################################################################################
+## Script execution itself
+################################################################################
+
+
+input = file_stream_from_argv || fallback_stream
+if input.nil?
+ puts "Failed to read `#{FALLBACK_PATH}`"
+ exit -1
+end
+enums = Enums.new(input)
+input.close
+
+
+result = if options[:mode] == :nsenum
+ enums.ns_enums.join("\n\n")
+ elsif options[:mode] == :swift
+ enums.swift_descriptions.join("\n")
+ else
+ puts "Illegal mode: #{options[:mode]}"
+ exit -1
+ end
+
+
+output = if options[:outpath].nil?
+ $stdout
+ else
+ File.open(options[:outpath], "w")
+ end
+output.puts result
+output.close