Commit 9d79deba authored by Siebers, Michael's avatar Siebers, Michael
Browse files

added get_dict_typed/4 for getting and type checking dict values

parent 035b9839
:- use_module(web_api).
/*************************************
* *
* Helpers generating correct values *
* *
*************************************/
%! type_is(?Type, -Value) is nondet
% Generates several Value's of type Type, If Type is unbound it is sequentially bound to
% the types atom, integer, string, chars, codes, text, and dict.
type_is(atom, Value) :- member(Value, [a, some_atom, 'an atom with spaces']).
type_is(integer, Value) :- member(Value, [-100, 0, 1, 5, 1000000000]).
type_is(string, Value) :- member(Value, ["some", "random strings", ""]).
type_is(chars, Value) :- type_is(atom, ValueAtom), atom_chars(ValueAtom, Value).
type_is(chars, Value) :- type_is(string, ValueStr), string_chars(ValueStr, Value).
type_is(codes, Value) :- type_is(atom, ValueAtom), atom_codes(ValueAtom, Value).
type_is(codes, Value) :- type_is(string, ValueStr), string_codes(ValueStr, Value).
type_is(text, Value) :-
type_is(atom, Value)
; type_is(string, Value)
; type_is(codes, Value)
; type_is(chars, Value).
type_is(dict, Value) :- between(0,2,N), type_is(dict, N, Value).
type_is(X, Value) :- X==dict_value, !,
(type_is(atom, Value)
; type_is(integer, Value)
).
type_is(dict, 0, _{}).
type_is(dict, 0, _{key: Value}) :- type_is(dict_value, Value).
type_is(dict, 0, _{key1: Value1, key2: Value2}) :-
type_is(dict_value, Value1),
type_is(dict_value, Value2).
type_is(dict, 1, _{key: Value}) :-
findall(V, type_is(dict, 0, V), Vs),
length(Vs, NVs),
randset(3, NVs, ValueIndices),
member(Pos, ValueIndices),
nth1(Pos, Vs, Value).
type_is(dict, 1, _{key1: Value1, key2: Value2}) :-
findall(V, type_is(dict, 0, V), Vs),
length(Vs, NVs),
randset(3, NVs, ValueIndices1),
member(Pos1, ValueIndices1),
nth1(Pos1, Vs, Value1),
randset(3, NVs, ValueIndices2),
member(Pos2, ValueIndices2),
nth1(Pos2, Vs, Value2).
type_is(dict, 2, _{key: Value}) :-
findall(V, type_is(dict, 1, V), Vs),
length(Vs, NVs),
randset(3, NVs, ValueIndices),
member(Pos, ValueIndices),
nth1(Pos, Vs, Value).
type_is(dict, 2, _{key1: Value1, key2: Value2}) :-
findall(V, type_is(dict, 1, V), Vs1),
findall(V, type_is(dict, 0, V), V0s),
append(V0s, Vs1, Vs2),
length(Vs1, NVs1),
randset(3, NVs1, ValueIndices1),
member(Pos1, ValueIndices1),
nth1(Pos1, Vs1, Value1),
length(Vs2, NVs2),
randset(10, NVs2, ValueIndices2),
member(Pos2, ValueIndices2),
nth1(Pos2, Vs2, Value2).
/***************************************
* *
* Helpers generating erroneous values *
* *
***************************************/
type_not_atom(Value) :- member(Value, [1, "test", 7.3, some(term)]).
type_not_dict(Value) :- member(Value, [
type_not(atom, Value) :- member(Value, [1, "test", 7.3, some(term)]).
type_not(dict, Value) :- member(Value, [
1,
test,
"test",
......@@ -15,7 +81,7 @@ type_not_dict(Value) :- member(Value, [
[key=value],
[key: value]
]).
type_not_integer(Value) :- member(Value, [
type_not(integer, Value) :- member(Value, [
an_atom,
"test",
1.0,
......@@ -23,22 +89,28 @@ type_not_integer(Value) :- member(Value, [
some(term),
["test"]
]).
type_not_list(Value) :- type_not_term('[|]'/2, Value).
type_not_text(Value) :- member(Value, [1, 7.3, some(term), ["test"]]).
type_not(list, Value) :- type_not_term('[|]'/2, Value).
type_not(text, Value) :- member(Value, [1, 7.3, some(term), ["test"]]).
type_not_term(_,Value) :- member(Value, [1, "test", test, 7.3]).
type_not_term(P/A, ["test"]) :- P/A \= '[|]'/2.
type_not_term(P/A, some(term)) :- P/A \= some/1.
type_not_term(P/A, Value) :-
type_not(term(_),Value) :- member(Value, [1, "test", test, 7.3]).
type_not(term(P/A), ["test"]) :- P/A \= '[|]'/2.
type_not(term(P/A), some(term)) :- P/A \= some/1.
type_not(term(P/A), Value) :-
succ(TooFew, A),
between(1,TooFew,N),
functor(Value,P,N),
numbervars(Value,0,_).
type_not_term(P/A, Value) :-
type_not(term(P/A), Value) :-
succ(A,TooMany),
functor(Value,P,TooMany),
numbervars(Value,0,_).
type_not_atom(Value) :- type_not(atom, Value).
type_not_dict(Value) :- type_not(dict, Value).
type_not_integer(Value) :- type_not(integer, Value).
type_not_list(Value) :- type_not(list, Value).
type_not_text(Value) :- type_not(text, Value).
type_not_term(P/A, Value) :- type_not(term(P/A), Value).
:- prolog_load_context(directory, Dir),
path_segments_atom(Dir/web_api/'*.plt',WebApiFiles),
......
......@@ -89,5 +89,90 @@ test(text_to_string_type,
error(type_error(text,Text))
]) :-
web_api:text_to_string(Text,_String).
/********************
* get_dict_typed/4 *
********************/
% get_dict_typed(+Dict, +Key, +Type, -Value) succeeds with the correct value on
% correct input
test(get_dict_typed_happy,
[ forall((
type_is(dict, RandomDict),
type_is(atom, Key),
type_is(Type, ExpectedValue),
false
)),
blocked(takes_too_long),
true(ExpectedValue =@= ActualValue)
]) :-
Dict = RandomDict.put([Key=ExpectedValue]),
web_api:get_dict_typed(Dict, Key, Type, ActualValue).
% get_dict_typed(+Dict, +Key, +Type, -Value) throws instantiation_error if Dict,
% Key, or Type is unbound.
test(get_dict_typed_instantiation_Dict,
[ error(instantiation_error)
]) :-
web_api:get_dict_typed(_, some_key, some_type, _ActualValue).
test(get_dict_typed_instantiation_Key,
[ error(instantiation_error)
]) :-
web_api:get_dict_typed(_{some_key: some_value}, _, some_type, _ActualValue).
test(get_dict_typed_instantiation_Type,
[ error(instantiation_error)
]) :-
web_api:get_dict_typed(_{some_key: some_value}, some_key, _, _ActualValue).
% get_dict_typed(+Dict, +Key, +Type, -Value) throws type_error(dict, Dict) if
% Dict is no dict.
test(get_dict_typed_type_Dict,
[ forall(type_not_dict(NoDict)),
error(type_error(dict, NoDict))
]) :-
web_api:get_dict_typed(NoDict, some_key, some_type, _ActualValue).
% get_dict_typed(+Dict, +Key, +Type, -Value) throws
% existence_error(key, Key, Dict) if Dict does not contain the key Key.
test(get_dict_typed_existence,
[ forall((
type_is(dict, Dict)
)),
error(existence_error(key, some_key, Dict))
]) :-
web_api:get_dict_typed(Dict, some_key, some_type, _ActualValue).
% get_dict_typed(+Dict, +Key, +Type, -Value) throws key_instantiation_error(Key)
% if Value is insufficiently instantiated
test(get_dict_typed_key_instantiation,
[ forall((
type_is(dict, RandomDict),
type_is(atom, Key),
type_is(Type, _ExpectedValue)
)),
error(key_instantiation_error(Key))
]) :-
Dict = RandomDict.put([Key=_]),
web_api:get_dict_typed(Dict, Key, Type, _ActualValue).
% get_dict_typed(+Dict, +Key, +Type, -Value) throws
% key_type_error(Key, Type, Value) if Value is not of type Type.
test(get_dict_typed_key_type_error,
[ forall((
type_is(dict, RandomDict),
type_is(atom, Key),
bagof(1,V^type_is(Type,V),_),
type_not(Type, Value)
)),
error(key_type_error(Key, Type, Value))
]) :-
Dict = RandomDict.put([Key=Value]),
web_api:get_dict_typed(Dict, Key, Type, _ActualValue).
:- end_tests('web_api -> helpers').
......@@ -101,9 +101,8 @@ server_opts(Opts) :-
% explanation may be created.
% @throws bad_request(missing_key(Key)) If a required parameter was missing
% in the JSON payload.
% @throws bad_request(wrong_type(abs_path, atom, Value))) If the Value
% associated with the key
% abs_path is not an atom.
% @throws bad_request(wrong_type(abs_path, atom))) If abs_path's value
% is not an atom.
% @throws bad_request(unknown_key(KeyName)) The unknown key KeyName was supplied
% in the request's payload.
% @throw server_error(E,Context) if an exception occured which is not the user's
......@@ -314,3 +313,33 @@ match_with(Tag, Field, Dict, Value) :- theory_bg:item(_,Dict), is_dict(Dict,Tag)
text_to_string(Text, String) :-
must_be(text, Text),
format(string(String), '~s', [Text]).
%! get_dict_typed(+Dict, +Key, +Type, -Value) is det
%
% Unify the value associated with Key in Dict with Value. Throws an existence
% error if Key does not appear in Dict. Additionally, validates whether Value
% is bound and of type Type. Type definitions are taken from must_be/2.
%
% @see get_dict/3
% @see must_be/2
% @error instantiation_error if Dict Key, or Type is unbound
% @error type_error(dict, Dict) if Dict is no dict
% @error existence_error(key, Key, Dict) if Dict does not contain the key Key
% @error key_instantiation_error(Key) if Value is insufficiently instantiated
% @error key_type_error(Key, Type, Value) if Value is not of type Type
get_dict_typed(Dict, Key, Type, Value) :-
( nonvar(Key)
-> true
; throw(error(instantiation_error,_))
),
must_be(dict,Dict),
Value = Dict.Key,
( nonvar(Value)
-> true
; throw(error(key_instantiation_error(Key), _))
),
( is_of_type(Type, Value)
-> true
; throw(error(key_type_error(Key, Type, Value), _))
).
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment