Current section
Files
Jump to
Current section
Files
src/ktn_random.erl
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% ktn_random: a gen_server for generating random alfanumeric strings
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-module(ktn_random).
-behaviour(gen_server).
-export([
start_link/0,
generate/0,
generate/1,
uniform/1,
uniform/2,
pick/1
]).
-export([
init/1,
terminate/2,
code_change/3,
handle_call/3,
handle_cast/2,
handle_info/2
]).
-type state() :: {}.
-spec start_link() -> {ok, pid()}.
start_link() ->
Seed = os:timestamp(),
gen_server:start_link({local, ?MODULE}, ?MODULE, Seed, []).
-spec generate() -> nonempty_string().
generate() ->
gen_server:call(?MODULE, random_string).
-spec generate(pos_integer()) -> nonempty_string().
generate(Length) ->
gen_server:call(?MODULE, {random_string, Length}).
-spec uniform(term()) -> non_neg_integer() | {error, {invalid_value, term()}}.
uniform(Max) ->
gen_server:call(?MODULE, {random_uniform, Max}).
-spec uniform(term(), term()) ->
non_neg_integer() | {error, {invalid_range, term(), term()}}.
uniform(Min, Max) ->
gen_server:call(?MODULE, {random_uniform, Min, Max}).
%% @doc Randomly chooses one element from the list
-spec pick([X, ...]) -> X.
pick(List) -> lists:nth(uniform(length(List)), List).
%% Callback implementation
-spec init(erlang:timestamp()) -> {ok, state()}.
init(Seed) ->
_ = random:seed(Seed),
{ok, {}}. % no state necessary, random uses process dictionary
-spec handle_call
(random_string, _, state()) ->
{reply, nonempty_string(), state()};
({random_string, pos_integer()}, _, state()) ->
{reply, nonempty_string(), state()};
({random_uniform, non_neg_integer()}, _, state()) ->
{reply, non_neg_integer(), state()};
({random_uniform, non_neg_integer(), non_neg_integer()}, _, state()) ->
{reply, non_neg_integer(), state()}.
handle_call(random_string, _From, State) ->
{reply, random_string(), State};
handle_call({random_string, Length}, _From, State) ->
{reply, random_string(Length), State};
handle_call({random_uniform, Max}, _From, State) ->
{reply, random_uniform(Max), State};
handle_call({random_uniform, Min, Max}, _From, State) ->
{reply, random_uniform(Min, Max), State}.
%% Unused callbacks
-spec handle_cast(_, state()) -> {noreply, state()}.
handle_cast(_Msg, State) -> {noreply, State}.
-spec handle_info(_, state()) -> {noreply, state()}.
handle_info(_Msg, State) -> {noreply, State}.
-spec terminate(_, state()) -> ok.
terminate(_Reason, _State) -> ok.
-spec code_change(_, state(), _) -> {ok, state()}.
code_change(_OldVersion, State, _Extra) -> {ok, State}.
%% internal
random_string() ->
Length = get_random_length(),
random_string_cont(Length).
random_string(Length) ->
random_string_cont(Length).
random_string_cont(Length) ->
RandomAllowedChars = get_random_allowed_chars(),
[ random_alphanumeric(RandomAllowedChars)
|| _N <- lists:seq(1, Length)
].
random_uniform(Max) when Max > 0->
random:uniform(Max);
random_uniform(Max) ->
{error, {invalid_value, Max}}.
random_uniform(Min, Max) when Max > Min ->
Min + random:uniform(Max - Min + 1) - 1;
random_uniform(Min, Max) ->
{error, {invalid_range, Min, Max}}.
%% internal
random_alphanumeric(AllowedChars) ->
Length = erlang:length(AllowedChars),
lists:nth(random:uniform(Length), AllowedChars).
get_random_length() ->
case application:get_env(katana, random_length) of
{ok, SecretLength} ->
SecretLength;
undefined ->
16
end.
get_random_allowed_chars() ->
case application:get_env(katana, random_allowed_chars) of
{ok, RandomAllowedChars} ->
RandomAllowedChars;
undefined ->
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
end.