Skip to content

Commit

Permalink
Merge pull request #24 from hmmr/fqdn-datatype
Browse files Browse the repository at this point in the history
new datatype 'fqdn'
  • Loading branch information
michaelklishin authored Mar 24, 2021
2 parents a0a3ed5 + f43e9fd commit f62037c
Showing 1 changed file with 63 additions and 0 deletions.
63 changes: 63 additions & 0 deletions src/cuttlefish_datatypes.erl
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
{flag, {atom(), term()}, {atom(), term()}} |
{enum, [atom()]} |
ip |
fqdn |
{duration, cuttlefish_duration:time_unit() } |
bytesize |
{percent, integer} |
Expand Down Expand Up @@ -76,6 +77,7 @@ is_supported({flag, {On, _}, {Off, _}}) when is_atom(On), is_atom(Off) -> true;
is_supported(atom) -> true;
is_supported({enum, E}) when is_list(E) -> true;
is_supported(ip) -> true;
is_supported(fqdn) -> true;
is_supported({duration, f}) -> true;
is_supported({duration, w}) -> true;
is_supported({duration, d}) -> true;
Expand All @@ -102,6 +104,8 @@ is_extended({file, F}) when is_list(F) -> true;
is_extended({directory, D}) when is_list(D) -> true;
is_extended({ip, {IP, Port}}) when is_list(IP) andalso is_integer(Port) -> true;
is_extended({ip, StringIP}) when is_list(StringIP) -> true;
is_extended({fqdn, {FQDN, Port}}) when is_list(FQDN) andalso is_integer(Port) -> true;
is_extended({fqdn, StringFQDN}) when is_list(StringFQDN) -> true;
is_extended({{duration, f}, D}) when is_list(D) -> true;
is_extended({{duration, w}, D}) when is_list(D) -> true;
is_extended({{duration, d}, D}) when is_list(D) -> true;
Expand All @@ -122,6 +126,7 @@ extended_from({atom, _}) -> atom;
extended_from({file, _}) -> file;
extended_from({directory, _}) -> directory;
extended_from({ip, _}) -> ip;
extended_from({fqdn, _}) -> fqdn;
extended_from({{duration, Unit}, _}) -> {duration, Unit};
extended_from({bytesize, _}) -> bytesize;
extended_from({{percent, integer}, _}) -> {percent, integer};
Expand Down Expand Up @@ -155,6 +160,9 @@ to_string(Integer, integer) when is_list(Integer) -> Integer;
to_string({IP, Port}, ip) when is_list(IP), is_integer(Port) -> IP ++ ":" ++ integer_to_list(Port);
to_string(IPString, ip) when is_list(IPString) -> IPString;

to_string({FQDN, Port}, fqdn) when is_list(FQDN), is_integer(Port) -> FQDN ++ ":" ++ integer_to_list(Port);
to_string(FQDNString, fqdn) when is_list(FQDNString) -> FQDNString;

to_string(Enum, {enum, _}) when is_list(Enum) -> Enum;
to_string(Enum, {enum, _}) when is_atom(Enum) -> atom_to_list(Enum);

Expand Down Expand Up @@ -217,6 +225,10 @@ from_string({IP, Port}, ip) when is_list(IP), is_integer(Port) -> {IP, Port};
from_string(String, ip) when is_list(String) ->
from_string_to_ip(String, lists:split(string:rchr(String, $:), String));

from_string({FQDN, Port}, fqdn) when is_list(FQDN), is_integer(Port) -> {FQDN, Port};
from_string(String, fqdn) when is_list(String) ->
from_string_to_fqdn(String, lists:split(string:rchr(String, $:), String));

from_string(Duration, {duration, _}) when is_integer(Duration) -> Duration;
from_string(Duration, {duration, Unit}) when is_list(Duration) -> cuttlefish_duration:parse(Duration, Unit);

