diff --git a/src/ec_cnv.erl b/src/ec_cnv.erl new file mode 100644 index 0000000..a063e2a --- /dev/null +++ b/src/ec_cnv.erl @@ -0,0 +1,309 @@ +%% -*- erlang-indent-level: 4; indent-tabs-mode: nil; fill-column: 80 -*- +%%% Copyright 2012 Erlware, LLC. All Rights Reserved. +%%% +%%% This file is provided to you under the Apache License, +%%% Version 2.0 (the "License"); you may not use this file +%%% except in compliance with the License. You may obtain +%%% a copy of the License at +%%% +%%% http://www.apache.org/licenses/LICENSE-2.0 +%%% +%%% Unless required by applicable law or agreed to in writing, +%%% software distributed under the License is distributed on an +%%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%%% KIND, either express or implied. See the License for the +%%% specific language governing permissions and limitations +%%% under the License. +%%%--------------------------------------------------------------------------- +%%% @author Eric Merritt +%%% @copyright (C) 2012 Erlware, LLC. +%%% +-module(ec_cnv). + +%% API +-export([to_integer/1, + to_integer/2, + to_float/1, + to_float/2, + to_number/1, + to_list/1, + to_binary/1, + to_atom/1, + to_boolean/1, + is_true/1, + is_false/1]). + +-ifndef(NOTEST). +-include_lib("proper/include/proper.hrl"). +-endif. + +%%%=================================================================== +%%% API +%%%=================================================================== + +%% @doc +%% Automatic conversion of a term into integer type. The conversion +%% will round a float value if nonstrict is specified otherwise badarg +-spec to_integer(string() | binary() | integer() | float()) -> + integer(). +to_integer(X)-> + to_integer(X, nonstrict). + +-spec to_integer(string() | binary() | integer() | float(), + strict | nonstrict) -> + integer(). +to_integer(X, strict) + when erlang:is_float(X) -> + erlang:error(badarg); +to_integer(X, nonstrict) + when erlang:is_float(X) -> + erlang:round(X); +to_integer(X, S) + when erlang:is_binary(X) -> + to_integer(erlang:binary_to_list(X), S); +to_integer(X, S) + when erlang:is_list(X) -> + try erlang:list_to_integer(X) of + Result -> + Result + catch + error:badarg when S =:= nonstrict -> + erlang:round(erlang:list_to_float(X)) + end; +to_integer(X, _) + when erlang:is_integer(X) -> + X. + +%% @doc +%% Automatic conversion of a term into float type. badarg if strict +%% is defined and an integer value is passed. +-spec to_float(string() | binary() | integer() | float()) -> + float(). +to_float(X) -> + to_float(X, nonstrict). + +-spec to_float(string() | binary() | integer() | float(), + strict | nonstrict) -> + float(). +to_float(X, S) when is_binary(X) -> + to_float(erlang:binary_to_list(X), S); +to_float(X, S) + when erlang:is_list(X) -> + try erlang:list_to_float(X) of + Result -> + Result + catch + error:badarg when S =:= nonstrict -> + erlang:list_to_integer(X) * 1.0 + end; +to_float(X, strict) when + erlang:is_integer(X) -> + erlang:error(badarg); +to_float(X, nonstrict) + when erlang:is_integer(X) -> + X * 1.0; +to_float(X, _) when erlang:is_float(X) -> + X. + +%% @doc +%% Automatic conversion of a term into number type. +-spec to_number(binary() | string() | number()) -> + number(). +to_number(X) + when erlang:is_number(X) -> + X; +to_number(X) + when erlang:is_binary(X) -> + to_number(to_list(X)); +to_number(X) + when erlang:is_list(X) -> + try list_to_integer(X) of + Int -> Int + catch + error:badarg -> + list_to_float(X) + end. + +%% @doc +%% Automatic conversion of a term into Erlang list +-spec to_list(atom() | list() | binary() | integer() | float()) -> + list(). +to_list(X) + when erlang:is_float(X) -> + erlang:float_to_list(X); +to_list(X) + when erlang:is_integer(X) -> + erlang:integer_to_list(X); +to_list(X) + when erlang:is_binary(X) -> + erlang:binary_to_list(X); +to_list(X) + when erlang:is_atom(X) -> + erlang:atom_to_list(X); +to_list(X) + when erlang:is_list(X) -> + X. + +%% @doc +%% Known limitations: +%% Converting [256 | _], lists with integers > 255 +-spec to_binary(atom() | string() | binary() | integer() | float()) -> + binary(). +to_binary(X) + when erlang:is_float(X) -> + to_binary(to_list(X)); +to_binary(X) + when erlang:is_integer(X) -> + erlang:iolist_to_binary(integer_to_list(X)); +to_binary(X) + when erlang:is_atom(X) -> + erlang:list_to_binary(erlang:atom_to_list(X)); +to_binary(X) + when erlang:is_list(X) -> + erlang:iolist_to_binary(X); +to_binary(X) + when erlang:is_binary(X) -> + X. + +-spec to_boolean(binary() | string() | atom()) -> + boolean(). +to_boolean(<<"true">>) -> + true; +to_boolean("true") -> + true; +to_boolean(true) -> + true; +to_boolean(<<"false">>) -> + false; +to_boolean("false") -> + false; +to_boolean(false) -> + false. + +-spec is_true(binary() | string() | atom()) -> + boolean(). +is_true(<<"true">>) -> + true; +is_true("true") -> + true; +is_true(true) -> + true; +is_true(_) -> + false. + +-spec is_false(binary() | string() | atom()) -> + boolean(). +is_false(<<"false">>) -> + true; +is_false("false") -> + true; +is_false(false) -> + true; +is_false(_) -> + false. + +%% @doc +%% Automation conversion a term to an existing atom. badarg is +%% returned if the atom doesn't exist. the safer version, won't let +%% you leak atoms +-spec to_atom(atom() | list() | binary() | integer() | float()) -> + atom(). +to_atom(X) + when erlang:is_atom(X) -> + X; +to_atom(X) + when erlang:is_list(X) -> + erlang:list_to_existing_atom(X); +to_atom(X) -> + to_atom(to_list(X)). + +%%%=================================================================== +%%% API +%%%=================================================================== + +-ifndef(NOTEST). +-include_lib("eunit/include/eunit.hrl"). + +force_proper_test_() -> + {"Runs PropEr test during EUnit phase", + {timeout, 15000, [?_assertEqual([], proper:module(?MODULE))]}}. + +to_integer_test() -> + ?assertError(badarg, to_integer(1.5, strict)). + +to_float_test() -> + ?assertError(badarg, to_float(10, strict)). + +to_atom_test() -> + ?assertMatch(true, to_atom("true")), + ?assertMatch(true, to_atom(<<"true">>)), + ?assertMatch(false, to_atom(<<"false">>)), + ?assertMatch(false, to_atom(false)), + ?assertError(badarg, to_atom("hello_foo_bar_baz")), + + S = erlang:list_to_atom("1"), + ?assertMatch(S, to_atom(1)). + +to_boolean_test()-> + ?assertMatch(true, to_boolean(<<"true">>)), + ?assertMatch(true, to_boolean("true")), + ?assertMatch(true, to_boolean(true)), + ?assertMatch(false, to_boolean(<<"false">>)), + ?assertMatch(false, to_boolean("false")), + ?assertMatch(false, to_boolean(false)). + +%%% PropEr testing + +prop_to_integer() -> + ?FORALL({F, I}, {float(), integer()}, + begin + Is = [[Fun(N), N] || + Fun <- [fun to_list/1, + fun to_binary/1], + N <- [F, I]], + lists:all(fun([FN, N]) -> + erlang:is_integer(to_integer(N)) andalso + erlang:is_integer(to_integer(FN)) + end, Is) + end). + +prop_to_list() -> + ?FORALL({A, L, B, I, F}, {atom(), list(), binary(), integer(), float()}, + lists:all(fun(X) -> + erlang:is_list(to_list(X)) + end, [A, L, B, I, F])). + +prop_to_binary() -> + ?FORALL({A, L, B, I, F, IO}, {atom(), list(range(0,255)), binary(), + integer(), float(), iolist()}, + lists:all(fun(X) -> + erlang:is_binary(to_binary(X)) + end, [A, L, B, I, F, IO])). + +prop_iolist_t() -> + ?FORALL(IO, iolist(), erlang:is_binary(to_binary(IO))). + +prop_to_float() -> + ?FORALL({F, I}, {float(), integer()}, + begin + Fs = [[Fun(N), N] || + Fun <- [fun to_list/1, fun to_binary/1], + N <- [F, I]], + lists:all(fun([FN, N]) -> + erlang:is_float(to_float(N)) andalso + erlang:is_float(to_float(FN)) + end, Fs) + end). + +prop_to_number() -> + ?FORALL({F, I}, {float(), integer()}, + begin + Is = [[Fun(N), N] || + Fun <- [fun to_list/1, fun to_binary/1], + N <- [F, I] ], + lists:all(fun([FN, N]) -> + erlang:is_number(to_number(N)) andalso + erlang:is_number(to_number(FN)) + end, Is) + end). +-endif.