diff --git a/README.md b/README.md index 96cf42a..019d464 100644 --- a/README.md +++ b/README.md @@ -70,11 +70,7 @@ module Domain.Definitions exposing (..) import Json.Decode as Decode exposing - ( float - , int - , string - , list - , succeed + ( succeed , fail , map , maybe @@ -95,10 +91,6 @@ import Json.Decode.Pipeline import Json.Encode as Encode exposing ( Value - , float - , int - , string - , list , object ) @@ -198,10 +190,78 @@ that have references across files, e.g. } ``` -then the corresponding Elm file, `Domain.Circle`, will import the +then the corresponding Elm file, `Domain/Circle.elm`, will import the definitions (types, encoders and decoders) from the other Elm module, `Domain/Definitions.elm`. +``` elm +module Domain.Circle exposing (..) + +-- Schema for a circle shape + +import Json.Decode as Decode + exposing + ( succeed + , fail + , map + , maybe + , field + , at + , andThen + , oneOf + , nullable + , Decoder + ) +import Json.Decode.Pipeline + exposing + ( decode + , required + , optional + , custom + ) +import Json.Encode as Encode + exposing + ( Value + , object + ) +import Domain.Definitions + + +type alias Circle = + { center : Domain.Definitions.Point + , color : Maybe Domain.Definitions.Color + , radius : Float + } + + +circleDecoder : Decoder Circle +circleDecoder = + decode Circle + |> required "center" Domain.Definitions.pointDecoder + |> optional "color" (Decode.string |> andThen Domain.Definitions.colorDecoder |> maybe) Nothing + |> required "radius" Decode.float + + +encodeCircle : Circle -> Value +encodeCircle circle = + let + center = + [ ( "center", Domain.Definitions.encodePoint circle.center ) ] + + color = + case circle.color of + Just color -> + [ ( "color", Domain.Definitions.encodeColor color ) ] + + Nothing -> + [] + + radius = + [ ( "radius", Encode.float circle.radius ) ] + in + object <| center ++ color ++ radius +``` + ## Tests Run the standard mix task diff --git a/examples/example-output-elm-code/Domain/Circle.elm b/examples/example-output-elm-code/Domain/Circle.elm index 4abee7e..4c30175 100644 --- a/examples/example-output-elm-code/Domain/Circle.elm +++ b/examples/example-output-elm-code/Domain/Circle.elm @@ -4,11 +4,7 @@ module Domain.Circle exposing (..) import Json.Decode as Decode exposing - ( float - , int - , string - , list - , succeed + ( succeed , fail , map , maybe @@ -29,53 +25,44 @@ import Json.Decode.Pipeline import Json.Encode as Encode exposing ( Value - , float - , int - , string - , list , object ) import Domain.Definitions - exposing - ( Color - , colorDecoder - , encodeColor - , Point - , pointDecoder - , encodePoint - ) -type alias Root = - { center : Point - , color : Maybe Color +type alias Circle = + { center : Domain.Definitions.Point + , color : Maybe Domain.Definitions.Color , radius : Float } -rootDecoder : Decoder Root -rootDecoder = - decode Root - |> required "center" pointDecoder - |> optional "color" (Decode.string |> andThen colorDecoder |> maybe) Nothing +circleDecoder : Decoder Circle +circleDecoder = + decode Circle + |> required "center" Domain.Definitions.pointDecoder + |> optional "color" (Decode.string |> andThen Domain.Definitions.colorDecoder |> maybe) Nothing |> required "radius" Decode.float -encodeRoot : Root -> Value -encodeRoot root = +encodeCircle : Circle -> Value +encodeCircle circle = let center = - [ ( "center", encodePoint root.center ) ] + [ ( "center", Domain.Definitions.encodePoint circle.center ) ] color = - case root.color of + case circle.color of Just color -> - [ ( "color", encodeColor color ) ] + [ ( "color", Domain.Definitions.encodeColor color ) ] Nothing -> [] radius = - [ ( "radius", Encode.float root.radius ) ] + [ ( "radius", Encode.float circle.radius ) ] in - object <| center ++ color ++ radius + object <| + center + ++ color + ++ radius diff --git a/examples/example-output-elm-code/Domain/Definitions.elm b/examples/example-output-elm-code/Domain/Definitions.elm index 7a29044..6fdaaac 100644 --- a/examples/example-output-elm-code/Domain/Definitions.elm +++ b/examples/example-output-elm-code/Domain/Definitions.elm @@ -4,11 +4,7 @@ module Domain.Definitions exposing (..) import Json.Decode as Decode exposing - ( float - , int - , string - , list - , succeed + ( succeed , fail , map , maybe @@ -29,10 +25,6 @@ import Json.Decode.Pipeline import Json.Encode as Encode exposing ( Value - , float - , int - , string - , list , object ) @@ -101,4 +93,5 @@ encodePoint point = y = [ ( "y", Encode.float point.y ) ] in - object <| x ++ y + object <| + x ++ y diff --git a/lib/js2e.ex b/lib/js2e.ex index 6d00a28..12c1d1c 100644 --- a/lib/js2e.ex +++ b/lib/js2e.ex @@ -105,8 +105,8 @@ defmodule JS2E do @spec generate([String.t], String.t) :: :ok def generate(json_schema_paths, module_name) do - schema_dict = Parser.parse_schema_files(json_schema_paths) - printed_schemas = Printer.print_schemas(schema_dict, module_name) + schema_dict = Parser.parse_schema_files(json_schema_paths, module_name) + printed_schemas = Printer.print_schemas(schema_dict) printed_schemas |> Enum.each(fn{file_path, file_content} -> diff --git a/lib/parser.ex b/lib/parser.ex index dfdd5f2..c1c6a2b 100644 --- a/lib/parser.ex +++ b/lib/parser.ex @@ -19,27 +19,27 @@ defmodule JS2E.Parser do "http://json-schema.org/draft-04/schema" ] - @spec parse_schema_files([String.t]) :: Types.schemaDictionary - def parse_schema_files(json_schema_paths) do + @spec parse_schema_files([String.t], String.t) :: Types.schemaDictionary + def parse_schema_files(json_schema_paths, module_name) do json_schema_paths |> Enum.reduce(%{}, fn (json_schema_path, schema_dict) -> json_schema_path - |> parse_schema_file + |> parse_schema_file(module_name) |> Map.merge(schema_dict) end) end - @spec parse_schema_file(String.t) :: Types.schemaDictionary - def parse_schema_file(json_schema_path) do + @spec parse_schema_file(String.t, String.t) :: Types.schemaDictionary + def parse_schema_file(json_schema_path, module_name) do json_schema_path |> File.read! |> Poison.decode! - |> parse_schema + |> parse_schema(module_name) end - @spec parse_schema(map) :: Types.schemaDictionary - def parse_schema(schema_root_node) do + @spec parse_schema(map, String.t) :: Types.schemaDictionary + def parse_schema(schema_root_node, module_name) do if not supported_schema_version?(schema_root_node) do exit(:bad_version) @@ -64,7 +64,7 @@ defmodule JS2E.Parser do |> Map.merge(root, handle_conflict) %{to_string(schema_id) => - SchemaDefinition.new(schema_id, title, description, types)} + SchemaDefinition.new(schema_id, title, module_name, description, types)} end @spec parse_schema_id(any) :: {:ok, URI.t} | {:error, String.t} diff --git a/lib/printer.ex b/lib/printer.ex index 43e7f3c..658257b 100644 --- a/lib/printer.ex +++ b/lib/printer.ex @@ -5,20 +5,20 @@ defmodule JS2E.Printer do """ require Logger + import JS2E.Printers.Util alias JS2E.{TypePath, Types} alias JS2E.Printers.{ArrayPrinter, EnumPrinter, ObjectPrinter, AllOfPrinter, AnyOfPrinter, OneOfPrinter, PrimitivePrinter, UnionPrinter, PreamblePrinter, - TypeReferencePrinter, Util} + TypeReferencePrinter} alias JS2E.Types.{PrimitiveType, TypeReference, SchemaDefinition} - @primitive_types ["boolean", "null", "string", "number", "integer"] + @spec print_schemas(Types.schemaDictionary) :: Types.fileDictionary + def print_schemas(schema_dict) do - @spec print_schemas(Types.schemaDictionary, String.t) :: Types.fileDictionary - def print_schemas(schema_dict, module_name \\ "") do - - create_file_path = fn (module_name, schema_def) -> + create_file_path = fn (schema_def) -> title = schema_def.title + module_name = schema_def.module if module_name != "" do "./#{module_name}/#{title}.elm" @@ -29,39 +29,37 @@ defmodule JS2E.Printer do schema_dict |> Enum.reduce(%{}, fn ({_id, schema_def}, acc) -> - file_path = create_file_path.(module_name, schema_def) - output_file = print_schema(schema_def, schema_dict, module_name) + file_path = create_file_path.(schema_def) + output_file = print_schema(schema_def, schema_dict) acc |> Map.put(file_path, output_file) end) end - @spec print_schema( - SchemaDefinition.t, - Types.schemaDictionary, - String.t - ) :: String.t - def print_schema(%SchemaDefinition{id: _id, - title: _title, - description: _description, - types: type_dict} = schema_def, - schema_dict, module_name \\ "") do + @spec print_schema(SchemaDefinition.t, Types.schemaDictionary) :: String.t + def print_schema(schema_def, schema_dict) do preamble = schema_def - |> PreamblePrinter.print_preamble(schema_dict, module_name) + |> PreamblePrinter.print_preamble(schema_dict) - values = type_dict - |> filter_aliases - |> Enum.sort(&(&1.name < &2.name)) + type_dict = schema_def.types - types = values - |> Enum.map_join("\n\n", &(print_type(&1, type_dict, schema_dict))) + values = + type_dict + |> filter_aliases + |> Enum.sort(&(&1.name < &2.name)) - decoders = values - |> Enum.map_join("\n\n", &(print_decoder(&1, type_dict, schema_dict))) + types = + values + |> Enum.map_join("\n\n", &(print_type(&1, schema_def, schema_dict))) - encoders = values - |> Enum.map_join("\n\n", &(print_encoder(&1, type_dict, schema_dict))) + decoders = + values + |> Enum.map_join("\n\n", &(print_decoder(&1, schema_def, schema_dict))) + + encoders = + values + |> Enum.map_join("\n\n", &(print_encoder(&1, schema_def, schema_dict))) """ #{String.trim(preamble)} @@ -88,10 +86,10 @@ defmodule JS2E.Printer do @spec print_type( Types.typeDefinition, - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: String.t - def print_type(type_def, type_dict, schema_dict) do + def print_type(type_def, schema_def, schema_dict) do type_to_printer_dict = %{ "ArrayType" => &ArrayPrinter.print_type/3, @@ -105,10 +103,10 @@ defmodule JS2E.Printer do "TypeReference" => &TypeReferencePrinter.print_type/3 } - struct_name = Util.get_string_name(type_def) + struct_name = get_string_name(type_def) if Map.has_key?(type_to_printer_dict, struct_name) do - type_to_printer_dict[struct_name].(type_def, type_dict, schema_dict) + type_to_printer_dict[struct_name].(type_def, schema_def, schema_dict) else Logger.error "Error(print_type) unknown type: #{inspect struct_name}" end @@ -116,10 +114,10 @@ defmodule JS2E.Printer do @spec print_decoder( Types.typeDefinition, - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: String.t - def print_decoder(type_def, type_dict, schema_dict) do + def print_decoder(type_def, schema_def, schema_dict) do type_to_printer_dict = %{ "ArrayType" => &ArrayPrinter.print_decoder/3, @@ -133,11 +131,11 @@ defmodule JS2E.Printer do "TypeReference" => &TypeReferencePrinter.print_decoder/3 } - struct_name = Util.get_string_name(type_def) + struct_name = get_string_name(type_def) if Map.has_key?(type_to_printer_dict, struct_name) do printer = type_to_printer_dict[struct_name] - printer.(type_def, type_dict, schema_dict) + printer.(type_def, schema_def, schema_dict) else Logger.error "Error(print_decoder) unknown type: #{inspect struct_name}" end @@ -145,10 +143,10 @@ defmodule JS2E.Printer do @spec print_encoder( Types.typeDefinition, - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: String.t - def print_encoder(type_def, type_dict, schema_dict) do + def print_encoder(type_def, schema_def, schema_dict) do type_to_printer_dict = %{ "ArrayType" => &ArrayPrinter.print_encoder/3, @@ -162,62 +160,85 @@ defmodule JS2E.Printer do "TypeReference" => &TypeReferencePrinter.print_encoder/3 } - struct_name = Util.get_string_name(type_def) + struct_name = get_string_name(type_def) if Map.has_key?(type_to_printer_dict, struct_name) do printer = type_to_printer_dict[struct_name] - printer.(type_def, type_dict, schema_dict) + printer.(type_def, schema_def, schema_dict) else Logger.error "Error(print_encoder) unknown type: #{inspect struct_name}" end end - @spec resolve_type( + @spec resolve_type!( Types.typeIdentifier, - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary - ) :: Types.typeDefinition - def resolve_type(identifier, type_dict, schema_dict) do - Logger.debug "Looking up '#{identifier}' in #{inspect type_dict}" + ) :: {Types.typeDefinition, SchemaDefinition.t} + def resolve_type!(identifier, schema_def, schema_dict) do + Logger.debug "Looking up '#{inspect identifier}' in #{inspect schema_def}" + type_dict = schema_def.types + + {resolved_type, resolved_schema_def} = cond do - type = cond do - identifier in @primitive_types -> - PrimitiveType.new(identifier, identifier, identifier) + identifier in ["string", "number", "integer", "boolean"] -> + resolve_primitive_identifier(identifier, schema_def) TypePath.type_path?(identifier) -> - type_dict[TypePath.to_string(identifier)] + resolve_type_path_identifier(identifier, schema_def) URI.parse(identifier).scheme != nil -> - schema_id = determine_schema_id(identifier) - schema = schema_dict[to_string(schema_id)] - schema_type_dict = schema.types - - if to_string(identifier) == schema_id do - schema_type_dict["#"] - else - schema_type_dict[to_string(identifier)] - end + resolve_uri_identifier(identifier, schema_dict) true -> - Logger.error("Could not resolve '#{identifier}'") - nil + raise "Could not resolve identifier: '#{identifier}'" end - Logger.debug("Found type: #{inspect type}") + Logger.debug("Resolved type: #{inspect resolved_type}") + Logger.debug("Resolved schema: #{inspect resolved_schema_def}") - if type.__struct__ == TypeReference do - resolve_type(type.path, type_dict, schema_dict) + if resolved_type.__struct__ == TypeReference do + resolve_type!(resolved_type.path, resolved_schema_def, schema_dict) else - type + {resolved_type, resolved_schema_def} end end - @spec determine_schema_id(URI.t) :: String.t - defp determine_schema_id(identifier) do - identifier - |> URI.parse - |> URI.merge("#") - |> to_string + @spec resolve_primitive_identifier(String.t, SchemaDefinition.t) + :: {Types.typeDefinition, SchemaDefinition.t} + defp resolve_primitive_identifier(identifier, schema_def) do + primitive_type = PrimitiveType.new(identifier, identifier, identifier) + {primitive_type, schema_def} + end + + @spec resolve_type_path_identifier(TypePath.t, SchemaDefinition.t) + :: {Types.typeDefinition, SchemaDefinition.t} + defp resolve_type_path_identifier(identifier, schema_def) do + type_dict = schema_def.types + resolved_type = type_dict[TypePath.to_string(identifier)] + {resolved_type, schema_def} + + end + + @spec resolve_uri_identifier(String.t, Types.schemaDictionary) + :: {Types.typeDefinition, SchemaDefinition.t} + defp resolve_uri_identifier(identifier, schema_dict) do + + determine_schema_id = fn identifier -> + identifier |> URI.parse |> URI.merge("#") |> to_string + end + + schema_id = determine_schema_id.(identifier) + schema_def = schema_dict[schema_id] + type_dict = schema_def.types + + resolved_type = if to_string(identifier) == schema_id do + type_dict["#"] + else + type_dict[to_string(identifier)] + end + + {resolved_type, schema_def} end end diff --git a/lib/printers/all_of_printer.ex b/lib/printers/all_of_printer.ex index 2dda2bc..23aa156 100644 --- a/lib/printers/all_of_printer.ex +++ b/lib/printers/all_of_printer.ex @@ -11,7 +11,7 @@ defmodule JS2E.Printers.AllOfPrinter do require Elixir.{EEx, Logger} import JS2E.Printers.Util alias JS2E.{Printer, TypePath, Types} - alias JS2E.Types.AllOfType + alias JS2E.Types.{AllOfType, SchemaDefinition} EEx.function_from_file(:defp, :type_template, @type_location, [:type_name, :fields]) @@ -24,39 +24,39 @@ defmodule JS2E.Printers.AllOfPrinter do @spec print_type( Types.typeDefinition, - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: String.t def print_type(%AllOfType{name: name, path: _path, - types: types}, type_dict, schema_dict) do + types: types}, schema_def, schema_dict) do type_name = upcase_first name - fields = create_type_fields(types, type_dict, schema_dict) + fields = create_type_fields(types, schema_def, schema_dict) type_template(type_name, fields) end @spec create_type_fields( [TypePath.t], - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: [map] - defp create_type_fields(types, type_dict, schema_dict) do - types |> Enum.map(&(create_type_field(&1, type_dict, schema_dict))) + defp create_type_fields(types, schema_def, schema_dict) do + types |> Enum.map(&(create_type_field(&1, schema_def, schema_dict))) end @spec create_type_field( TypePath.t, - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: map - defp create_type_field(type_path, type_dict, schema_dict) do + defp create_type_field(type_path, schema_def, schema_dict) do field_type = type_path - |> Printer.resolve_type(type_dict, schema_dict) - |> create_type_name + |> Printer.resolve_type!(schema_def, schema_dict) + |> create_type_name(schema_def) field_name = downcase_first field_type @@ -64,75 +64,50 @@ defmodule JS2E.Printers.AllOfPrinter do type: field_type} end - - @spec create_type_name(Types.typeDefinition) :: String.t - defp create_type_name(property_type) do - - if primitive_type?(property_type) do - property_type_value = property_type.type - - case property_type_value do - "integer" -> - "Int" - - "number" -> - "Float" - - "boolean" -> - "Bool" - - _ -> - upcase_first property_type_value - end - - else - property_type_name = upcase_first property_type.name - end - end - @spec print_decoder( Types.typeDefinition, - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: String.t def print_decoder(%AllOfType{name: name, path: _path, types: type_paths}, - type_dict, schema_dict) do + schema_def, schema_dict) do decoder_name = "#{name}Decoder" type_name = upcase_first name - clauses = create_decoder_clauses(type_paths, type_dict, schema_dict) + clauses = create_decoder_clauses(type_paths, schema_def, schema_dict) decoder_template(decoder_name, type_name, clauses) end @spec create_decoder_clauses( [TypePath.t], - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: [map] - defp create_decoder_clauses(type_paths, type_dict, schema_dict) do + defp create_decoder_clauses(type_paths, schema_def, schema_dict) do type_paths |> Enum.map(fn type_path -> - create_decoder_property(type_path, type_dict, schema_dict) + create_decoder_property(type_path, schema_def, schema_dict) end) end @spec create_decoder_property( TypePath.t, - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: map - defp create_decoder_property(type_path, type_dict, schema_dict) do + defp create_decoder_property(type_path, schema_def, schema_dict) do - property_type = + {property_type, resolved_schema_def} = type_path - |> Printer.resolve_type(type_dict, schema_dict) + |> Printer.resolve_type!(schema_def, schema_dict) property_name = property_type.name - decoder_name = create_decoder_name(property_type) + decoder_name = create_decoder_name( + {property_type, resolved_schema_def}, schema_def) cond do union_type?(property_type) || one_of_type?(property_type) -> @@ -141,7 +116,7 @@ defmodule JS2E.Printers.AllOfPrinter do enum_type?(property_type) -> property_type_decoder = property_type.type - |> determine_primitive_type_decoder() + |> determine_primitive_type_decoder!() create_decoder_enum_clause( property_name, property_type_decoder, decoder_name) @@ -151,34 +126,6 @@ defmodule JS2E.Printers.AllOfPrinter do end end - @spec determine_primitive_type_decoder(String.t) :: String.t - defp determine_primitive_type_decoder(property_type_value) do - case property_type_value do - "integer" -> - "Decode.int" - - "number" -> - "Decode.float" - - "boolean" -> - "Decode.bool" - - _ -> - "Decode.#{property_type_value}" - end - end - - @spec create_decoder_name(Types.typeDefinition) :: String.t - defp create_decoder_name(property_type) do - - if primitive_type?(property_type) do - determine_primitive_type_decoder(property_type.type) - else - property_type_name = property_type.name - "#{property_type_name}Decoder" - end - end - defp create_decoder_union_clause(property_name, decoder_name) do %{property_name: property_name, decoder_name: decoder_name} @@ -199,36 +146,37 @@ defmodule JS2E.Printers.AllOfPrinter do @spec print_encoder( Types.typeDefinition, - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: String.t def print_encoder(%AllOfType{name: name, path: _path, types: type_paths}, - type_dict, schema_dict) do + schema_def, schema_dict) do type_name = upcase_first name encoder_name = "encode#{type_name}" argument_name = downcase_first type_name - properties = create_encoder_properties(type_paths, type_dict, schema_dict) + properties = create_encoder_properties(type_paths, schema_def, schema_dict) template = encoder_template(encoder_name, type_name, argument_name, properties) trim_newlines(template) end - defp create_encoder_properties(type_paths, type_dict, schema_dict) do + defp create_encoder_properties(type_paths, schema_def, schema_dict) do - type_paths - |> Enum.map(fn type_path -> - Printer.resolve_type(type_path, type_dict, schema_dict) + type_paths + |> Enum.map(fn type_path -> + Printer.resolve_type!(type_path, schema_def, schema_dict) + end) + |> Enum.reduce([], fn ({resolved_property, resolved_schema}, properties) -> + encoder_name = create_encoder_name( + {resolved_property, resolved_schema}, schema_def) + updated_property = Map.put(resolved_property, :encoder_name, encoder_name) + properties ++ [updated_property] end) - |> Enum.reduce([], fn (property, properties) -> - encoder_name = create_encoder_name(property) - updated_property = Map.put(property, :encoder_name, encoder_name) - properties ++ [updated_property] - end) end end diff --git a/lib/printers/any_of_printer.ex b/lib/printers/any_of_printer.ex index b80aeb1..e24556d 100644 --- a/lib/printers/any_of_printer.ex +++ b/lib/printers/any_of_printer.ex @@ -11,7 +11,7 @@ defmodule JS2E.Printers.AnyOfPrinter do require Elixir.{EEx, Logger} import JS2E.Printers.Util alias JS2E.{Printer, TypePath, Types} - alias JS2E.Types.AnyOfType + alias JS2E.Types.{AnyOfType, SchemaDefinition} EEx.function_from_file(:defp, :type_template, @type_location, [:type_name, :fields]) @@ -24,39 +24,39 @@ defmodule JS2E.Printers.AnyOfPrinter do @spec print_type( Types.typeDefinition, - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: String.t def print_type(%AnyOfType{name: name, path: _path, - types: types}, type_dict, schema_dict) do + types: types}, schema_def, schema_dict) do type_name = upcase_first name - fields = create_type_fields(types, type_dict, schema_dict) + fields = create_type_fields(types, schema_def, schema_dict) type_template(type_name, fields) end @spec create_type_fields( [TypePath.t], - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: [map] - defp create_type_fields(types, type_dict, schema_dict) do + defp create_type_fields(types, schema_def, schema_dict) do types - |> Enum.map(&(create_type_field(&1, type_dict, schema_dict))) + |> Enum.map(&(create_type_field(&1, schema_def, schema_dict))) end @spec create_type_field( TypePath.t, - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: map - defp create_type_field(type_path, type_dict, schema_dict) do + defp create_type_field(type_path, schema_def, schema_dict) do field_type = type_path - |> Printer.resolve_type(type_dict, schema_dict) + |> Printer.resolve_type!(schema_def, schema_dict) |> create_type_name field_name = downcase_first field_type @@ -65,26 +65,11 @@ defmodule JS2E.Printers.AnyOfPrinter do type: "Maybe #{field_type}"} end - @spec create_type_name(Types.typeDefinition) :: String.t - defp create_type_name(property_type) do + @spec create_type_name({Types.typeDefinition, SchemaDefinition.t}) :: String.t + defp create_type_name({property_type, schema_def}) do if primitive_type?(property_type) do - property_type_value = property_type.type - - case property_type_value do - "integer" -> - "Int" - - "number" -> - "Float" - - "boolean" -> - "Bool" - - _ -> - upcase_first property_type_value - end - + determine_primitive_type!(property_type.type) else property_type_name = upcase_first property_type.name end @@ -92,44 +77,44 @@ defmodule JS2E.Printers.AnyOfPrinter do @spec print_decoder( Types.typeDefinition, - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: String.t def print_decoder(%AnyOfType{name: name, path: _path, types: type_paths}, - type_dict, schema_dict) do + schema_def, schema_dict) do decoder_name = "#{name}Decoder" type_name = upcase_first name - clauses = create_decoder_clauses(type_paths, type_dict, schema_dict) + clauses = create_decoder_clauses(type_paths, schema_def, schema_dict) decoder_template(decoder_name, type_name, clauses) end @spec create_decoder_clauses( [TypePath.t], - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: [map] - defp create_decoder_clauses(type_paths, type_dict, schema_dict) do + defp create_decoder_clauses(type_paths, schema_def, schema_dict) do type_paths |> Enum.map(fn type_path -> - create_decoder_property(type_path, type_dict, schema_dict) + create_decoder_property(type_path, schema_def, schema_dict) end) end @spec create_decoder_property( TypePath.t, - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: map - defp create_decoder_property(type_path, type_dict, schema_dict) do + defp create_decoder_property(type_path, schema_def, schema_dict) do - property_type = + {property_type, resolved_schema_def} = type_path - |> Printer.resolve_type(type_dict, schema_dict) + |> Printer.resolve_type!(schema_def, schema_dict) property_name = property_type.name decoder_name = create_decoder_name(property_type) @@ -141,7 +126,7 @@ defmodule JS2E.Printers.AnyOfPrinter do enum_type?(property_type) -> property_type_decoder = property_type.type - |> determine_primitive_type_decoder() + |> determine_primitive_type_decoder!() create_decoder_enum_clause( property_name, property_type_decoder, decoder_name) @@ -151,28 +136,11 @@ defmodule JS2E.Printers.AnyOfPrinter do end end - @spec determine_primitive_type_decoder(String.t) :: String.t - defp determine_primitive_type_decoder(property_type_value) do - case property_type_value do - "integer" -> - "Decode.int" - - "number" -> - "Decode.float" - - "boolean" -> - "Decode.bool" - - _ -> - "Decode.#{property_type_value}" - end - end - @spec create_decoder_name(Types.typeDefinition) :: String.t defp create_decoder_name(property_type) do if primitive_type?(property_type) do - determine_primitive_type_decoder(property_type.type) + determine_primitive_type_decoder!(property_type.type) else property_type_name = property_type.name "#{property_type_name}Decoder" @@ -199,36 +167,42 @@ defmodule JS2E.Printers.AnyOfPrinter do @spec print_encoder( Types.typeDefinition, - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: String.t def print_encoder(%AnyOfType{name: name, path: _path, types: type_paths}, - type_dict, schema_dict) do + schema_def, schema_dict) do type_name = upcase_first name encoder_name = "encode#{type_name}" argument_name = downcase_first type_name - properties = create_encoder_properties(type_paths, type_dict, schema_dict) + properties = create_encoder_properties(type_paths, schema_def, schema_dict) template = encoder_template(encoder_name, type_name, argument_name, properties) trim_newlines(template) end - defp create_encoder_properties(type_paths, type_dict, schema_dict) do + @spec create_encoder_properties( + [TypePath.t], + SchemaDefinition.t, + Types.schemaDictionary + ) :: Types.typeDefinition + defp create_encoder_properties(type_paths, schema_def, schema_dict) do - type_paths - |> Enum.map(fn type_path -> - Printer.resolve_type(type_path, type_dict, schema_dict) + type_paths + |> Enum.map(fn type_path -> + Printer.resolve_type!(type_path, schema_def, schema_dict) + end) + |> Enum.reduce([], fn ({resolved_property, resolved_schema}, properties) -> + encoder_name = create_encoder_name( + {resolved_property, resolved_schema}, schema_def) + updated_property = Map.put(resolved_property, :encoder_name, encoder_name) + properties ++ [updated_property] end) - |> Enum.reduce([], fn (property, properties) -> - encoder_name = create_encoder_name(property) - updated_property = Map.put(property, :encoder_name, encoder_name) - properties ++ [updated_property] - end) end end diff --git a/lib/printers/array_printer.ex b/lib/printers/array_printer.ex index 079372a..cda7672 100644 --- a/lib/printers/array_printer.ex +++ b/lib/printers/array_printer.ex @@ -20,27 +20,27 @@ defmodule JS2E.Printers.ArrayPrinter do @spec print_type( Types.typeDefinition, - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: String.t def print_type(%ArrayType{name: _name, path: _path, - items: _items_path}, _type_dict, _schema_dict) do + items: _items_path}, _schema_def, _schema_dict) do "" end @spec print_decoder( Types.typeDefinition, - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: String.t def print_decoder(%ArrayType{name: name, path: _path, - items: items_path}, type_dict, schema_dict) do + items: items_path}, schema_def, schema_dict) do - items_type = + {items_type, resolved_schema_def} = items_path - |> Printer.resolve_type(type_dict, schema_dict) + |> Printer.resolve_type!(schema_def, schema_dict) items_type_name = determine_type_name(items_type) items_decoder_name = determine_decoder_name(items_type) @@ -54,22 +54,7 @@ defmodule JS2E.Printers.ArrayPrinter do defp determine_decoder_name(items_type) do if primitive_type?(items_type) do - items_type_value = items_type.type - - case items_type do - "integer" -> - "Decode.int" - - "number" -> - "Decode.float" - - "boolean" -> - "Decode.bool" - - _ -> - "Decode.#{downcase_first items_type_value}" - end - + determine_primitive_type_decoder!(items_type.type) else items_type_name = items_type.name @@ -85,22 +70,7 @@ defmodule JS2E.Printers.ArrayPrinter do defp determine_type_name(items_type) do if primitive_type?(items_type) do - items_type_value = items_type.type - - case items_type_value do - "integer" -> - "Int" - - "number" -> - "Float" - - "boolean" -> - "Bool" - - _ -> - upcase_first items_type_value - end - + determine_primitive_type!(items_type.type) else items_type_name = items_type.name @@ -115,16 +85,15 @@ defmodule JS2E.Printers.ArrayPrinter do @spec print_encoder( Types.typeDefinition, - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: String.t - def print_encoder(%ArrayType{name: name, - path: _path, - items: items_path}, type_dict, schema_dict) do + def print_encoder(%ArrayType{name: name, path: _path, items: items_path}, + schema_def, schema_dict) do - items_type = + {items_type, resolved_schema_def} = items_path - |> Printer.resolve_type(type_dict, schema_dict) + |> Printer.resolve_type!(schema_def, schema_dict) items_type_name = determine_type_name(items_type) items_encoder_name = determine_encoder_name(items_type) @@ -138,22 +107,7 @@ defmodule JS2E.Printers.ArrayPrinter do defp determine_encoder_name(items_type) do if primitive_type?(items_type) do - items_type_value = items_type.type - - case items_type_value do - "integer" -> - "Encode.int" - - "number" -> - "Encode.float" - - "boolean" -> - "Encode.bool" - - true -> - "Encode.#{downcase_first items_type_value}" - end - + determine_primitive_type_encoder!(items_type.type) else items_type_name = items_type.name diff --git a/lib/printers/enum_printer.ex b/lib/printers/enum_printer.ex index de0c2d3..d1e4025 100644 --- a/lib/printers/enum_printer.ex +++ b/lib/printers/enum_printer.ex @@ -11,7 +11,7 @@ defmodule JS2E.Printers.EnumPrinter do require Elixir.{EEx, Logger} import JS2E.Printers.Util alias JS2E.Types - alias JS2E.Types.EnumType + alias JS2E.Types.{EnumType, SchemaDefinition} EEx.function_from_file(:defp, :type_template, @type_location, [:type_name, :clauses]) @@ -24,13 +24,13 @@ defmodule JS2E.Printers.EnumPrinter do @spec print_type( Types.typeDefinition, - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: String.t def print_type(%EnumType{name: name, path: _path, type: type, - values: values}, _type_dict, _schema_dict) do + values: values}, _schema_def, _schema_dict) do type_name = upcase_first name clauses = values |> Enum.map(&(create_elm_value!(&1, type))) @@ -40,39 +40,22 @@ defmodule JS2E.Printers.EnumPrinter do @spec print_decoder( Types.typeDefinition, - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: String.t def print_decoder(%EnumType{name: name, path: _path, type: type, - values: values}, _type_dict, _schema_dict) do + values: values}, _schema_def, _schema_dict) do decoder_name = "#{downcase_first name}Decoder" - argument_type = create_type_value type + argument_type = determine_primitive_type!(type) decoder_type = upcase_first name cases = create_decoder_cases(values, type) decoder_template(decoder_name, decoder_type, name, argument_type, cases) end - @spec create_type_value(String.t) :: String.t - defp create_type_value(type) do - case type do - "string" -> - "String" - - "integer" -> - "Int" - - "number" -> - "Float" - - _ -> - raise "Unknown or unsupported enum type: #{type}" - end - end - @spec create_decoder_cases([String.t], String.t) :: [map] defp create_decoder_cases(values, type) do @@ -104,13 +87,13 @@ defmodule JS2E.Printers.EnumPrinter do @spec print_encoder( Types.typeDefinition, - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: String.t def print_encoder(%EnumType{name: name, path: _path, type: type, - values: values}, _type_dict, _schema_dict) do + values: values}, _schema_def, _schema_dict) do argument_type = upcase_first name encoder_name = "encode#{argument_type}" diff --git a/lib/printers/object_printer.ex b/lib/printers/object_printer.ex index 449536f..bd7cb17 100644 --- a/lib/printers/object_printer.ex +++ b/lib/printers/object_printer.ex @@ -11,7 +11,7 @@ defmodule JS2E.Printers.ObjectPrinter do require Elixir.{EEx, Logger} import JS2E.Printers.Util alias JS2E.{Printer, Types} - alias JS2E.Types.ObjectType + alias JS2E.Types.{ObjectType, SchemaDefinition} EEx.function_from_file(:defp, :type_template, @type_location, [:type_name, :fields]) @@ -24,21 +24,25 @@ defmodule JS2E.Printers.ObjectPrinter do @spec print_type( Types.typeDefinition, - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: String.t def print_type(%ObjectType{name: name, path: _path, properties: properties, - required: required}, type_dict, schema_dict) do + required: required}, schema_def, schema_dict) do - type_name = if name == "#" do - "Root" + type_name = (if name == "#" do + if schema_def.title != nil do + upcase_first schema_def.title + else + "Root" + end else upcase_first name - end + end) - fields = create_type_fields(properties, required, type_dict, schema_dict) + fields = create_type_fields(properties, required, schema_def, schema_dict) type_template(type_name, fields) end @@ -46,100 +50,66 @@ defmodule JS2E.Printers.ObjectPrinter do @spec create_type_fields( Types.propertyDictionary, [String.t], - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: [map] - defp create_type_fields(properties, required, type_dict, schema_dict) do + defp create_type_fields(properties, required, schema_def, schema_dict) do properties - |> Enum.map(&(create_type_field(&1, required, type_dict, schema_dict))) + |> Enum.map(&(create_type_field(&1, required, schema_def, schema_dict))) end @spec create_type_field( {String.t, String.t}, [String.t], - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: map defp create_type_field({property_name, property_path}, - required, type_dict, schema_dict) do + required, schema_def, schema_dict) do - field_type = - property_path - |> Printer.resolve_type(type_dict, schema_dict) - |> create_type_name - |> (fn (field_name) -> + check_if_maybe = fn (field_name, property_name, required) -> if property_name in required do field_name else "Maybe #{field_name}" end - end).() + end + + field_type = + property_path + |> Printer.resolve_type!(schema_def, schema_dict) + |> create_type_name(schema_def) + |> check_if_maybe.(property_name, required) %{name: property_name, type: field_type} end - @spec create_type_name(Types.typeDefinition) :: String.t - defp create_type_name(property_type) do - - if primitive_type?(property_type) do - property_type_value = property_type.type - - case property_type_value do - "string" -> - "String" - - "integer" -> - "Int" - - "number" -> - "Float" - - "boolean" -> - "Bool" - - _ -> - upcase_first property_type_value - end - - else - - property_type_name = property_type.name - if property_type_name == "#" do - "Root" - else - upcase_first property_type_name - end - - end - end - @spec print_decoder( Types.typeDefinition, - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: String.t def print_decoder(%ObjectType{name: name, path: _path, properties: properties, required: required}, - type_dict, schema_dict) do - - decoder_name = if name == "#" do - "rootDecoder" - else - "#{downcase_first name}Decoder" - end + schema_def, schema_dict) do - type_name = if name == "#" do - "Root" + type_name = (if name == "#" do + if schema_def.title != nil do + upcase_first schema_def.title + else + "Root" + end else upcase_first name - end + end) + decoder_name = "#{downcase_first type_name}Decoder" clauses = create_decoder_properties( - properties, required, type_dict, schema_dict) + properties, required, schema_def, schema_dict) decoder_template(decoder_name, type_name, clauses) end @@ -147,31 +117,34 @@ defmodule JS2E.Printers.ObjectPrinter do @spec create_decoder_properties( Types.propertyDictionary, [String.t], - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: [map] - defp create_decoder_properties(properties, required, type_dict, schema_dict) do + defp create_decoder_properties(properties, required, + schema_def, schema_dict) do properties |> Enum.map(fn property -> - create_decoder_property(property, required, type_dict, schema_dict) + create_decoder_property(property, required, schema_def, schema_dict) end) end @spec create_decoder_property( {String.t, String.t}, [String.t], - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: map defp create_decoder_property({property_name, property_path}, - required, type_dict, schema_dict) do + required, schema_def, schema_dict) do - property_type = + {property_type, resolved_schema_def} = property_path - |> Printer.resolve_type(type_dict, schema_dict) + |> Printer.resolve_type!(schema_def, schema_dict) + + decoder_name = create_decoder_name( + {property_type, resolved_schema_def}, schema_def) - decoder_name = create_decoder_name(property_type) is_required = property_name in required cond do @@ -191,24 +164,6 @@ defmodule JS2E.Printers.ObjectPrinter do end end - @spec create_decoder_name(Types.typeDefinition) :: String.t - defp create_decoder_name(property_type) do - - if primitive_type?(property_type) do - primitive_type_name = property_type.type - determine_primitive_type_decoder!(primitive_type_name) - else - - property_type_name = property_type.name - if property_type_name == "#" do - "rootDecoder" - else - "#{property_type_name}Decoder" - end - - end - end - defp create_decoder_union_clause(property_name, decoder_name, is_required) do if is_required do %{option: "required", @@ -250,21 +205,31 @@ defmodule JS2E.Printers.ObjectPrinter do @spec print_encoder( Types.typeDefinition, - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: String.t def print_encoder(%ObjectType{name: name, path: _path, properties: properties, required: required}, - type_dict, schema_dict) do + schema_def, schema_dict) do + + type_name = (if name == "#" do + if schema_def.title != nil do + upcase_first schema_def.title + else + "Root" + end + else + upcase_first name + end) - type_name = if name == "#", do: "Root", else: upcase_first name encoder_name = "encode#{type_name}" + argument_name = downcase_first type_name properties = create_encoder_properties(properties, required, - type_dict, schema_dict) + schema_def, schema_dict) template = encoder_template(encoder_name, type_name, argument_name, properties) @@ -274,31 +239,33 @@ defmodule JS2E.Printers.ObjectPrinter do @spec create_encoder_properties( Types.propertyDictionary, [String.t], - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: [map] - defp create_encoder_properties(properties, required, type_dict, schema_dict) do + defp create_encoder_properties(properties, required, + schema_def, schema_dict) do properties |> Enum.map(fn property -> - create_encoder_property(property, required, type_dict, schema_dict) + create_encoder_property(property, required, schema_def, schema_dict) end) end @spec create_encoder_property( {String.t, String.t}, [String.t], - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: map defp create_encoder_property({property_name, property_path}, required, - type_dict, schema_dict) do + schema_def, schema_dict) do - property_type = + {resolved_type, resolved_schema} = property_path - |> Printer.resolve_type(type_dict, schema_dict) + |> Printer.resolve_type!(schema_def, schema_dict) - encoder_name = create_encoder_name(property_type) + encoder_name = create_encoder_name( + {resolved_type, resolved_schema}, schema_def) is_required = property_name in required %{name: property_name, diff --git a/lib/printers/one_of_printer.ex b/lib/printers/one_of_printer.ex index 4e21c2a..165c255 100644 --- a/lib/printers/one_of_printer.ex +++ b/lib/printers/one_of_printer.ex @@ -11,7 +11,7 @@ defmodule JS2E.Printers.OneOfPrinter do require Elixir.{EEx, Logger} import JS2E.Printers.Util alias JS2E.{Printer, TypePath, Types} - alias JS2E.Types.OneOfType + alias JS2E.Types.{OneOfType, SchemaDefinition} EEx.function_from_file(:defp, :type_template, @type_location, [:type_name, :clauses]) @@ -24,15 +24,15 @@ defmodule JS2E.Printers.OneOfPrinter do @spec print_type( Types.typeDefinition, - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: String.t def print_type(%OneOfType{name: name, path: _path, - types: types}, type_dict, schema_dict) do + types: types}, schema_def, schema_dict) do type_name = upcase_first name - clauses = create_type_clauses(types, name, type_dict, schema_dict) + clauses = create_type_clauses(types, name, schema_def, schema_dict) type_template(type_name, clauses) end @@ -40,17 +40,17 @@ defmodule JS2E.Printers.OneOfPrinter do @spec create_type_clauses( [TypePath.t], String.t, - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: [map] - defp create_type_clauses(types, name, type_dict, schema_dict) do + defp create_type_clauses(types, name, schema_def, schema_dict) do type_name = upcase_first name create_type_clause = fn type_path -> - clause_type = + {clause_type, resolved_schema_def} = type_path - |> Printer.resolve_type(type_dict, schema_dict) + |> Printer.resolve_type!(schema_def, schema_dict) type_value = upcase_first clause_type.name @@ -69,31 +69,31 @@ defmodule JS2E.Printers.OneOfPrinter do @spec print_decoder( Types.typeDefinition, - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: String.t def print_decoder(%OneOfType{name: name, path: _path, - types: types}, type_dict, schema_dict) do + types: types}, schema_def, schema_dict) do decoder_name = "#{name}Decoder" decoder_type = upcase_first name - clause_decoders = create_decoder_clauses(types, type_dict, schema_dict) + clause_decoders = create_decoder_clauses(types, schema_def, schema_dict) decoder_template(decoder_name, decoder_type, clause_decoders) end @spec create_decoder_clauses( [TypePath.t], - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: [String.t] - defp create_decoder_clauses(types, type_dict, schema_dict) do + defp create_decoder_clauses(types, schema_def, schema_dict) do create_decoder_clause = fn type_path -> - clause_type = + {clause_type, resolved_schema_def} = type_path - |> Printer.resolve_type(type_dict, schema_dict) + |> Printer.resolve_type!(schema_def, schema_dict) "#{clause_type.name}Decoder" end @@ -104,16 +104,16 @@ defmodule JS2E.Printers.OneOfPrinter do @spec print_encoder( Types.typeDefinition, - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: String.t def print_encoder(%OneOfType{name: name, path: _path, - types: types}, type_dict, schema_dict) do + types: types}, schema_def, schema_dict) do type_name = upcase_first name encoder_name = "encode#{type_name}" - cases = create_encoder_cases(types, name, type_dict, schema_dict) + cases = create_encoder_cases(types, name, schema_def, schema_dict) template = encoder_template(encoder_name, type_name, name, cases) trim_newlines(template) @@ -122,30 +122,30 @@ defmodule JS2E.Printers.OneOfPrinter do @spec create_encoder_cases( [String.t], String.t, - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: [map] - defp create_encoder_cases(types, name, type_dict, schema_dict) do + defp create_encoder_cases(types, name, schema_def, schema_dict) do types |> Enum.map(fn type -> - create_encoder_clause(type, name, type_dict, schema_dict) + create_encoder_clause(type, name, schema_def, schema_dict) end) end @spec create_encoder_clause( String.t, String.t, - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: map - defp create_encoder_clause(type_path, name, type_dict, schema_dict) do + defp create_encoder_clause(type_path, name, schema_def, schema_dict) do type_name = upcase_first name create_type_clause = fn type_path -> - clause_type = + {clause_type, resolved_schema_def} = type_path - |> Printer.resolve_type(type_dict, schema_dict) + |> Printer.resolve_type!(schema_def, schema_dict) argument_name = clause_type.name type_value = upcase_first argument_name diff --git a/lib/printers/preamble_printer.ex b/lib/printers/preamble_printer.ex index 73af594..4bdb06e 100644 --- a/lib/printers/preamble_printer.ex +++ b/lib/printers/preamble_printer.ex @@ -5,7 +5,6 @@ defmodule JS2E.Printers.PreamblePrinter do @templates_location Application.get_env(:js2e, :templates_location) @preamble_location Path.join(@templates_location, "preamble/preamble.elm.eex") - @import_location Path.join(@templates_location, "preamble/import.elm.eex") require Elixir.{EEx, Logger} import JS2E.Printers.Util @@ -13,28 +12,21 @@ defmodule JS2E.Printers.PreamblePrinter do alias JS2E.Types.{TypeReference, SchemaDefinition} EEx.function_from_file(:defp, :preamble_template, @preamble_location, - [:prefix, :title, :description, :imports, :import_template]) + [:prefix, :title, :description, :imports]) - EEx.function_from_file(:defp, :import_template, @import_location, - [:prefix, :import]) - - @spec print_preamble( - SchemaDefinition.t, - Types.schemaDictionary, - String.t) :: String.t - def print_preamble(%SchemaDefinition{id: schema_id, + @spec print_preamble(SchemaDefinition.t, Types.schemaDictionary) :: String.t + def print_preamble(%SchemaDefinition{id: _id, title: title, + module: module_name, description: description, - types: type_dict}, - schema_dict, module_name \\ "") do + types: type_dict} = schema_def, + schema_dict) do prefix = create_prefix(module_name) - imports = - type_dict - |> create_imports(schema_id, schema_dict) + imports = schema_def |> create_imports(schema_dict) - preamble_template(prefix, title, description, imports, &import_template/2) + preamble_template(prefix, title, description, imports) end @spec create_prefix(String.t) :: String.t @@ -47,19 +39,22 @@ defmodule JS2E.Printers.PreamblePrinter do end @spec create_imports( - Types.typeDictionary, - URI.t, + SchemaDefinition.t, Types.schemaDictionary ) :: [map] - defp create_imports(type_dict, schema_id, schema_dict) do + defp create_imports(schema_def, schema_dict) do + schema_id = schema_def.id + type_dict = schema_def.types + type_dict |> get_type_references |> create_dependency_map(schema_id, schema_dict) - |> create_dependencies(type_dict, schema_dict) + |> create_dependencies(schema_def, schema_dict) end @spec get_type_references(Types.typeDictionary) :: [TypeReference.t] defp get_type_references(type_dict) do + type_dict |> Enum.reduce([], fn ({_path, type}, types) -> if get_string_name(type) == "TypeReference" do @@ -110,7 +105,7 @@ defmodule JS2E.Printers.PreamblePrinter do type_ref_schema_def = schema_dict[type_ref_schema_uri] type_refs = if Map.has_key?(dependency_map, type_ref_schema_def.id) do - [type_ref | dependency_map[type_ref_schema_def.id]] + [type_ref | dependency_map[type_ref_schema_def.id]] else [type_ref] end @@ -122,47 +117,18 @@ defmodule JS2E.Printers.PreamblePrinter do @spec create_dependencies( %{required(String.t) => TypeReference.t}, - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary - ) :: [map] - defp create_dependencies(dependency_map, type_dict, schema_dict) do + ) :: [String.t] + defp create_dependencies(dependency_map, schema_def, schema_dict) do dependency_map |> Enum.map(fn{schema_id, type_refs} -> - type_ref_schema = schema_dict[to_string(schema_id)] - type_ref_schema_title = type_ref_schema.title - - type_ref_dependencies = - type_refs - |> Enum.sort(&(&2.name < &1.name)) - |> Enum.map(fn type_ref -> - create_import(type_ref, type_dict, schema_dict) - end) - - %{title: type_ref_schema_title, - dependencies: type_ref_dependencies} + type_ref_schema.title end) end - @spec create_import( - TypeReference.t, - Types.typeDictionary, - Types.schemaDictionary - ) :: map - defp create_import(type_ref, type_dict, schema_dict) do - - type_path = type_ref.path - resolved_type = type_path |> Printer.resolve_type(type_dict, schema_dict) - resolved_type_name = upcase_first resolved_type.name - resolved_decoder_name = "#{resolved_type.name}Decoder" - resolved_encoder_name = "encode#{resolved_type_name}" - - %{type_name: resolved_type_name, - decoder_name: resolved_decoder_name, - encoder_name: resolved_encoder_name} - end - @spec has_relative_path?(URI.t) :: boolean defp has_relative_path?(type_uri) do type_uri.scheme == nil diff --git a/lib/printers/primitive_printer.ex b/lib/printers/primitive_printer.ex index 5ec3f0e..cac02fd 100644 --- a/lib/printers/primitive_printer.ex +++ b/lib/printers/primitive_printer.ex @@ -5,38 +5,38 @@ defmodule JS2E.Printers.PrimitivePrinter do require Logger alias JS2E.Types - alias JS2E.Types.PrimitiveType + alias JS2E.Types.{PrimitiveType, SchemaDefinition} @spec print_type( Types.typeDefinition, - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: String.t def print_type(%PrimitiveType{name: _name, path: _path, - type: _type}, _type_dict, _schema_dict) do + type: _type}, _schema_def, _schema_dict) do "" end @spec print_decoder( Types.typeDefinition, - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: String.t def print_decoder(%PrimitiveType{name: _name, path: _path, - type: _type}, _type_dict, _schema_dict) do + type: _type}, _schema_def, _schema_dict) do "" end @spec print_encoder( Types.typeDefinition, - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: String.t def print_encoder(%PrimitiveType{name: _name, path: _path, - type: _type}, _type_dict, _schema_dict) do + type: _type}, _schema_def, _schema_dict) do "" end diff --git a/lib/printers/type_reference_printer.ex b/lib/printers/type_reference_printer.ex index 57c6994..fffcf2a 100644 --- a/lib/printers/type_reference_printer.ex +++ b/lib/printers/type_reference_printer.ex @@ -5,35 +5,35 @@ defmodule JS2E.Printers.TypeReferencePrinter do require Logger alias JS2E.Types - alias JS2E.Types.TypeReference + alias JS2E.Types.{TypeReference, SchemaDefinition} @spec print_type( Types.typeDefinition, - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: String.t def print_type(%TypeReference{name: _name, - path: _path}, _type_dict, _schema_dict) do + path: _path}, _schema_def, _schema_dict) do "" end @spec print_decoder( Types.typeDefinition, - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: String.t def print_decoder(%TypeReference{name: _name, - path: _path}, _type_dict, _schema_dict) do + path: _path}, _schema_def, _schema_dict) do "" end @spec print_encoder( Types.typeDefinition, - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: String.t def print_encoder(%TypeReference{name: _name, - path: _path}, _type_dict, _schema_dict) do + path: _path}, _schema_def, _schema_dict) do "" end diff --git a/lib/printers/union_printer.ex b/lib/printers/union_printer.ex index 46376f2..ab23391 100644 --- a/lib/printers/union_printer.ex +++ b/lib/printers/union_printer.ex @@ -11,7 +11,7 @@ defmodule JS2E.Printers.UnionPrinter do require Elixir.{EEx, Logger} import JS2E.Printers.Util alias JS2E.{TypePath, Types} - alias JS2E.Types.UnionType + alias JS2E.Types.{UnionType, SchemaDefinition} EEx.function_from_file(:defp, :type_template, @type_location, [:type_name, :clauses]) @@ -24,12 +24,12 @@ defmodule JS2E.Printers.UnionPrinter do @spec print_type( Types.typeDefinition, - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: String.t def print_type(%UnionType{name: name, path: _path, - types: types}, _type_dict, _schema_dict) do + types: types}, _schema_def, _schema_dict) do type_name = upcase_first name clauses = create_type_clauses(types, name) @@ -69,12 +69,12 @@ defmodule JS2E.Printers.UnionPrinter do @spec print_decoder( Types.typeDefinition, - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: String.t def print_decoder(%UnionType{name: name, path: _path, - types: types}, _type_dict, _schema_dict) do + types: types}, _schema_def, _schema_dict) do type_name = upcase_first name nullable? = "null" in types @@ -129,12 +129,12 @@ defmodule JS2E.Printers.UnionPrinter do @spec print_encoder( Types.typeDefinition, - Types.typeDictionary, + SchemaDefinition.t, Types.schemaDictionary ) :: String.t def print_encoder(%UnionType{name: name, path: _path, - types: types}, _type_dict, _schema_dict) do + types: types}, _schema_def, _schema_dict) do type_name = upcase_first name encoder_name = "encode#{type_name}" diff --git a/lib/printers/util.ex b/lib/printers/util.ex index 17ff896..a3f7f80 100644 --- a/lib/printers/util.ex +++ b/lib/printers/util.ex @@ -5,6 +5,8 @@ defmodule JS2E.Printers.Util do alias JS2E.Types + # Indentation, whitespace and casing - start + @indent_size 4 @doc ~S""" @@ -21,6 +23,14 @@ defmodule JS2E.Printers.Util do String.pad_leading("", tabs * @indent_size) end + @doc ~S""" + Remove excessive newlines of a string. + """ + @spec trim_newlines(String.t) :: String.t + def trim_newlines(str) do + String.trim(str) <> "\n" + end + @doc ~S""" Upcases the first letter of a string. @@ -59,32 +69,110 @@ defmodule JS2E.Printers.Util do end end - @doc ~S""" - Remove excessive newlines of a string. - """ - @spec trim_newlines(String.t) :: String.t - def trim_newlines(str) do - String.trim(str) <> "\n" + # Indentation, whitespace and casing - end + + # Printing types - start + + @spec create_type_name( + {Types.typeDefinition, SchemaDefinition.t}, + SchemaDefinition.t + ) :: String.t + def create_type_name({resolved_type, resolved_schema}, context_schema) do + + type_name = (if primitive_type?(resolved_type) do + determine_primitive_type!(resolved_type.type) + else + resolved_type_name = resolved_type.name + if resolved_type_name == "#" do + if resolved_schema.title != nil do + upcase_first resolved_schema.title + else + "Root" + end + else + upcase_first resolved_type_name + end + end) + + if resolved_schema.id != context_schema.id do + qualify_name(type_name, resolved_schema) + else + type_name + end + end @doc ~S""" - Returns the encoder name given a JSON schema type definition. + Converts the following primitive types: "string", "integer", "number", + and "boolean" into their Elm type equivalent. Raises and error otherwise. + + ## Examples + + iex> determine_primitive_type!("string") + "String" + + iex> determine_primitive_type!("integer") + "Int" + + iex> determine_primitive_type!("number") + "Float" + + iex> determine_primitive_type!("boolean") + "Bool" + + iex> determine_primitive_type!("array") + ** (RuntimeError) Unknown or unsupported primitive type: 'array' + """ - @spec create_encoder_name(Types.typeDefinition) :: String.t - def create_encoder_name(type) do + @spec determine_primitive_type!(String.t) :: String.t + def determine_primitive_type!(type_name) do + case type_name do + "string" -> + "String" - if primitive_type?(type) do - primitive_type_name = type.type - determine_primitive_type_encoder!(primitive_type_name) - else + "integer" -> + "Int" + + "number" -> + "Float" + + "boolean" -> + "Bool" + + _ -> + raise "Unknown or unsupported primitive type: '#{type_name}'" + end + end + + # Printing types - end + + # Printing decoders - start - type_name = type.name + @spec create_decoder_name( + {Types.typeDefinition, SchemaDefinition.t}, + SchemaDefinition.t + ) :: String.t + def create_decoder_name({resolved_type, resolved_schema}, context_schema) do + + decoder_name = (if primitive_type?(resolved_type) do + determine_primitive_type_decoder!(resolved_type.type) + else + type_name = resolved_type.name if type_name == "#" do - "encodeRoot" + if resolved_schema.title != nil do + "#{downcase_first resolved_schema.title}Decoder" + else + "rootDecoder" + end else - "encode#{upcase_first type_name}" + "#{type_name}Decoder" end + end) + if resolved_schema.id != context_schema.id do + qualify_name(decoder_name, resolved_schema) + else + decoder_name end end @@ -131,6 +219,41 @@ defmodule JS2E.Printers.Util do end end + # Printing decoders - end + + # Printing encoders - start + + @doc ~S""" + Returns the encoder name given a JSON schema type definition. + """ + @spec create_encoder_name( + {Types.typeDefinition, SchemaDefinition.t}, + SchemaDefinition.t + ) :: String.t + def create_encoder_name({resolved_type, resolved_schema}, context_schema) do + + encoder_name = (if primitive_type?(resolved_type) do + determine_primitive_type_encoder!(resolved_type.type) + else + type_name = resolved_type.name + if type_name == "#" do + if resolved_schema.title != nil do + "encode#{upcase_first resolved_schema.title}" + else + "encodeRoot" + end + else + "encode#{upcase_first type_name}" + end + end) + + if resolved_schema.id != context_schema.id do + qualify_name(encoder_name, resolved_schema) + else + encoder_name + end + end + @doc ~S""" Converts the following primitive types: "string", "integer", "number", "boolean", and "null" into their Elm encoder equivalent. Raises an error @@ -181,45 +304,16 @@ defmodule JS2E.Printers.Util do end end - @doc ~S""" - Converts the following primitive types: "string", "integer", "number", - and "boolean" into their Elm type equivalent. Raises and error otherwise. - - ## Examples - - iex> determine_primitive_type!("string") - "String" - - iex> determine_primitive_type!("integer") - "Int" - - iex> determine_primitive_type!("number") - "Float" + # Printing encoders - end - iex> determine_primitive_type!("boolean") - "Bool" - - iex> determine_primitive_type!("array") - ** (RuntimeError) Unknown or unsupported primitive type: 'array' - - """ - @spec determine_primitive_type!(String.t) :: String.t - def determine_primitive_type!(type_name) do - case type_name do - "string" -> - "String" + # Printing utils - start - "integer" -> - "Int" - - "number" -> - "Float" - - "boolean" -> - "Bool" - - _ -> - raise "Unknown or unsupported primitive type: '#{type_name}'" + @spec qualify_name(String.t, SchemaDefinition.t) :: String.t + def qualify_name(type_name, schema_def) do + if schema_def.title do + "#{schema_def.module}.#{schema_def.title}.#{type_name}" + else + "#{schema_def.module}.#{type_name}" end end @@ -228,11 +322,11 @@ defmodule JS2E.Printers.Util do ## Examples - iex> primitive_type = %JS2E.Types.PrimitiveType{name: "foo", - ...> path: ["#","foo"], - ...> type: "string"} - ...> get_string_name(primitive_type) - "PrimitiveType" + iex> primitive_type = %JS2E.Types.PrimitiveType{name: "foo", + ...> path: ["#","foo"], + ...> type: "string"} + ...> get_string_name(primitive_type) + "PrimitiveType" """ @spec get_string_name(struct) :: String.t @@ -243,6 +337,10 @@ defmodule JS2E.Printers.Util do |> List.last end + # Printing utils - end + + # Predicate functions - start + @spec primitive_type?(struct) :: boolean def primitive_type?(type) do get_string_name(type) == "PrimitiveType" @@ -263,4 +361,6 @@ defmodule JS2E.Printers.Util do get_string_name(type) == "UnionType" end + # Predicate functions - end + end diff --git a/lib/types.ex b/lib/types.ex index 8021bf1..300d6dd 100644 --- a/lib/types.ex +++ b/lib/types.ex @@ -21,7 +21,7 @@ defmodule JS2E.Types do TypeReference.t ) - @type typeIdentifier :: (TypePath.t | URI.t | String.t) + @type typeIdentifier :: (String.t | TypePath.t | URI.t) @type propertyDictionary :: %{required(String.t) => typeIdentifier} @type typeDictionary :: %{required(String.t) => typeDefinition} @type schemaDictionary :: %{required(String.t) => SchemaDefinition.t} diff --git a/lib/types/schema_definition.ex b/lib/types/schema_definition.ex index 51886fb..d8d4619 100644 --- a/lib/types/schema_definition.ex +++ b/lib/types/schema_definition.ex @@ -7,15 +7,17 @@ defmodule JS2E.Types.SchemaDefinition do @type t :: %__MODULE__{id: URI.t, title: String.t, + module: String.t, description: String.t, types: Types.typeDictionary} - defstruct [:id, :title, :description, :types] + defstruct [:id, :title, :module, :description, :types] - @spec new(URI.t, String.t, String.t, Types.typeDictionary) :: t - def new(id, title, description, types) do + @spec new(URI.t, String.t, String.t, String.t, Types.typeDictionary) :: t + def new(id, title, module, description, types) do %__MODULE__{id: id, title: title, + module: module, description: description, types: types} end diff --git a/mix.exs b/mix.exs index 1bd67cb..a338db5 100644 --- a/mix.exs +++ b/mix.exs @@ -4,7 +4,7 @@ defmodule JS2E.Mixfile do def project do [app: :js2e, version: "2.0.0", - elixir: "~> 1.4", + elixir: "~> 1.5", deps: deps(), aliases: aliases(), build_embedded: Mix.env == :prod, diff --git a/priv/templates/preamble/import.elm.eex b/priv/templates/preamble/import.elm.eex deleted file mode 100644 index c38889b..0000000 --- a/priv/templates/preamble/import.elm.eex +++ /dev/null @@ -1,14 +0,0 @@ -import <%= prefix %><%= import.title %> - exposing - ( <%= for {dep, idx} <- Enum.with_index(import.dependencies) do %><%# - %><%= if idx == 0 do %><%# - %><%= dep.type_name %> - , <%= dep.decoder_name %> - , <%= dep.encoder_name %><%# - %><% else %> - , <%= dep.type_name %> - , <%= dep.decoder_name %> - , <%= dep.encoder_name %><%# - %><% end %><%# - %><% end %> - ) diff --git a/priv/templates/preamble/preamble.elm.eex b/priv/templates/preamble/preamble.elm.eex index 69f335d..d57a684 100644 --- a/priv/templates/preamble/preamble.elm.eex +++ b/priv/templates/preamble/preamble.elm.eex @@ -4,12 +4,7 @@ module <%= prefix %><%= title %> exposing (..) import Json.Decode as Decode exposing - ( float - , int - , bool - , string - , list - , succeed + ( succeed , fail , map , maybe @@ -30,13 +25,9 @@ import Json.Decode.Pipeline import Json.Encode as Encode exposing ( Value - , float - , int - , bool - , string - , list , object ) -<%= for import <- imports do %><%# -%><%= import_template(prefix, import) %><%# +<%= for import_name <- imports do %><%# +%>import <%= prefix %><%= import_name %><%# %><% end %> + diff --git a/test/parsers/definitions_parser_test.exs b/test/parsers/definitions_parser_test.exs index b7e7a83..3b62942 100644 --- a/test/parsers/definitions_parser_test.exs +++ b/test/parsers/definitions_parser_test.exs @@ -10,7 +10,7 @@ defmodule JS2ETest.Parsers.DefinitionsParser do ~S""" { "$schema": "http://json-schema.org/draft-04/schema#", - "title": "", + "title": "Root", "id": "http://example.com/root.json", "type": "array", "items": { "$ref": "#/definitions/positiveInteger" }, @@ -24,7 +24,7 @@ defmodule JS2ETest.Parsers.DefinitionsParser do } """ |> Poison.decode!() - |> Parser.parse_schema() + |> Parser.parse_schema("Domain") expected_root_type_reference = %ArrayType{ name: "#", @@ -43,7 +43,8 @@ defmodule JS2ETest.Parsers.DefinitionsParser do assert schema_dict == %{ "http://example.com/root.json" => %SchemaDefinition{ - title: "", + title: "Root", + module: "Domain", id: URI.parse("http://example.com/root.json"), types: %{ "#" => expected_root_type_reference, diff --git a/test/parsers/internal_references_test.exs b/test/parsers/internal_references_test.exs index 0c11765..235b57c 100644 --- a/test/parsers/internal_references_test.exs +++ b/test/parsers/internal_references_test.exs @@ -41,7 +41,7 @@ defmodule JS2ETest.Parsers.InternalReferences do } """ |> Poison.decode!() - |> Parser.parse_schema() + |> Parser.parse_schema("Domain") expected_root_type_reference = %TypeReference{ name: "#", @@ -72,6 +72,7 @@ defmodule JS2ETest.Parsers.InternalReferences do %SchemaDefinition{ description: "Demonstrates the different types of internal references", title: "Internal references", + module: "Domain", id: URI.parse("http://example.com/root.json"), types: %{ "#" => expected_root_type_reference, diff --git a/test/printers/all_of_printer_test.exs b/test/printers/all_of_printer_test.exs index 537ec29..d9d3f0b 100644 --- a/test/printers/all_of_printer_test.exs +++ b/test/printers/all_of_printer_test.exs @@ -2,7 +2,7 @@ defmodule JS2ETest.Printers.AllOfPrinter do use ExUnit.Case require Logger - alias JS2E.Types.{AllOfType, ObjectType, TypeReference} + alias JS2E.Types.{AllOfType, ObjectType, TypeReference, SchemaDefinition} alias JS2E.Printers.AllOfPrinter test "print 'all of' type value" do @@ -31,6 +31,13 @@ defmodule JS2ETest.Printers.AllOfPrinter do "radius" => ["#", "properties", "radius"]}} } + schema_def = %SchemaDefinition{ + description: "Test schema", + id: URI.parse("http://example.com/test.json"), + title: "Test", + module: "Domain", + types: type_dict} + all_of_type_program = %AllOfType{ name: "shape", @@ -38,7 +45,7 @@ defmodule JS2ETest.Printers.AllOfPrinter do types: [["#", "shape", "0"], ["#", "shape", "1"]] } - |> AllOfPrinter.print_type(type_dict, %{}) + |> AllOfPrinter.print_type(schema_def, %{}) expected_all_of_type_program = """ @@ -69,6 +76,13 @@ defmodule JS2ETest.Printers.AllOfPrinter do "radius" => ["#", "properties", "radius"]}} } + schema_def = %SchemaDefinition{ + description: "Test schema", + id: URI.parse("http://example.com/test.json"), + title: "Test", + module: "Domain", + types: type_dict} + all_of_decoder_program = %AllOfType{ name: "shape", @@ -76,7 +90,7 @@ defmodule JS2ETest.Printers.AllOfPrinter do types: [["#", "definitions", "square"], ["#", "definitions", "circle"]] } - |> AllOfPrinter.print_decoder(type_dict, %{}) + |> AllOfPrinter.print_decoder(schema_def, %{}) expected_all_of_decoder_program = """ @@ -108,6 +122,13 @@ defmodule JS2ETest.Printers.AllOfPrinter do "radius" => ["#", "properties", "radius"]}} } + schema_def = %SchemaDefinition{ + description: "Test schema", + id: URI.parse("http://example.com/test.json"), + title: "Test", + module: "Domain", + types: type_dict} + all_of_encoder_program = %AllOfType{ name: "shape", @@ -115,7 +136,7 @@ defmodule JS2ETest.Printers.AllOfPrinter do types: [["#", "definitions", "square"], ["#", "definitions", "circle"]] } - |> AllOfPrinter.print_encoder(type_dict, %{}) + |> AllOfPrinter.print_encoder(schema_def, %{}) expected_all_of_encoder_program = """ diff --git a/test/printers/any_of_printer_test.exs b/test/printers/any_of_printer_test.exs index 454fa3a..22200fd 100644 --- a/test/printers/any_of_printer_test.exs +++ b/test/printers/any_of_printer_test.exs @@ -2,7 +2,7 @@ defmodule JS2ETest.Printers.AnyOfPrinter do use ExUnit.Case require Logger - alias JS2E.Types.{AnyOfType, ObjectType, TypeReference} + alias JS2E.Types.{AnyOfType, ObjectType, TypeReference, SchemaDefinition} alias JS2E.Printers.AnyOfPrinter test "print 'any of' type value" do @@ -31,6 +31,13 @@ defmodule JS2ETest.Printers.AnyOfPrinter do "radius" => ["#", "properties", "radius"]}} } + schema_def = %SchemaDefinition{ + description: "Test schema", + id: URI.parse("http://example.com/test.json"), + title: "Test", + module: "Domain", + types: type_dict} + any_of_type_program = %AnyOfType{ name: "shape", @@ -38,7 +45,7 @@ defmodule JS2ETest.Printers.AnyOfPrinter do types: [["#", "shape", "0"], ["#", "shape", "1"]] } - |> AnyOfPrinter.print_type(type_dict, %{}) + |> AnyOfPrinter.print_type(schema_def, %{}) expected_any_of_type_program = """ @@ -69,6 +76,13 @@ defmodule JS2ETest.Printers.AnyOfPrinter do "radius" => ["#", "properties", "radius"]}} } + schema_def = %SchemaDefinition{ + description: "Test schema", + id: URI.parse("http://example.com/test.json"), + title: "Test", + module: "Domain", + types: type_dict} + any_of_decoder_program = %AnyOfType{ name: "shape", @@ -76,7 +90,7 @@ defmodule JS2ETest.Printers.AnyOfPrinter do types: [["#", "definitions", "square"], ["#", "definitions", "circle"]] } - |> AnyOfPrinter.print_decoder(type_dict, %{}) + |> AnyOfPrinter.print_decoder(schema_def, %{}) expected_any_of_decoder_program = """ @@ -108,6 +122,13 @@ defmodule JS2ETest.Printers.AnyOfPrinter do "radius" => ["#", "properties", "radius"]}} } + schema_def = %SchemaDefinition{ + description: "Test schema", + id: URI.parse("http://example.com/test.json"), + title: "Test", + module: "Domain", + types: type_dict} + any_of_encoder_program = %AnyOfType{ name: "shape", @@ -115,7 +136,7 @@ defmodule JS2ETest.Printers.AnyOfPrinter do types: [["#", "definitions", "square"], ["#", "definitions", "circle"]] } - |> AnyOfPrinter.print_encoder(type_dict, %{}) + |> AnyOfPrinter.print_encoder(schema_def, %{}) expected_any_of_encoder_program = """ diff --git a/test/printers/array_printer_test.exs b/test/printers/array_printer_test.exs index a6fd46d..ad4f14e 100644 --- a/test/printers/array_printer_test.exs +++ b/test/printers/array_printer_test.exs @@ -2,12 +2,17 @@ defmodule JS2ETest.Printers.ArrayPrinter do use ExUnit.Case require Logger - alias JS2E.Types.{ArrayType, EnumType} + alias JS2E.Types.{ArrayType, EnumType, SchemaDefinition} alias JS2E.Printers.ArrayPrinter test "print array type" do - type_dict = %{} + schema_def = %SchemaDefinition{ + description: "Test schema", + id: URI.parse("http://example.com/test.json"), + title: "Test", + module: "Domain", + types: %{}} array_type_program = %ArrayType{ @@ -15,7 +20,7 @@ defmodule JS2ETest.Printers.ArrayPrinter do path: ["#", "items"], items: ["#", "definitions", "color"] } - |> ArrayPrinter.print_type(type_dict, %{}) + |> ArrayPrinter.print_type(schema_def, %{}) expected_array_type_program = "" @@ -32,13 +37,20 @@ defmodule JS2ETest.Printers.ArrayPrinter do values: ["none", "green", "yellow", "red"]} } + schema_def = %SchemaDefinition{ + description: "Test schema", + id: URI.parse("http://example.com/test.json"), + title: "Test", + module: "Domain", + types: type_dict} + array_decoder_program = %ArrayType{ name: "colors", path: ["#"], items: ["#", "items"] } - |> ArrayPrinter.print_decoder(type_dict, %{}) + |> ArrayPrinter.print_decoder(schema_def, %{}) expected_array_decoder_program = """ @@ -60,13 +72,20 @@ defmodule JS2ETest.Printers.ArrayPrinter do values: ["none", "green", "yellow", "red"]} } + schema_def = %SchemaDefinition{ + description: "Test schema", + id: URI.parse("http://example.com/test.json"), + title: "Test", + module: "Domain", + types: type_dict} + array_encoder_program = %ArrayType{ name: "colors", path: ["#"], items: ["#", "items"] } - |> ArrayPrinter.print_encoder(type_dict, %{}) + |> ArrayPrinter.print_encoder(schema_def, %{}) expected_array_encoder_program = """ diff --git a/test/printers/enum_printer_test.exs b/test/printers/enum_printer_test.exs index 270a54a..96d0c78 100644 --- a/test/printers/enum_printer_test.exs +++ b/test/printers/enum_printer_test.exs @@ -2,12 +2,17 @@ defmodule JS2ETest.Printers.EnumPrinter do use ExUnit.Case require Logger - alias JS2E.Types.EnumType + alias JS2E.Types.{EnumType, SchemaDefinition} alias JS2E.Printers.EnumPrinter test "print enum type with string values" do - type_dict = %{} + schema_def = %SchemaDefinition{ + description: "Test schema", + id: URI.parse("http://example.com/test.json"), + title: "Test", + module: "Domain", + types: %{}} enum_type_program = %EnumType{ @@ -15,7 +20,7 @@ defmodule JS2ETest.Printers.EnumPrinter do path: ["#", "definitions", "color"], type: "string", values: ["none", "green", "yellow", "red"]} - |> EnumPrinter.print_type(type_dict, %{}) + |> EnumPrinter.print_type(schema_def, %{}) expected_enum_type_program = """ @@ -31,7 +36,12 @@ defmodule JS2ETest.Printers.EnumPrinter do test "print enum type with number values" do - type_dict = %{} + schema_def = %SchemaDefinition{ + description: "Test schema", + id: URI.parse("http://example.com/test.json"), + title: "Test", + module: "Domain", + types: %{}} enum_type_program = %EnumType{ @@ -39,7 +49,7 @@ defmodule JS2ETest.Printers.EnumPrinter do path: ["#", "definitions", "temperature"], type: "number", values: [-0.618, 1.618, 3.14, 7.73]} - |> EnumPrinter.print_type(type_dict, %{}) + |> EnumPrinter.print_type(schema_def, %{}) expected_enum_type_program = """ @@ -55,7 +65,12 @@ defmodule JS2ETest.Printers.EnumPrinter do test "print enum decoder with string values" do - type_dict = %{} + schema_def = %SchemaDefinition{ + description: "Test schema", + id: URI.parse("http://example.com/test.json"), + title: "Test", + module: "Domain", + types: %{}} enum_decoder_program = %EnumType{ @@ -63,7 +78,7 @@ defmodule JS2ETest.Printers.EnumPrinter do path: ["#", "definitions", "color"], type: "string", values: ["none", "green", "yellow", "red"]} - |> EnumPrinter.print_decoder(type_dict, %{}) + |> EnumPrinter.print_decoder(schema_def, %{}) expected_enum_decoder_program = """ @@ -91,7 +106,12 @@ defmodule JS2ETest.Printers.EnumPrinter do test "print enum decoder with number values" do - type_dict = %{} + schema_def = %SchemaDefinition{ + description: "Test schema", + id: URI.parse("http://example.com/test.json"), + title: "Test", + module: "Domain", + types: %{}} enum_decoder_program = %EnumType{ @@ -99,7 +119,7 @@ defmodule JS2ETest.Printers.EnumPrinter do path: ["#", "definitions", "temperature"], type: "number", values: [-0.618, 1.618, 3.14, 7.73]} - |> EnumPrinter.print_decoder(type_dict, %{}) + |> EnumPrinter.print_decoder(schema_def, %{}) expected_enum_decoder_program = """ @@ -127,7 +147,12 @@ defmodule JS2ETest.Printers.EnumPrinter do test "print enum encoder with string values" do - type_dict = %{} + schema_def = %SchemaDefinition{ + description: "Test schema", + id: URI.parse("http://example.com/test.json"), + title: "Test", + module: "Domain", + types: %{}} enum_encoder_program = %EnumType{ @@ -135,7 +160,7 @@ defmodule JS2ETest.Printers.EnumPrinter do path: ["#", "definitions", "color"], type: "string", values: ["none", "green", "yellow", "red"]} - |> EnumPrinter.print_encoder(type_dict, %{}) + |> EnumPrinter.print_encoder(schema_def, %{}) expected_enum_encoder_program = """ @@ -160,7 +185,12 @@ defmodule JS2ETest.Printers.EnumPrinter do test "print enum encoder with number values" do - type_dict = %{} + schema_def = %SchemaDefinition{ + description: "Test schema", + id: URI.parse("http://example.com/test.json"), + title: "Test", + module: "Domain", + types: %{}} enum_encoder_program = %EnumType{ @@ -168,7 +198,7 @@ defmodule JS2ETest.Printers.EnumPrinter do path: ["#", "definitions", "temperature"], type: "number", values: [-0.618, 1.618, 3.14, 7.73]} - |> EnumPrinter.print_encoder(type_dict, %{}) + |> EnumPrinter.print_encoder(schema_def, %{}) expected_enum_encoder_program = """ diff --git a/test/printers/external_references_test.exs b/test/printers/external_references_test.exs index 038bd2a..ac3b0e4 100644 --- a/test/printers/external_references_test.exs +++ b/test/printers/external_references_test.exs @@ -15,6 +15,7 @@ defmodule JS2ETest.Printers.ExternalReferences do description: "Schema for common types", id: URI.parse("http://example.com/definitions.json"), title: "Definitions", + module: "Domain", types: %{ "#/definitions/color" => @@ -61,6 +62,7 @@ defmodule JS2ETest.Printers.ExternalReferences do %SchemaDefinition{ id: URI.parse("http://example.com/circle.json"), title: "Circle", + module: "Domain", description: "Schema for a circle shape", types: %{ @@ -101,9 +103,7 @@ defmodule JS2ETest.Printers.ExternalReferences do } module_prefix = "Domain" - elm_program = Printer.print_schemas( - schema_representations, module_prefix) - + elm_program = Printer.print_schemas(schema_representations) Logger.debug "#{inspect elm_program}" circle_program = elm_program["./Domain/Circle.elm"] @@ -116,12 +116,7 @@ defmodule JS2ETest.Printers.ExternalReferences do import Json.Decode as Decode exposing - ( float - , int - , bool - , string - , list - , succeed + ( succeed , fail , map , maybe @@ -142,27 +137,14 @@ defmodule JS2ETest.Printers.ExternalReferences do import Json.Encode as Encode exposing ( Value - , float - , int - , bool - , string - , list , object ) import Domain.Definitions - exposing - ( Color - , colorDecoder - , encodeColor - , Point - , pointDecoder - , encodePoint - ) type alias Circle = - { center : Point - , color : Maybe Color + { center : Domain.Definitions.Point + , color : Maybe Domain.Definitions.Color , radius : Float } @@ -170,8 +152,8 @@ defmodule JS2ETest.Printers.ExternalReferences do circleDecoder : Decoder Circle circleDecoder = decode Circle - |> required "center" pointDecoder - |> optional "color" (Decode.string |> andThen colorDecoder |> maybe) Nothing + |> required "center" Domain.Definitions.pointDecoder + |> optional "color" (Decode.string |> andThen Domain.Definitions.colorDecoder |> maybe) Nothing |> required "radius" Decode.float @@ -179,12 +161,12 @@ defmodule JS2ETest.Printers.ExternalReferences do encodeCircle circle = let center = - [ ( "center", encodePoint circle.center ) ] + [ ( "center", Domain.Definitions.encodePoint circle.center ) ] color = case circle.color of Just color -> - [ ( "color", encodeColor color ) ] + [ ( "color", Domain.Definitions.encodeColor color ) ] Nothing -> [] @@ -196,8 +178,7 @@ defmodule JS2ETest.Printers.ExternalReferences do center ++ color ++ radius """ - definitions_program = - elm_program["./Domain/Definitions.elm"] + definitions_program = elm_program["./Domain/Definitions.elm"] assert definitions_program == """ @@ -207,12 +188,7 @@ defmodule JS2ETest.Printers.ExternalReferences do import Json.Decode as Decode exposing - ( float - , int - , bool - , string - , list - , succeed + ( succeed , fail , map , maybe @@ -233,11 +209,6 @@ defmodule JS2ETest.Printers.ExternalReferences do import Json.Encode as Encode exposing ( Value - , float - , int - , bool - , string - , list , object ) diff --git a/test/printers/object_printer_test.exs b/test/printers/object_printer_test.exs index 0bab223..d59fa81 100644 --- a/test/printers/object_printer_test.exs +++ b/test/printers/object_printer_test.exs @@ -2,7 +2,7 @@ defmodule JS2ETest.Printers.ObjectPrinter do use ExUnit.Case require Logger - alias JS2E.Types.{ObjectType, EnumType, PrimitiveType} + alias JS2E.Types.{ObjectType, EnumType, PrimitiveType, SchemaDefinition} alias JS2E.Printers.ObjectPrinter test "print object type" do @@ -25,6 +25,13 @@ defmodule JS2ETest.Printers.ObjectPrinter do type: "number"} } + schema_def = %SchemaDefinition{ + description: "Test schema", + id: URI.parse("http://example.com/test.json"), + title: "Test", + module: "Domain", + types: type_dict} + object_type_program = %ObjectType{ name: "circle", @@ -35,7 +42,7 @@ defmodule JS2ETest.Printers.ObjectPrinter do "title" => ["#", "properties", "title"], "radius" => ["#", "properties", "radius"]} } - |> ObjectPrinter.print_type(type_dict, %{}) + |> ObjectPrinter.print_type(schema_def, %{}) expected_object_type_program = """ @@ -69,6 +76,13 @@ defmodule JS2ETest.Printers.ObjectPrinter do type: "number"} } + schema_def = %SchemaDefinition{ + description: "Test schema", + id: URI.parse("http://example.com/test.json"), + title: "Test", + module: "Domain", + types: type_dict} + object_decoder_program = %ObjectType{ name: "circle", @@ -79,7 +93,7 @@ defmodule JS2ETest.Printers.ObjectPrinter do "title" => ["#", "properties", "title"], "radius" => ["#", "properties", "radius"]} } - |> ObjectPrinter.print_decoder(type_dict, %{}) + |> ObjectPrinter.print_decoder(schema_def, %{}) expected_object_decoder_program = """ @@ -114,6 +128,13 @@ defmodule JS2ETest.Printers.ObjectPrinter do type: "number"} } + schema_def = %SchemaDefinition{ + description: "Test schema", + id: URI.parse("http://example.com/test.json"), + title: "Test", + module: "Domain", + types: type_dict} + object_encoder_program = %ObjectType{ name: "circle", @@ -124,7 +145,7 @@ defmodule JS2ETest.Printers.ObjectPrinter do "title" => ["#", "properties", "title"], "radius" => ["#", "properties", "radius"]} } - |> ObjectPrinter.print_encoder(type_dict, %{}) + |> ObjectPrinter.print_encoder(schema_def, %{}) expected_object_encoder_program = """ diff --git a/test/printers/one_of_printer_test.exs b/test/printers/one_of_printer_test.exs index 68bcbaf..aee9bd3 100644 --- a/test/printers/one_of_printer_test.exs +++ b/test/printers/one_of_printer_test.exs @@ -2,7 +2,7 @@ defmodule JS2ETest.Printers.OneOfPrinter do use ExUnit.Case require Logger - alias JS2E.Types.{OneOfType, ObjectType, TypeReference} + alias JS2E.Types.{OneOfType, ObjectType, TypeReference, SchemaDefinition} alias JS2E.Printers.OneOfPrinter test "print 'one of' type value" do @@ -31,6 +31,13 @@ defmodule JS2ETest.Printers.OneOfPrinter do "radius" => ["#", "properties", "radius"]}} } + schema_def = %SchemaDefinition{ + description: "Test schema", + id: URI.parse("http://example.com/test.json"), + title: "Test", + module: "Domain", + types: type_dict} + one_of_type_program = %OneOfType{ name: "shape", @@ -38,7 +45,7 @@ defmodule JS2ETest.Printers.OneOfPrinter do types: [["#", "shape", "0"], ["#", "shape", "1"]] } - |> OneOfPrinter.print_type(type_dict, %{}) + |> OneOfPrinter.print_type(schema_def, %{}) expected_one_of_type_program = """ @@ -68,6 +75,13 @@ defmodule JS2ETest.Printers.OneOfPrinter do "radius" => ["#", "properties", "radius"]}} } + schema_def = %SchemaDefinition{ + description: "Test schema", + id: URI.parse("http://example.com/test.json"), + title: "Test", + module: "Domain", + types: type_dict} + one_of_decoder_program = %OneOfType{ name: "shape", @@ -75,7 +89,7 @@ defmodule JS2ETest.Printers.OneOfPrinter do types: [["#", "definitions", "square"], ["#", "definitions", "circle"]] } - |> OneOfPrinter.print_decoder(type_dict, %{}) + |> OneOfPrinter.print_decoder(schema_def, %{}) expected_one_of_decoder_program = """ @@ -107,6 +121,13 @@ defmodule JS2ETest.Printers.OneOfPrinter do "radius" => ["#", "properties", "radius"]}} } + schema_def = %SchemaDefinition{ + description: "Test schema", + id: URI.parse("http://example.com/test.json"), + title: "Test", + module: "Domain", + types: type_dict} + one_of_encoder_program = %OneOfType{ name: "shape", @@ -114,7 +135,7 @@ defmodule JS2ETest.Printers.OneOfPrinter do types: [["#", "definitions", "square"], ["#", "definitions", "circle"]] } - |> OneOfPrinter.print_encoder(type_dict, %{}) + |> OneOfPrinter.print_encoder(schema_def, %{}) expected_one_of_encoder_program = """ diff --git a/test/printers/union_printer_test.exs b/test/printers/union_printer_test.exs index 198b528..c595470 100644 --- a/test/printers/union_printer_test.exs +++ b/test/printers/union_printer_test.exs @@ -2,12 +2,17 @@ defmodule JS2ETest.Printers.UnionPrinter do use ExUnit.Case require Logger - alias JS2E.Types.UnionType + alias JS2E.Types.{UnionType, SchemaDefinition} alias JS2E.Printers.UnionPrinter test "print union type value" do - type_dict = %{} + schema_def = %SchemaDefinition{ + description: "Test schema", + id: URI.parse("http://example.com/test.json"), + title: "Test", + module: "Domain", + types: %{}} union_type_program = %UnionType{ @@ -15,7 +20,7 @@ defmodule JS2ETest.Printers.UnionPrinter do path: ["#", "definitions", "favoriteNumber"], types: ["number", "integer"] } - |> UnionPrinter.print_type(type_dict, %{}) + |> UnionPrinter.print_type(schema_def, %{}) expected_union_type_program = """ @@ -29,7 +34,12 @@ defmodule JS2ETest.Printers.UnionPrinter do test "print union type with null value" do - type_dict = %{} + schema_def = %SchemaDefinition{ + description: "Test schema", + id: URI.parse("http://example.com/test.json"), + title: "Test", + module: "Domain", + types: %{}} union_type_program = %UnionType{ @@ -37,7 +47,7 @@ defmodule JS2ETest.Printers.UnionPrinter do path: ["#", "definitions", "favoriteNumber"], types: ["number", "integer", "null"] } - |> UnionPrinter.print_type(type_dict, %{}) + |> UnionPrinter.print_type(schema_def, %{}) expected_union_type_program = """ @@ -51,7 +61,12 @@ defmodule JS2ETest.Printers.UnionPrinter do test "print union decoder" do - type_dict = %{} + schema_def = %SchemaDefinition{ + description: "Test schema", + id: URI.parse("http://example.com/test.json"), + title: "Test", + module: "Domain", + types: %{}} union_decoder_program = %UnionType{ @@ -59,7 +74,7 @@ defmodule JS2ETest.Printers.UnionPrinter do path: ["#", "definitions", "favoriteNumber"], types: ["number", "integer"] } - |> UnionPrinter.print_decoder(type_dict, %{}) + |> UnionPrinter.print_decoder(schema_def, %{}) expected_union_decoder_program = """ @@ -75,7 +90,12 @@ defmodule JS2ETest.Printers.UnionPrinter do test "print union decoder with null value" do - type_dict = %{} + schema_def = %SchemaDefinition{ + description: "Test schema", + id: URI.parse("http://example.com/test.json"), + title: "Test", + module: "Domain", + types: %{}} union_decoder_program = %UnionType{ @@ -83,7 +103,7 @@ defmodule JS2ETest.Printers.UnionPrinter do path: ["#", "definitions", "favoriteNumber"], types: ["number", "integer", "null"] } - |> UnionPrinter.print_decoder(type_dict, %{}) + |> UnionPrinter.print_decoder(schema_def, %{}) expected_union_decoder_program = """ @@ -100,7 +120,12 @@ defmodule JS2ETest.Printers.UnionPrinter do test "print union encoder" do - type_dict = %{} + schema_def = %SchemaDefinition{ + description: "Test schema", + id: URI.parse("http://example.com/test.json"), + title: "Test", + module: "Domain", + types: %{}} union_encoder_program = %UnionType{ @@ -108,7 +133,7 @@ defmodule JS2ETest.Printers.UnionPrinter do path: ["#", "definitions", "favoriteNumber"], types: ["number", "integer"] } - |> UnionPrinter.print_encoder(type_dict, %{}) + |> UnionPrinter.print_encoder(schema_def, %{}) expected_union_encoder_program = """