-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, 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, auto_update/1, default_encoder/1, new_custom_format/4, default_decoder/1, new/2]). -export_type([cache/0, cache_error/0]). -type cache() :: {cache, binary(), list({binary(), binary()}), boolean(), fun((binary()) -> list({binary(), binary()})), fun((list({binary(), binary()})) -> binary())}. -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 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, <<((erlang:element(6, Cache))(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, {_, Rest}} -> Result = {cache, erlang:element(2, Orig), Rest, erlang:element(4, Orig), erlang:element(5, Orig), erlang:element(6, 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), erlang:element(5, Orig), erlang:element(6, 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 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, erlang:element(5, Cache), erlang:element(6, Cache)} end. -spec default_encoder(list({binary(), binary()})) -> binary(). default_encoder(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, (default_encoder(Tail))/binary>> 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, <>}; Err -> Err end; {error, Fe} -> {error, {file_error, Fe}} end; _ -> {ok, <<""/utf8>>} end. -spec new_custom_format( binary(), boolean(), fun((binary()) -> list({binary(), binary()})), fun((list({binary(), binary()})) -> binary()) ) -> {ok, cache()} | {error, cache_error()}. new_custom_format(File_path, Auto_update, Decode, Encode) -> case file_streams@file_stream:open_read(File_path) of {ok, Stream} -> case read_all(Stream) of {ok, Content} -> {ok, {cache, File_path, Decode(Content), Auto_update, Decode, Encode}}; {error, Err} -> {error, Err} end; {error, enoent} -> case create_file(File_path) of {ok, _} -> new_custom_format(File_path, Auto_update, Decode, Encode); {error, Fe} -> {error, {file_error, Fe}} end; {error, Fe@1} -> {error, {file_error, Fe@1}} 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 default_decoder(binary()) -> list({binary(), binary()}). default_decoder(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) -> new_custom_format( File_path, Auto_update, fun default_decoder/1, fun default_encoder/1 ).