diff options
Diffstat (limited to 'src/couch_epi/src/couch_epi_functions_gen.erl')
-rw-r--r-- | src/couch_epi/src/couch_epi_functions_gen.erl | 330 |
1 files changed, 210 insertions, 120 deletions
diff --git a/src/couch_epi/src/couch_epi_functions_gen.erl b/src/couch_epi/src/couch_epi_functions_gen.erl index 7408593b8..d7364c044 100644 --- a/src/couch_epi/src/couch_epi_functions_gen.erl +++ b/src/couch_epi/src/couch_epi_functions_gen.erl @@ -45,20 +45,30 @@ get_handle(ServiceId) -> apply(ServiceId, Function, Args, Opts) when is_atom(ServiceId) -> apply(get_handle(ServiceId), ServiceId, Function, Args, Opts). --spec apply(Handle :: atom(), ServiceId :: atom(), Function :: atom(), - Args :: [term()], Opts :: couch_epi:apply_opts()) -> [any()]. +-spec apply( + Handle :: atom(), + ServiceId :: atom(), + Function :: atom(), + Args :: [term()], + Opts :: couch_epi:apply_opts() +) -> [any()]. apply(Handle, _ServiceId, Function, Args, Opts) -> DispatchOpts = parse_opts(Opts), Modules = providers(Handle, Function, length(Args), DispatchOpts), dispatch(Handle, Modules, Function, Args, DispatchOpts). --spec decide(Handle :: atom(), ServiceId :: atom(), Function :: atom(), - Args :: [term()], Opts :: couch_epi:apply_opts()) -> - no_decision | {decided, term()}. +-spec decide( + Handle :: atom(), + ServiceId :: atom(), + Function :: atom(), + Args :: [term()], + Opts :: couch_epi:apply_opts() +) -> + no_decision | {decided, term()}. decide(Handle, _ServiceId, Function, Args, Opts) -> - DispatchOpts = parse_opts([interruptible|Opts]), + DispatchOpts = parse_opts([interruptible | Opts]), Modules = providers(Handle, Function, length(Args), DispatchOpts), dispatch(Handle, Modules, Function, Args, DispatchOpts). @@ -67,33 +77,33 @@ decide(Handle, _ServiceId, Function, Args, Opts) -> %% ------------------------------------------------------------------ preamble() -> - " - -export([version/0, version/1]). - -export([providers/0, providers/2]). - -export([definitions/0, definitions/1]). - -export([dispatch/3]). - -export([callbacks/2]). - - version() -> - [{Provider, version(Provider)} || Provider <- providers()]. - - definitions() -> - [{Provider, definitions(Provider)} || Provider <- providers()]. - - callbacks(Provider, Function) -> - []. - - " - %% In addition to preamble we also generate following methods - %% dispatch(Module, Function, [A1, A2]) -> Module:Function(A1, A2); - - %% version(Source1) -> "HASH"; - %% version(Source) -> {error, {unknown, Source}}. - - %% providers() -> []. - %% providers(Function, Arity) -> []. - %% definitions(Provider) -> [{Module, [{Fun, Arity}]}]. - . + "\n" + " -export([version/0, version/1]).\n" + " -export([providers/0, providers/2]).\n" + " -export([definitions/0, definitions/1]).\n" + " -export([dispatch/3]).\n" + " -export([callbacks/2]).\n" + "\n" + " version() ->\n" + " [{Provider, version(Provider)} || Provider <- providers()].\n" + "\n" + " definitions() ->\n" + " [{Provider, definitions(Provider)} || Provider <- providers()].\n" + "\n" + " callbacks(Provider, Function) ->\n" + " [].\n" + "\n" + " " +%% In addition to preamble we also generate following methods +%% dispatch(Module, Function, [A1, A2]) -> Module:Function(A1, A2); + +%% version(Source1) -> "HASH"; +%% version(Source) -> {error, {unknown, Source}}. + +%% providers() -> []. +%% providers(Function, Arity) -> []. +%% definitions(Provider) -> [{Module, [{Fun, Arity}]}]. +. generate(Handle, Defs) -> DispatchFunForms = couch_epi_codegen:function(dispatchers(Defs)), @@ -103,10 +113,11 @@ generate(Handle, Defs) -> ProvidersForms = couch_epi_codegen:function(providers_method(Defs)), DefinitionsForms = couch_epi_codegen:function(definitions_method(Defs)), - Forms = couch_epi_codegen:scan(preamble()) - ++ DispatchFunForms ++ VersionFunForms - ++ ProvidersForms ++ AllProvidersForms - ++ DefinitionsForms, + Forms = + couch_epi_codegen:scan(preamble()) ++ + DispatchFunForms ++ VersionFunForms ++ + ProvidersForms ++ AllProvidersForms ++ + DefinitionsForms, couch_epi_codegen:generate(Handle, Forms). @@ -117,9 +128,13 @@ all_providers_method(Defs) -> providers_method(Defs) -> Providers = providers_by_function(Defs), DefaultClause = "providers(_, _) -> [].", - lists:foldl(fun({{Fun, Arity}, Modules}, Clauses) -> - providers(Fun, Arity, Modules) ++ Clauses - end, [couch_epi_codegen:scan(DefaultClause)], Providers). + lists:foldl( + fun({{Fun, Arity}, Modules}, Clauses) -> + providers(Fun, Arity, Modules) ++ Clauses + end, + [couch_epi_codegen:scan(DefaultClause)], + Providers + ). providers(Function, Arity, Modules) -> ArityStr = integer_to_list(Arity), @@ -127,26 +142,38 @@ providers(Function, Arity, Modules) -> Fun = atom_to_list(Function), %% providers(Function, Arity) -> [Module]; couch_epi_codegen:scan( - "providers(" ++ Fun ++ "," ++ ArityStr ++ ") ->" ++ Mods ++ ";"). + "providers(" ++ Fun ++ "," ++ ArityStr ++ ") ->" ++ Mods ++ ";" + ). dispatchers(Defs) -> DefaultClause = "dispatch(_Module, _Fun, _Args) -> ok.", - fold_defs(Defs, [couch_epi_codegen:scan(DefaultClause)], + fold_defs( + Defs, + [couch_epi_codegen:scan(DefaultClause)], fun({_Source, Module, Function, Arity}, Acc) -> dispatcher(Module, Function, Arity) ++ Acc - end). + end + ). version_method(Defs) -> DefaultClause = "version(S) -> {error, {unknown, S}}.", - lists:foldl(fun({Source, SrcDefs}, Clauses) -> - version(Source, SrcDefs) ++ Clauses - end, [couch_epi_codegen:scan(DefaultClause)], Defs). + lists:foldl( + fun({Source, SrcDefs}, Clauses) -> + version(Source, SrcDefs) ++ Clauses + end, + [couch_epi_codegen:scan(DefaultClause)], + Defs + ). definitions_method(Defs) -> DefaultClause = "definitions(S) -> {error, {unknown, S}}.", - lists:foldl(fun({Source, SrcDefs}, Clauses) -> - definition(Source, SrcDefs) ++ Clauses - end, [couch_epi_codegen:scan(DefaultClause)], Defs). + lists:foldl( + fun({Source, SrcDefs}, Clauses) -> + definition(Source, SrcDefs) ++ Clauses + end, + [couch_epi_codegen:scan(DefaultClause)], + Defs + ). definition(Source, Defs) -> Src = atom_to_list(Source), @@ -159,27 +186,28 @@ dispatcher(Module, Function, 0) -> %% dispatch(Module, Function, []) -> Module:Function(); couch_epi_codegen:scan( - "dispatch(" ++ M ++ "," ++ Fun ++ ", []) ->" - ++ M ++ ":" ++ Fun ++ "();"); + "dispatch(" ++ M ++ "," ++ Fun ++ ", []) ->" ++ + M ++ ":" ++ Fun ++ "();" + ); dispatcher(Module, Function, Arity) -> Args = args_string(Arity), M = atom_to_list(Module), Fun = atom_to_list(Function), %% dispatch(Module, Function, [A1, A2]) -> Module:Function(A1, A2); couch_epi_codegen:scan( - "dispatch(" ++ M ++ "," ++ Fun ++ ", [" ++ Args ++ "]) ->" - ++ M ++ ":" ++ Fun ++ "(" ++ Args ++ ");"). + "dispatch(" ++ M ++ "," ++ Fun ++ ", [" ++ Args ++ "]) ->" ++ + M ++ ":" ++ Fun ++ "(" ++ Args ++ ");" + ). args_string(Arity) -> - Vars = ["A" ++ integer_to_list(Seq) || Seq <- lists:seq(1, Arity)], + Vars = ["A" ++ integer_to_list(Seq) || Seq <- lists:seq(1, Arity)], string:join(Vars, ", "). version(Source, SrcDefs) -> Modules = [Module || {Module, _Exports} <- SrcDefs], couch_epi_codegen:scan( - "version(" ++ atom_to_list(Source) ++ ") ->" ++ hash(Modules) ++ ";"). - - + "version(" ++ atom_to_list(Source) ++ ") ->" ++ hash(Modules) ++ ";" + ). %% ------------------------------------------------------------------ %% Helper functions @@ -204,26 +232,48 @@ defined_providers(Defs) -> %% Defs = [{Source, [{Module, [{Fun, Arity}]}]}] fold_defs(Defs, Acc, Fun) -> - lists:foldl(fun({Source, SourceData}, Clauses) -> - lists:foldl(fun({Module, Exports}, ExportsAcc) -> - lists:foldl(fun({Function, Arity}, InAcc) -> - Fun({Source, Module, Function, Arity}, InAcc) - end, [], Exports) ++ ExportsAcc - end, [], SourceData) ++ Clauses - end, Acc, Defs). + lists:foldl( + fun({Source, SourceData}, Clauses) -> + lists:foldl( + fun({Module, Exports}, ExportsAcc) -> + lists:foldl( + fun({Function, Arity}, InAcc) -> + Fun({Source, Module, Function, Arity}, InAcc) + end, + [], + Exports + ) ++ ExportsAcc + end, + [], + SourceData + ) ++ Clauses + end, + Acc, + Defs + ). providers_by_function(Defs) -> - Providers = fold_defs(Defs, [], + Providers = fold_defs( + Defs, + [], fun({_Source, Module, Function, Arity}, Acc) -> [{{Function, Arity}, Module} | Acc] end ), - Dict = lists:foldl(fun({K, V}, Acc) -> - dict:update(K, fun(Modules) -> - append_if_missing(Modules, V) - end, [V], Acc) - - end, dict:new(), Providers), + Dict = lists:foldl( + fun({K, V}, Acc) -> + dict:update( + K, + fun(Modules) -> + append_if_missing(Modules, V) + end, + [V], + Acc + ) + end, + dict:new(), + Providers + ), dict:to_list(Dict). append_if_missing(List, Value) -> @@ -238,36 +288,75 @@ hash(Modules) -> dispatch(_Handle, _Modules, _Func, _Args, #opts{concurrent = true, pipe = true}) -> throw({error, {incompatible_options, [concurrent, pipe]}}); -dispatch(Handle, Modules, Function, Args, - #opts{pipe = true, ignore_errors = true}) -> - lists:foldl(fun(Module, Acc) -> - try +dispatch( + Handle, + Modules, + Function, + Args, + #opts{pipe = true, ignore_errors = true} +) -> + lists:foldl( + fun(Module, Acc) -> + try + Handle:dispatch(Module, Function, Acc) + catch + _:_ -> + Acc + end + end, + Args, + Modules + ); +dispatch( + Handle, + Modules, + Function, + Args, + #opts{pipe = true} +) -> + lists:foldl( + fun(Module, Acc) -> Handle:dispatch(Module, Function, Acc) - catch _:_ -> - Acc - end - end, Args, Modules); -dispatch(Handle, Modules, Function, Args, - #opts{pipe = true}) -> - lists:foldl(fun(Module, Acc) -> - Handle:dispatch(Module, Function, Acc) - end, Args, Modules); -dispatch(Handle, Modules, Function, Args, - #opts{interruptible = true}) -> + end, + Args, + Modules + ); +dispatch( + Handle, + Modules, + Function, + Args, + #opts{interruptible = true} +) -> apply_while(Modules, Handle, Function, Args); dispatch(Handle, Modules, Function, Args, #opts{} = Opts) -> [do_dispatch(Handle, Module, Function, Args, Opts) || Module <- Modules]. -do_dispatch(Handle, Module, Function, Args, - #opts{concurrent = true, ignore_errors = true}) -> +do_dispatch( + Handle, + Module, + Function, + Args, + #opts{concurrent = true, ignore_errors = true} +) -> spawn(fun() -> (catch Handle:dispatch(Module, Function, Args)) end); -do_dispatch(Handle, Module, Function, Args, - #opts{ignore_errors = true}) -> +do_dispatch( + Handle, + Module, + Function, + Args, + #opts{ignore_errors = true} +) -> (catch Handle:dispatch(Module, Function, Args)); -do_dispatch(Handle, Module, Function, Args, - #opts{concurrent = true}) -> +do_dispatch( + Handle, + Module, + Function, + Args, + #opts{concurrent = true} +) -> spawn(fun() -> Handle:dispatch(Module, Function, Args) end); do_dispatch(Handle, Module, Function, Args, #opts{}) -> Handle:dispatch(Module, Function, Args). @@ -285,13 +374,13 @@ apply_while([Module | Modules], Handle, Function, Args) -> parse_opts(Opts) -> parse_opts(Opts, #opts{}). -parse_opts([ignore_errors|Rest], #opts{} = Acc) -> +parse_opts([ignore_errors | Rest], #opts{} = Acc) -> parse_opts(Rest, Acc#opts{ignore_errors = true}); -parse_opts([pipe|Rest], #opts{} = Acc) -> +parse_opts([pipe | Rest], #opts{} = Acc) -> parse_opts(Rest, Acc#opts{pipe = true}); -parse_opts([concurrent|Rest], #opts{} = Acc) -> +parse_opts([concurrent | Rest], #opts{} = Acc) -> parse_opts(Rest, Acc#opts{concurrent = true}); -parse_opts([interruptible|Rest], #opts{} = Acc) -> +parse_opts([interruptible | Rest], #opts{} = Acc) -> parse_opts(Rest, Acc#opts{interruptible = true}); parse_opts([], Acc) -> Acc. @@ -324,16 +413,17 @@ basic_test() -> generate(Module, [{app1, Defs}, {app2, Defs}]), Exports = lists:sort([ - {callbacks,2}, - {version,1}, - {providers,2}, - {definitions,1}, - {module_info,0}, - {version,0}, - {dispatch,3}, - {providers,0}, - {module_info,1}, - {definitions,0}]), + {callbacks, 2}, + {version, 1}, + {providers, 2}, + {definitions, 1}, + {module_info, 0}, + {version, 0}, + {dispatch, 3}, + {providers, 0}, + {module_info, 1}, + {definitions, 0} + ]), ?assertEqual(Exports, lists:sort(Module:module_info(exports))), ?assertEqual([app1, app2], lists:sort(Module:providers())), @@ -356,19 +446,19 @@ generate_module(Name, Body) -> couch_epi_codegen:generate(Name, Tokens). decide_module(decide) -> - " - -export([inc/1]). - - inc(A) -> - {decided, A + 1}. - "; + "\n" + " -export([inc/1]).\n" + "\n" + " inc(A) ->\n" + " {decided, A + 1}.\n" + " "; decide_module(no_decision) -> - " - -export([inc/1]). - - inc(_A) -> - no_decision. - ". + "\n" + " -export([inc/1]).\n" + "\n" + " inc(_A) ->\n" + " no_decision.\n" + " ". decide_test() -> ok = generate_module(decide, decide_module(decide)), @@ -380,12 +470,12 @@ decide_test() -> DecideFirstHandle = decide_first_handle, ok = generate(DecideFirstHandle, [DecideDef, NoDecissionDef]), ?assertMatch([decide, no_decision], DecideFirstHandle:providers(inc, 1)), - ?assertMatch({decided,4}, decide(DecideFirstHandle, anything, inc, [3], [])), + ?assertMatch({decided, 4}, decide(DecideFirstHandle, anything, inc, [3], [])), DecideSecondHandle = decide_second_handle, ok = generate(DecideSecondHandle, [NoDecissionDef, DecideDef]), ?assertMatch([no_decision, decide], DecideSecondHandle:providers(inc, 1)), - ?assertMatch({decided,4}, decide(DecideSecondHandle, anything, inc, [3], [])), + ?assertMatch({decided, 4}, decide(DecideSecondHandle, anything, inc, [3], [])), NoDecissionHandle = no_decision_handle, ok = generate(NoDecissionHandle, [NoDecissionDef]), |