Expand Down Expand Up @@ -294,6 +306,17 @@ ip_conversions(String, _IPStr, _IP, undefined) ->
ip_conversions(_String, IPStr, {ok, _}, Port) ->
{IPStr, Port}.

fqdn_conversions(String, _FQDNStr, nomatch, _Port) ->
{error, {conversion, {String, 'FQDN'}}};
fqdn_conversions(String, _FQDNStr, _, undefined) ->
{error, {conversion, {String, 'FQDN'}}};
fqdn_conversions(_String, FQDNStr, {match, _}, Port) ->
{FQDNStr, Port}.

validate_fqdn(Str) ->
%% inspired by https://regexr.com/3g5j0, amended to disallow [:space:]
re:run(Str, "^(?!:\/\/)(?=[^[:space:]]{1,255}$)((.{1,63}\.){1,127}(?![0-9]*$)[a-z0-9-]+\.?)$").

droplast(List) ->
lists:sublist(List, length(List)-1).

Expand All @@ -306,6 +329,12 @@ from_string_to_ip(String, {IpPlusColon, PortString}) ->
IP = droplast(IpPlusColon),
ip_conversions(String, IP, inet:parse_address(IP), port_to_integer(PortString)).

from_string_to_fqdn(String, {[], String}) ->
{error, {conversion, {String, 'FQDN'}}};
from_string_to_fqdn(String, {FQDNPlusColon, PortString}) ->
FQDN = droplast(FQDNPlusColon),
fqdn_conversions(String, FQDN, validate_fqdn(FQDN), port_to_integer(PortString)).


-ifdef(TEST).

Expand Down Expand Up @@ -360,6 +389,8 @@ to_string_extended_type_test() ->
?assertEqual("32", to_string("32", {integer, 32})),
?assertEqual("127.0.0.1:8098", to_string("127.0.0.1:8098", {ip, "127.0.0.1:8098"})),
?assertEqual("127.0.0.1:8098", to_string({"127.0.0.1", 8098}, {ip, {"127.0.0.1", 8098}})),
?assertEqual("example.com:8098", to_string("example.com:8098", {fqdn, "example.com:8098"})),
?assertEqual("example.com:8098", to_string({"example.com", 8098}, {fqdn, {"example.com", 8098}})),
?assertEqual("string", to_string("string", {string, "string"})),
?assertEqual("1w", to_string("1w", {{duration, s}, "1w"})),
?assertEqual("1w", to_string(604800000, {{duration, ms}, "1w"})),
Expand Down Expand Up @@ -417,6 +448,38 @@ from_string_ip_test() ->
BadIPs),
ok.

from_string_fqdn_test() ->
?assertEqual({"fqdn.com", 8098}, from_string("fqdn.com:8098", fqdn)),
?assertEqual(
{"f.q.d.n.com", 8098},
from_string("f.q.d.n.com:8098", fqdn)),
?assertEqual(
{"fqdn.com.", 8098},
from_string("fqdn.com.:8098", fqdn)),
?assertEqual(
{"FqDn.com", 8098},
from_string("FqDn.com:8098", fqdn)),
?assertEqual(
{"ec2-35-160-210-253.us-west-2-.compute.amazonaws.com", 1},
from_string("ec2-35-160-210-253.us-west-2-.compute.amazonaws.com:1", fqdn)),

BadFQDNs = [
"This is not an fqdn:80",
"This.is not.an.fqdn:80",
"",
"127.0.0.1:80",
"fqdn.com", %% No port
"fqdn.com:-5",
"fqdn.com:80:81"
],

lists:foreach(fun(Bad) ->
?assertEqual({error, {conversion, {Bad, 'FQDN'}}},
from_string(Bad, fqdn))
end,
BadFQDNs),
ok.

from_string_enum_test() ->
?assertEqual("\"a\" is not a valid enum value, acceptable values are: b, c", ?XLATE(from_string(a, {enum, [b, c]}))),
?assertEqual(true, from_string("true", {enum, [true, false]})),
Expand Down

0 comments on commit f62037c

Please sign in to comment.