Packages

Gleam module that provides primitives for caching key-value pairs in files and using files as caches.

Current section

Files

Jump to
hardcache src hardcache.erl
Raw

src/hardcache.erl

-module(hardcache).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([get/2, get_unwrap/3, try_get/2, try_get_unwrap/3, auto_update/1, stringify_entries/1, update/1, remove/2, try_remove/2, set/3, defaults/2, try_defaults/2, try_set/3, set_many/2, try_set_many/2, try_update/1, parse_entries/1, new/2]).
-export_type([cache/0, cache_error/0]).
-type cache() :: {cache, binary(), list({binary(), binary()}), boolean()}.
-type cache_error() :: {file_error,
file_streams@file_stream_error:file_stream_error()}.
-spec get(cache(), binary()) -> gleam@option:option(binary()).
get(Cache, Key) ->
case gleam@list:key_find(erlang:element(3, Cache), Key) of
{ok, Value} ->
{some, Value};
{error, _} ->
none
end.
-spec get_unwrap(cache(), binary(), binary()) -> binary().
get_unwrap(Orig, Key, When_none) ->
case get(Orig, Key) of
{some, String} ->
String;
none ->
When_none
end.
-spec try_get({ok, cache()} | {error, cache_error()}, binary()) -> {ok,
gleam@option:option(binary())} |
{error, cache_error()}.
try_get(Orig, Key) ->
case Orig of
{ok, Orig@1} ->
{ok, get(Orig@1, Key)};
{error, Err} ->
{error, Err}
end.
-spec try_get_unwrap({ok, cache()} | {error, cache_error()}, binary(), binary()) -> binary().
try_get_unwrap(Orig, Key, When_none_or_error) ->
case try_get(Orig, Key) of
{ok, Option_string} ->
case Option_string of
{some, String} ->
String;
none ->
When_none_or_error
end;
{error, _} ->
When_none_or_error
end.
-spec auto_update(cache()) -> cache().
auto_update(Cache) ->
case erlang:element(4, Cache) of
true ->
Cache;
false ->
{cache, erlang:element(2, Cache), erlang:element(3, Cache), true}
end.
-spec stringify_entries(list({binary(), binary()})) -> binary().
stringify_entries(Entries) ->
case Entries of
[] ->
<<""/utf8>>;
[Entry | Tail] ->
<<<<<<<<<<"!"/utf8, (erlang:element(1, Entry))/binary>>/binary,
"="/utf8>>/binary,
(erlang:element(2, Entry))/binary>>/binary,
"\n"/utf8>>/binary,
(stringify_entries(Tail))/binary>>
end.
-spec update(cache()) -> {ok, cache()} | {error, cache_error()}.
update(Cache) ->
case file_streams@file_stream:open_write(erlang:element(2, Cache)) of
{ok, Stream} ->
case file_streams@file_stream:write_bytes(
Stream,
<<(stringify_entries(erlang:element(3, Cache)))/binary>>
) of
{ok, _} ->
case file_streams@file_stream:sync(Stream) of
{ok, _} ->
case file_streams@file_stream:close(Stream) of
{ok, _} ->
{ok, Cache};
{error, Fe} ->
{error, {file_error, Fe}}
end;
{error, Fe@1} ->
{error, {file_error, Fe@1}}
end;
{error, Fe@2} ->
{error, {file_error, Fe@2}}
end;
{error, Fe@3} ->
{error, {file_error, Fe@3}}
end.
-spec remove(cache(), binary()) -> {ok, cache()} | {error, cache_error()}.
remove(Orig, Key) ->
case gleam@list:key_pop(erlang:element(3, Orig), Key) of
{ok, {_, []}} ->
{ok, Orig};
{ok, {_, Rest}} ->
Result = {cache,
erlang:element(2, Orig),
Rest,
erlang:element(4, Orig)},
case erlang:element(4, Result) of
true ->
case update(Result) of
{ok, Result@1} ->
{ok, Result@1};
Err ->
Err
end;
false ->
{ok, Result}
end;
{error, _} ->
{ok, Orig}
end.
-spec try_remove({ok, cache()} | {error, cache_error()}, binary()) -> {ok,
cache()} |
{error, cache_error()}.
try_remove(Orig, Key) ->
case Orig of
{ok, Orig@1} ->
remove(Orig@1, Key);
Err ->
Err
end.
-spec set(cache(), binary(), binary()) -> {ok, cache()} | {error, cache_error()}.
set(Orig, Key, Value) ->
Result = {cache,
erlang:element(2, Orig),
gleam@list:key_set(erlang:element(3, Orig), Key, Value),
erlang:element(4, Orig)},
case erlang:element(4, Result) of
true ->
case update(Result) of
{ok, Result@1} ->
{ok, Result@1};
Err ->
Err
end;
false ->
{ok, Result}
end.
-spec defaults(cache(), list({binary(), binary()})) -> {ok, cache()} |
{error, cache_error()}.
defaults(Orig, Entries) ->
case Entries of
[] ->
{ok, Orig};
[Entry | Tail] ->
case get(Orig, erlang:element(1, Entry)) of
{some, _} ->
defaults(Orig, Tail);
none ->
case set(
Orig,
erlang:element(1, Entry),
erlang:element(2, Entry)
) of
{ok, Result} ->
defaults(Result, Tail);
Err ->
Err
end
end
end.
-spec try_defaults(
{ok, cache()} | {error, cache_error()},
list({binary(), binary()})
) -> {ok, cache()} | {error, cache_error()}.
try_defaults(Orig, Entries) ->
case Orig of
{ok, Orig@1} ->
defaults(Orig@1, Entries);
Err ->
Err
end.
-spec try_set({ok, cache()} | {error, cache_error()}, binary(), binary()) -> {ok,
cache()} |
{error, cache_error()}.
try_set(Orig, Key, Value) ->
case Orig of
{ok, Orig@1} ->
set(Orig@1, Key, Value);
Err ->
Err
end.
-spec set_many(cache(), list({binary(), binary()})) -> {ok, cache()} |
{error, cache_error()}.
set_many(Orig, Entries) ->
case Entries of
[] ->
{ok, Orig};
[Entry | Tail] ->
case set(Orig, erlang:element(1, Entry), erlang:element(2, Entry)) of
{ok, Result} ->
set_many(Result, Tail);
Fe ->
Fe
end
end.
-spec try_set_many(
{ok, cache()} | {error, cache_error()},
list({binary(), binary()})
) -> {ok, cache()} | {error, cache_error()}.
try_set_many(Orig, Entries) ->
case Orig of
{ok, Orig@1} ->
set_many(Orig@1, Entries);
Err ->
Err
end.
-spec try_update({ok, cache()} | {error, cache_error()}) -> {ok, cache()} |
{error, cache_error()}.
try_update(Cache) ->
case Cache of
{ok, Cache@1} ->
update(Cache@1);
Err ->
Err
end.
-spec create_file(binary()) -> {ok, nil} |
{error, file_streams@file_stream_error:file_stream_error()}.
create_file(File_path) ->
case file_streams@file_stream:open_write(File_path) of
{ok, Stream} ->
file_streams@file_stream:close(Stream);
{error, Fe} ->
{error, Fe}
end.
-spec read_all(file_streams@file_stream:file_stream()) -> {ok, binary()} |
{error, cache_error()}.
read_all(Stream) ->
case file_streams@file_stream:read_chars(Stream, 1024) of
{ok, Char} ->
case file_streams@file_stream:close(Stream) of
{ok, _} ->
case read_all(Stream) of
{ok, Next} ->
{ok, <<Char/binary, Next/binary>>};
Err ->
Err
end;
{error, Fe} ->
{error, {file_error, Fe}}
end;
_ ->
{ok, <<""/utf8>>}
end.
-spec parse_line(binary()) -> {ok, {binary(), binary()}} | {error, nil}.
parse_line(Line) ->
Parts = gleam@string:split(Line, <<"="/utf8>>),
case Parts of
[Key | Value] ->
case gleam@string:first(Key) of
{ok, <<"!"/utf8>>} ->
{ok,
{gleam@string:drop_left(Key, 1),
gleam@string:join(Value, <<"="/utf8>>)}};
_ ->
{error, nil}
end;
_ ->
{error, nil}
end.
-spec parse_entries(binary()) -> list({binary(), binary()}).
parse_entries(String) ->
_pipe = gleam@string:split(String, <<"\n"/utf8>>),
gleam@list:filter_map(_pipe, fun parse_line/1).
-spec new(binary(), boolean()) -> {ok, cache()} | {error, cache_error()}.
new(File_path, Auto_update) ->
case file_streams@file_stream:open_read(File_path) of
{ok, Stream} ->
case read_all(Stream) of
{ok, Content} ->
{ok,
{cache, File_path, parse_entries(Content), Auto_update}};
{error, Err} ->
{error, Err}
end;
{error, enoent} ->
case create_file(File_path) of
{ok, _} ->
new(File_path, Auto_update);
{error, Fe} ->
{error, {file_error, Fe}}
end;
{error, Fe@1} ->
{error, {file_error, Fe@1}}
end.