diff --git a/lib/aws_codegen/post_service.ex b/lib/aws_codegen/post_service.ex index afc1386..9a897c9 100644 --- a/lib/aws_codegen/post_service.ex +++ b/lib/aws_codegen/post_service.ex @@ -177,17 +177,16 @@ defmodule AWS.CodeGen.PostService do members: shape["members"], min: shape["min"], enum: shape["enum"], - is_input: is_input?(name, shape) + is_input: is_input?(shape) }} end) |> Enum.into(%{}) end - defp is_input?(name, shape) do + defp is_input?(shape) do if Map.has_key?(shape, "traits") do traits = shape["traits"] if Map.has_key?(traits, "smithy.api#input") do - IO.puts("Found shape: #{name} with input: true!!!") true else false diff --git a/lib/aws_codegen/types.ex b/lib/aws_codegen/types.ex index 86b16ca..7a6c5dd 100644 --- a/lib/aws_codegen/types.ex +++ b/lib/aws_codegen/types.ex @@ -4,103 +4,119 @@ defmodule AWS.CodeGen.Types do # Unfortunately, gotta patch over auto-defining types that already exist in Elixir - def shape_to_type("String", _) do + def shape_to_type(:elixir, "String", _) do "String.t()" end + def shape_to_type(:erlang, "String", _) do + "string()" + end - def shape_to_type("string", _) do + def shape_to_type(:elixir, "string", _) do "String.t()" end + def shape_to_type(:erlang, "string", _) do + "string()" + end - def shape_to_type("Identifier", _) do + def shape_to_type(:elixir, "Identifier", _) do "String.t()" end + def shape_to_type(:erlang, "Identifier", _) do + "string()" + end - def shape_to_type("identifier", _) do + def shape_to_type(:elixir, "identifier", _) do "String.t()" end + def shape_to_type(:erlang, "identifier", _) do + "string()" + end - def shape_to_type("XmlString" <> _rest, _) do + def shape_to_type(:elixir, "XmlString" <> _rest, _) do "String.t()" end + def shape_to_type(:erlang, "XmlString" <> _rest, _) do + "string" + end - def shape_to_type("NullablePositiveInteger", _) do + def shape_to_type(:elixir, "NullablePositiveInteger", _) do "nil | non_neg_integer()" end + def shape_to_type(:erlang, "NullablePositiveInteger", _) do + "undefined | non_neg_integer()" + end - def shape_to_type(%Shape{type: type}, _module_name) when type in ["float", "double", "long"] do + def shape_to_type(_, %Shape{type: type}, _module_name) when type in ["float", "double", "long"] do "float()" end - def shape_to_type(%Shape{type: "timestamp"}, _module_name) do + def shape_to_type(_, %Shape{type: "timestamp"}, _module_name) do "non_neg_integer()" end - def shape_to_type(%Shape{type: "map"}, _module_name) do + def shape_to_type(_, %Shape{type: "map"}, _module_name) do "map()" end - def shape_to_type(%Shape{type: "blob"}, _module_name) do + def shape_to_type(_, %Shape{type: "blob"}, _module_name) do "binary()" end - def shape_to_type(%Shape{type: "string"}, _module_name) do + def shape_to_type(:elixir, %Shape{type: "string"}, _module_name) do "String.t()" end + def shape_to_type(:erlang, %Shape{type: "string"}, _module_name) do + "string()" + end - def shape_to_type(%Shape{type: "integer"}, _module_name) do + def shape_to_type(_, %Shape{type: "integer"}, _module_name) do "integer()" end - def shape_to_type(%Shape{type: "boolean"}, _module_name) do + def shape_to_type(_, %Shape{type: "boolean"}, _module_name) do "boolean()" end - def shape_to_type(%Shape{type: "enum"}, _module_name) do + def shape_to_type(_, %Shape{type: "enum"}, _module_name) do "list(any())" end - def shape_to_type(%Shape{type: "union"}, _module_name) do + def shape_to_type(_, %Shape{type: "union"}, _module_name) do "list()" end - def shape_to_type(%Shape{type: "document"}, _module_name) do + def shape_to_type(_, %Shape{type: "document"}, _module_name) do "any()" end - def shape_to_type(%Shape{name: shape_name} = shape, module_name) do - raise "missing shape for type #{shape_name} #{inspect(shape, limit: :infinity)}" - "#{module_name}.#{Name.to_snake_case(shape_name)}" - end - def shape_to_type(context, shape_name, module_name, all_shapes) do case shape_name do "smithy.api#String" -> - "[#{shape_to_type(%Shape{type: "string"}, module_name)}]" + "[#{shape_to_type(context.language, %Shape{type: "string"}, module_name)}]" "smithy.api#Integer" -> - "[#{shape_to_type(%Shape{type: "integer"}, module_name)}]" + "[#{shape_to_type(context.language, %Shape{type: "integer"}, module_name)}]" "smithy.api#Timestamp" -> - "[#{shape_to_type(%Shape{type: "timestamp"}, module_name)}]" + "[#{shape_to_type(context.language, %Shape{type: "timestamp"}, module_name)}]" "smithy.api#PrimitiveLong" -> - "[#{shape_to_type(%Shape{type: "long"}, module_name)}]" + "[#{shape_to_type(context.language, %Shape{type: "long"}, module_name)}]" "smithy.api#Long" -> - "[#{shape_to_type(%Shape{type: "long"}, module_name)}]" + "[#{shape_to_type(context.language, %Shape{type: "long"}, module_name)}]" "smithy.api#Boolean" -> - "[#{shape_to_type(%Shape{type: "boolean"}, module_name)}]" + "[#{shape_to_type(context.language, %Shape{type: "boolean"}, module_name)}]" "smithy.api#PrimitiveBoolean" -> - "[#{shape_to_type(%Shape{type: "boolean"}, module_name)}]" + "[#{shape_to_type(context.language, %Shape{type: "boolean"}, module_name)}]" "smithy.api#Double" -> - "[#{shape_to_type(%Shape{type: "double"}, module_name)}]" + "[#{shape_to_type(context.language, %Shape{type: "double"}, module_name)}]" "smithy.api#Document" -> - "[#{shape_to_type(%Shape{type: "document"}, module_name)}]" + "[#{shape_to_type(context.language, %Shape{type: "document"}, module_name)}]" "smithy.api#Unit" -> "[]" @@ -110,7 +126,7 @@ defmodule AWS.CodeGen.Types do %Shape{type: "structure"} -> type = "#{AWS.CodeGen.Name.to_snake_case(String.replace(shape_name, ~r/com\.amazonaws\.[^#]+#/, ""))}" if AWS.CodeGen.Util.reserved_type(type) do - "#{String.downcase(String.replace(context.module_name, "AWS.", ""))}_#{type}()" + "#{String.downcase(String.replace(context.module_name, ["aws_", "AWS."], ""))}_#{type}()" else "#{type}()" end @@ -118,7 +134,7 @@ defmodule AWS.CodeGen.Types do %Shape{type: "list", member: member} -> type = "#{shape_to_type(context, member["target"], module_name, all_shapes)}" if AWS.CodeGen.Util.reserved_type(type) do - "list(#{String.downcase(String.replace(context.module_name, "AWS.", ""))}_#{type}())" + "list(#{String.downcase(String.replace(context.module_name, ["aws_", "AWS."], ""))}_#{type}())" else "list(#{type}())" end @@ -127,7 +143,7 @@ defmodule AWS.CodeGen.Types do raise "Tried to reference an undefined shape for #{shape_name}" shape -> - shape_to_type(shape, module_name) + shape_to_type(context.language, shape, module_name) end end end diff --git a/lib/aws_codegen/util.ex b/lib/aws_codegen/util.ex index 9918697..46fb073 100644 --- a/lib/aws_codegen/util.ex +++ b/lib/aws_codegen/util.ex @@ -1,7 +1,5 @@ defmodule AWS.CodeGen.Util do - alias AWS.CodeGen.PostService.Shape - def service_docs(service) do with "

