defmodule Unicode.Transform.Generator do
@moduledoc """
Generate Elixir source files from CLDR
XML files the define Unicode transforms.
"""
@source_dir "transforms"
@destination_dir "lib/transforms"
@base_module "Unicode.Transform"
# @unavailable_names [Generate, Parser, Utils]
# @builtin_names [AnyNFC, AnyNFD, AnyNFKC, AnyNFKC, AnyLower, AnyUpper, AnyTitle]
@default_files "*.xml"
def generate(files \\ @default_files, options \\ []) do
source_directory = Keyword.get(options, :source_dir, @source_dir)
destination_directory = Keyword.get(options, :destination_dir, @destination_dir)
transforms =
source_directory
|> Path.join(files)
|> Path.wildcard()
Enum.map(transforms, &generate_module(&1, destination_directory))
end
def generate_module(file, destination_directory) do
%{rules: rules, source: source, target: target, direction: direction, alias: alias} =
Unicode.Transform.Utils.file(file)
generated_code = generate_code(rules, source, target, direction, alias)
basename = Path.basename(file, ".xml") |> String.replace("-", "_") |> String.downcase()
file = Path.join(destination_directory, [basename, ".ex"])
File.write!(file, generated_code)
file
end
def generate_code(rules, source, target, direction, alias) do
[generate_header(source, target, direction, alias), generate_rules(rules), generate_footer()]
|> :erlang.iolist_to_binary()
|> Code.format_string!()
end
def generate_header(source, target, direction, alias) do
[
"defmodule",
" ",
@base_module,
".",
module_name(source, target),
" ",
"do",
"\n",
"use ",
@base_module,
"\n",
"\n",
"# This file is generated. Manual changes are not recommended",
"\n",
"# Source: #{source}",
"\n",
"# Target: #{target}",
"\n",
"# Transform direction: #{direction}",
"\n",
"# Transform alias: #{alias}",
"\n",
"\n"
]
end
defp generate_rules(rules) do
rules
|> Unicode.Transform.Parser.parse()
|> Enum.map(&generate_rule/1)
end
defp generate_rule(rule) do
Unicode.Transform.Rule.to_forward_code(rule)
end
defp generate_footer() do
["end", "\n"]
end
defp module_name(source, target) do
String.capitalize(source) <> String.capitalize(target)
end
end