%%%------------------------------------------------------------------- %%% @doc %%% A set of optics specific to maps. %%% @end %%%------------------------------------------------------------------- -module(optic_maps). %% API -export([all/0, all/1, keys/0, keys/1, values/0, values/1, associations/0, associations/1, key/1, key/2, association/1, association/2]). %%%=================================================================== %%% API %%%=================================================================== %% @see values/1 -spec all() -> optic:optic(). all() -> values(). %% @see values/1 -spec all(optic:extend_options()) -> optic:optic(). all(Options) -> values(Options). %% @see keys/1 -spec keys() -> optic:optic(). keys() -> keys(#{}). %% @doc %% Focus on all keys of a map. %% %% Example: %% %% ``` %% > optic:get([optic_maps:keys()], #{first => 1, second => 2). %% {ok,[first,second]} %% ''' %% @end %% @param Options Common optic options. %% @returns An opaque optic record. -spec keys(optic:extend_options()) -> optic:optic(). keys(Options) -> Fold = fun (Fun, Acc, Map) when is_map(Map) -> {ok, maps:fold(fun (Key, _Value, InnerAcc) -> Fun(Key, InnerAcc) end, Acc, Map)}; (_Fun, _Acc, _Data) -> {error, undefined} end, MapFold = fun (Fun, Acc, Map) when is_map(Map) -> {ok, maps:fold(fun (Key, Value, {InnerMap, InnerAcc}) -> {NewKey, NewAcc} = Fun(Key, InnerAcc), {InnerMap#{NewKey=>Value}, NewAcc} end, {#{}, Acc}, Map)}; (_Fun, _Acc, _Data) -> {error, undefined} end, New = fun (_Data, _Template) -> #{} end, Optic = optic:new(MapFold, Fold), optic:'%extend'(Optic, Options, New). %% @see values/1 -spec values() -> optic:optic(). values() -> values(#{}). %% @doc %% Focus on all values of a map. %% %% Example: %% %% ``` %% > optic:get([optic_maps:values()], #{first => 1, second => 2). %% {ok,[1,2]} %% ''' %% @end %% @param Options Common optic options. %% @returns An opaque optic record. -spec values(optic:extend_options()) -> optic:optic(). values(Options) -> Fold = fun (Fun, Acc, Map) when is_map(Map) -> {ok, maps:fold(fun (_Key, Value, InnerAcc) -> Fun(Value, InnerAcc) end, Acc, Map)}; (_Fun, _Acc, _Data) -> {error, undefined} end, MapFold = fun (Fun, Acc, Map) when is_map(Map) -> {ok, maps:fold(fun (Key, Value, {InnerMap, InnerAcc}) -> {NewValue, NewAcc} = Fun(Value, InnerAcc), {InnerMap#{Key=>NewValue}, NewAcc} end, {#{}, Acc}, Map)}; (_Fun, _Acc, _Data) -> {error, undefined} end, New = fun (_Data, _Template) -> #{} end, Optic = optic:new(MapFold, Fold), optic:'%extend'(Optic, Options, New). %% @see associations/1 -spec associations() -> optic:optic(). associations() -> associations(#{}). %% @doc %% Focus on all associations of a map. An association is a tuple of %% the key and value for each entry. %% %% Example: %% %% ``` %% > optic:get([optic_maps:associations()], #{first => 1, second => 2). %% {ok,[{first,1},{second,2}]} %% ''' %% @end %% @param Options Common optic options. %% @returns An opaque optic record. -spec associations(optic:extend_options()) -> optic:optic(). associations(Options) -> Fold = fun (Fun, Acc, Map) when is_map(Map) -> {ok, maps:fold(fun (Key, Value, InnerAcc) -> Fun({Key, Value}, InnerAcc) end, Acc, Map)}; (_Fun, _Acc, _Data) -> {error, undefined} end, MapFold = fun (Fun, Acc, Map) when is_map(Map) -> {ok, maps:fold(fun (Key, Value, {InnerMap, InnerAcc}) -> {{NewKey, NewValue}, NewAcc} = Fun({Key, Value}, InnerAcc), {InnerMap#{NewKey=>NewValue}, NewAcc} end, {#{}, Acc}, Map)}; (_Fun, _Acc, _Data) -> {error, undefined} end, New = fun (_Data, _Template) -> #{} end, Optic = optic:new(MapFold, Fold), optic:'%extend'(Optic, Options, New). %% @see key/2 -spec key(term()) -> optic:optic(). key(Key) -> key(Key, #{}). %% @doc %% Focus on the value of a map key. %% %% Example: %% %% ``` %% > optic:get([optic_maps:key(first)], #{first => 1, second => 2). %% {ok,[1]} %% ''' %% @end %% @param Key The key to focus on. %% @param Options Common optic options. %% @returns An opaque optic record. -spec key(term(), optic:extend_options()) -> optic:optic(). key(Key, Options) -> Fold = fun (Fun, Acc, #{Key:=Value}) -> {ok, Fun(Value, Acc)}; (_Fun, _Acc, _Data) -> {error, undefined} end, MapFold = fun (Fun, Acc, #{Key:=Value} = Map) when is_map(Map) -> {NewValue, NewAcc} = Fun(Value, Acc), {ok, {Map#{Key:=NewValue}, NewAcc}}; (_Fun, _Acc, _Data) -> {error, undefined} end, New = fun (Map, Template) when is_map(Map) -> Map#{Key=>Template}; (_Data, Template) -> #{Key=>Template} end, Optic = optic:new(MapFold, Fold), optic:'%extend'(Optic, Options, New). %% @see association/2 -spec association(term()) -> optic:optic(). association(Key) -> association(Key, #{}). %% @doc %% Focus on the association for a map key. An association is the tuple %% of a map key and value. If the key is modified, the optic is no %% longer well behaved. %% %% Example: %% %% ``` %% > optic:get([optic_maps:association(first)], #{first => 1, second => 2). %% {ok,[{first,1}]} %% ''' %% @end %% @param Key The key to focus on. %% @param Options Common optic options. %% @returns An opaque optic record. -spec association(term(), optic:extend_options()) -> optic:optic(). association(Key, Options) -> Fold = fun (Fun, Acc, #{Key:=Value}) -> {ok, Fun({Key, Value}, Acc)}; (_Fun, _Acc, _Data) -> {error, undefined} end, MapFold = fun (Fun, Acc, #{Key:=Value} = Map) when is_map(Map) -> {{NewKey, NewValue}, NewAcc} = Fun({Key, Value}, Acc), {ok, {(maps:remove(Key, Map))#{NewKey=>NewValue}, NewAcc}}; (_Fun, _Acc, _Data) -> {error, undefined} end, New = fun (Map, Template) when is_map(Map) -> Map#{Key=>Template}; (_Data, Template) -> #{Key=>Template} end, Optic = optic:new(MapFold, Fold), optic:'%extend'(Optic, Options, New).