mirror of
https://github.com/valitydev/elixir-thrift.git
synced 2024-11-07 02:35:17 +00:00
Behaviour module generation (#99)
* Behaviour module generation For the server side, I imagine that the user would specify a handler module and pass it into the service. Then it would be called with the appropriate arguments. I thought it might be nice to have a behaviour for these modules that users could be implement. Behaviours here are fully typed with the correct success types.
This commit is contained in:
parent
ceceea9a66
commit
4695aaed34
@ -7,4 +7,10 @@ defmodule Thrift do
|
||||
|
||||
* `Thrift.Parser` - functions for parsing `.thift` schema files
|
||||
"""
|
||||
|
||||
@type i8 :: (-128..127)
|
||||
@type i16 :: (-32_768..32_767)
|
||||
@type i32 :: (-2_147_483_648..2_147_483_647)
|
||||
@type i64 :: (-9_223_372_036_854_775_808..9_223_372_036_854_775_807)
|
||||
@type double :: float()
|
||||
end
|
||||
|
@ -40,7 +40,8 @@ defmodule Thrift.Generator do
|
||||
generate_struct_modules(schema),
|
||||
generate_union_modules(schema),
|
||||
generate_exception_modules(schema),
|
||||
generate_services(schema)
|
||||
generate_services(schema),
|
||||
generate_behaviours(schema)
|
||||
])
|
||||
end
|
||||
|
||||
@ -96,4 +97,11 @@ defmodule Thrift.Generator do
|
||||
Generator.Service.generate(schema, service)
|
||||
end
|
||||
end
|
||||
|
||||
defp generate_behaviours(schema) do
|
||||
for {_, service} <- schema.services do
|
||||
Generator.Behaviour.generate(schema, service)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
88
lib/thrift/generator/behaviour.ex
Normal file
88
lib/thrift/generator/behaviour.ex
Normal file
@ -0,0 +1,88 @@
|
||||
defmodule Thrift.Generator.Behaviour do
|
||||
@moduledoc """
|
||||
A generator for a handler module's behaviour.
|
||||
|
||||
Takes a thrift service definition and creates a behavoiur module for users
|
||||
to implement. Thrift types are converted into Elixir typespecs that are
|
||||
equivalent to their thrift counterparts.
|
||||
"""
|
||||
alias Thrift.Parser.FileGroup
|
||||
alias Thrift.Parser.Models.{
|
||||
Field,
|
||||
Struct,
|
||||
StructRef,
|
||||
}
|
||||
|
||||
def generate(schema, service) do
|
||||
file_group = schema.file_group
|
||||
dest_module = FileGroup.dest_module(file_group, service)
|
||||
|> Module.concat(Handler)
|
||||
|> Module.concat(Behaviour)
|
||||
|
||||
callbacks = service.functions
|
||||
|> Map.values
|
||||
|> Enum.map(&create_callback(file_group, &1))
|
||||
|
||||
behaviour_module = quote do
|
||||
defmodule unquote(dest_module) do
|
||||
unquote_splicing(callbacks)
|
||||
end
|
||||
end
|
||||
|
||||
{dest_module, behaviour_module}
|
||||
end
|
||||
|
||||
defp create_callback(file_group, function) do
|
||||
return_type = typespec(function.return_type, file_group)
|
||||
params = function.params
|
||||
|> Enum.map(&FileGroup.resolve(file_group, &1))
|
||||
|> Enum.map(&to_arg_spec(&1, file_group))
|
||||
|
||||
quote do
|
||||
@callback unquote(function.name)(unquote_splicing(params)) :: unquote(return_type)
|
||||
end
|
||||
end
|
||||
|
||||
def to_arg_spec(%Field{name: name, type: type}, file_group) do
|
||||
quote do
|
||||
unquote(Macro.var(name, nil)) :: unquote(typespec(type, file_group))
|
||||
end
|
||||
end
|
||||
|
||||
defp typespec(:void, _), do: quote do: no_return()
|
||||
defp typespec(:bool, _), do: quote do: boolean()
|
||||
defp typespec(:string, _), do: quote do: String.t
|
||||
defp typespec(:i8, _), do: quote do: Thrift.i8
|
||||
defp typespec(:i16, _), do: quote do: Thrift.i16
|
||||
defp typespec(:i32, _), do: quote do: Thrift.i32
|
||||
defp typespec(:i64, _), do: quote do: Thrift.i64
|
||||
defp typespec(:double, _), do: quote do: Thrift.double
|
||||
defp typespec(%StructRef{} = ref, file_group) do
|
||||
file_group
|
||||
|> FileGroup.resolve(ref)
|
||||
|> typespec(file_group)
|
||||
end
|
||||
defp typespec(%Struct{name: name}, file_group) do
|
||||
dest_module = FileGroup.dest_module(file_group, name)
|
||||
quote do
|
||||
%unquote(dest_module){}
|
||||
end
|
||||
end
|
||||
defp typespec({:set, _t}, _) do
|
||||
quote do
|
||||
%MapSet{}
|
||||
end
|
||||
end
|
||||
defp typespec({:list, t}, file_group) do
|
||||
quote do
|
||||
[unquote(typespec(t, file_group))]
|
||||
end
|
||||
end
|
||||
defp typespec({:map, {k, v}}, file_group) do
|
||||
key_type = typespec(k, file_group)
|
||||
val_type = typespec(v, file_group)
|
||||
quote do
|
||||
%{unquote(key_type) => unquote(val_type)}
|
||||
end
|
||||
end
|
||||
end
|
33
test/generator/behaviour_test.exs
Normal file
33
test/generator/behaviour_test.exs
Normal file
@ -0,0 +1,33 @@
|
||||
defmodule BehaviourTest do
|
||||
use ThriftTestCase
|
||||
|
||||
@thrift_file name: "behaviour.thrift", contents: """
|
||||
struct S {
|
||||
1: string username
|
||||
}
|
||||
|
||||
service BehaviourService {
|
||||
void ping(1: i64 my_int),
|
||||
void my_bool(1: bool my_bool),
|
||||
void numbers(1: byte b, 2: i16 i, 3: i32 eye32, 4: i64 eye64, 5: double dub),
|
||||
void my_set(1: set<string> my_set),
|
||||
void my_list(1: list<string> my_string),
|
||||
void my_map(1: map<string, string> my_map)
|
||||
map<string, bool> my_map2(1: map<string, map<string, string>> my_map)
|
||||
void struct_param(1: S my_struct)
|
||||
}
|
||||
"""
|
||||
|
||||
thrift_test "that behaviour callbacks exist" do
|
||||
behaviour_specs = Behaviour.behaviour_info(:callbacks)
|
||||
|
||||
assert {:ping, 1} in behaviour_specs
|
||||
assert {:my_bool, 1} in behaviour_specs
|
||||
assert {:numbers, 5} in behaviour_specs
|
||||
assert {:my_set, 1} in behaviour_specs
|
||||
assert {:my_list, 1} in behaviour_specs
|
||||
assert {:my_map, 1} in behaviour_specs
|
||||
assert {:my_map2, 1} in behaviour_specs
|
||||
assert {:struct_param, 1} in behaviour_specs
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue
Block a user