" <- service["traits"]["smithy.api#documentation"], do: "" end @@ -66,7 +64,7 @@ defmodule AWS.CodeGen.Util do fn {_name, shape_member}, a -> target = shape_member["target"] shape_member_type = AWS.CodeGen.Types.shape_to_type(context, target, context.module_name, context.shapes) - Map.put(a, is_required(shape.is_input, shape_member, target), shape_member_type) + Map.put(a, is_required(context.language, shape.is_input, shape_member, target), shape_member_type) end) if reserved_type(type) do Map.put(acc, "#{String.downcase(String.replace(context.module_name, "AWS.", ""))}_#{type}", types) @@ -79,7 +77,7 @@ defmodule AWS.CodeGen.Util do end) end - defp is_required(is_input, shape, target) do + defp is_required(:elixir, is_input, shape, target) do trimmed_name = String.replace(target, ~r/com\.amazonaws\.[^#]+#/, "") if is_input do if Map.has_key?(shape, "traits") do @@ -95,14 +93,27 @@ defmodule AWS.CodeGen.Util do "\"#{trimmed_name}\"" end end + defp is_required(:erlang, _is_input, _shape, target) do + # There is no optional/1 | required/1 type of thing in Erlang. + # Instead we'd use := | => but let's deal with that problem later... + trimmed_name = String.replace(target, ~r/com\.amazonaws\.[^#]+#/, "") + "\"#{trimmed_name}\"" + end - def function_argument_type(action) do + def function_argument_type(:elixir, action) do case Map.get(action.input, "target") do "smithy.api#Unit" -> "%{}" type -> "#{AWS.CodeGen.Name.to_snake_case(String.replace(type, ~r/com\.amazonaws\.[^#]+#/, ""))}()" end end + def function_argument_type(:erlang, action) do + case Map.get(action.input, "target") do + "smithy.api#Unit" -> "\#{}" + type -> + "#{AWS.CodeGen.Name.to_snake_case(String.replace(type, ~r/com\.amazonaws\.[^#]+#/, ""))}()" + end + end def return_type(action) do case Map.get(action.output, "target") do @@ -123,6 +134,25 @@ defmodule AWS.CodeGen.Util do Enum.join([normal, "{:error, {:unexpected_response, any()}}" | errors], " | ") end end + def return_type(:erlang, action) do + case Map.get(action.output, "target") do + "smithy.api#Unit" -> "[]" + type -> + normal = "{ok, #{AWS.CodeGen.Name.to_snake_case(String.replace(type, ~r/com\.amazonaws\.[^#]+#/, ""))}(), tuple()}" + errors = + if is_list(action.errors) do + Enum.map(action.errors, + fn %{"target" => error_type} -> + "{error, #{AWS.CodeGen.Name.to_snake_case(String.replace(error_type, ~r/com\.amazonaws\.[^#]+#/, ""))}(), tuple()}" + _ -> + "" + end ) + else + [] + end + Enum.join([normal, "{error, any()}" | errors], " |\n ") + end + end def reserved_type(type) do if type == "node" || type == "term" do diff --git a/priv/post.erl.eex b/priv/post.erl.eex index 691b1f3..f71b3ae 100644 --- a/priv/post.erl.eex +++ b/priv/post.erl.eex @@ -8,11 +8,23 @@ -include_lib("hackney/include/hackney_lib.hrl"). +<%= for {type_name, type_fields} <- AWS.CodeGen.Util.types(context) do %> +%% Example: +%% <%= type_name %>() :: #{ +<%= Enum.map_join(type_fields, ",\n", fn {field_name, field_type} -> + ~s{%% <<#{field_name}>> => #{field_type}} +end) %> +%% } +-type <%= "#{type_name}()" %> :: #{binary() => any()}. +<% end %> + %%==================================================================== %% API %%==================================================================== <%= for action <- context.actions do %> <%= action.docstring %> +-spec <%= action.function_name %>(map(), <%= AWS.CodeGen.Util.function_argument_type(context.language, action)%>, list()) -> + <%= AWS.CodeGen.Util.return_type(context.language, action)%>. <%= action.function_name %>(Client, Input) when is_map(Client), is_map(Input) -> <%= action.function_name %>(Client, Input, []). diff --git a/priv/post.ex.eex b/priv/post.ex.eex index 482f127..dfe1de1 100644 --- a/priv/post.ex.eex +++ b/priv/post.ex.eex @@ -43,7 +43,7 @@ end) %> @doc """ <%= action.docstring %> """<% end %> - @spec <%= action.function_name %>(map(), <%= AWS.CodeGen.Util.function_argument_type(action)%>, list()) :: <%= AWS.CodeGen.Util.return_type(action)%> + @spec <%= action.function_name %>(map(), <%= AWS.CodeGen.Util.function_argument_type(context.language, action)%>, list()) :: <%= AWS.CodeGen.Util.return_type(context.language, action)%> def <%= action.function_name %>(%Client{} = client, input, options \\ []) do meta = <%= if action.host_prefix do %>