-module(envie@parse). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/envie/parse.gleam"). -export([parse_dotenv/1]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. -file("src/envie/parse.gleam", 134). -spec do_process_escapes(list(binary()), binary()) -> binary(). do_process_escapes(Chars, Acc) -> case Chars of [] -> Acc; [<<"\\"/utf8>>, <<"n"/utf8>> | Rest] -> do_process_escapes(Rest, <>); [<<"\\"/utf8>>, <<"t"/utf8>> | Rest@1] -> do_process_escapes(Rest@1, <>); [<<"\\"/utf8>>, <<"\\"/utf8>> | Rest@2] -> do_process_escapes(Rest@2, <>); [<<"\\"/utf8>>, <<"\""/utf8>> | Rest@3] -> do_process_escapes(Rest@3, <>); [<<"\\"/utf8>>, C | Rest@4] -> do_process_escapes( Rest@4, <<<>/binary, C/binary>> ); [C@1 | Rest@5] -> do_process_escapes(Rest@5, <>) end. -file("src/envie/parse.gleam", 130). ?DOC( " Process escape sequences inside double-quoted values.\n" "\n" " Supported: `\\\\n` → newline, `\\\\t` → tab, `\\\\\\\\` → backslash,\n" " `\\\\\"` → double-quote. Unknown sequences are kept as-is.\n" ). -spec process_escapes(binary()) -> binary(). process_escapes(S) -> do_process_escapes(gleam@string:to_graphemes(S), <<""/utf8>>). -file("src/envie/parse.gleam", 104). -spec parse_quoted(list(binary()), binary(), binary(), boolean()) -> binary(). parse_quoted(Chars, Quote, Acc, Process) -> case Chars of [] -> Acc; [Q | _] when Q =:= Quote -> case Process of true -> process_escapes(Acc); false -> Acc end; [<<"\\"/utf8>>, C | Rest] when Process -> parse_quoted( Rest, Quote, <<<>/binary, C/binary>>, Process ); [C@1 | Rest@1] -> parse_quoted(Rest@1, Quote, <>, Process) end. -file("src/envie/parse.gleam", 149). ?DOC( " Remove an inline comment from an unquoted value.\n" "\n" " `8080 # default port` → `8080`\n" ). -spec strip_inline_comment(binary()) -> binary(). strip_inline_comment(S) -> case gleam@string:split_once(S, <<" #"/utf8>>) of {ok, {Before, _}} -> gleam@string:trim_end(Before); {error, _} -> S end. -file("src/envie/parse.gleam", 96). -spec unquote(binary()) -> binary(). unquote(S) -> case gleam@string:to_graphemes(S) of [<<"\""/utf8>> | Rest] -> parse_quoted(Rest, <<"\""/utf8>>, <<""/utf8>>, true); [<<"'"/utf8>> | Rest@1] -> parse_quoted(Rest@1, <<"'"/utf8>>, <<""/utf8>>, false); _ -> strip_inline_comment(S) end. -file("src/envie/parse.gleam", 61). -spec parse_assignment(integer(), binary()) -> {ok, gleam@option:option({binary(), binary()})} | {error, envie@error:parse_error()}. parse_assignment(Line_num, Line) -> Effective = case gleam_stdlib:string_starts_with(Line, <<"export "/utf8>>) of true -> gleam@string:slice(Line, 7, string:length(Line) - 7); false -> Line end, case gleam@string:split_once(Effective, <<"="/utf8>>) of {ok, {Raw_key, Raw_value}} -> Key = gleam@string:trim(Raw_key), case Key of <<""/utf8>> -> {error, {invalid_syntax, Line_num, Line, <<"Empty variable name"/utf8>>}}; _ -> Value = unquote(gleam@string:trim(Raw_value)), {ok, {some, {Key, Value}}} end; {error, _} -> {error, {invalid_syntax, Line_num, Line, <<"Expected KEY=value format"/utf8>>}} end. -file("src/envie/parse.gleam", 44). -spec parse_line(integer(), binary()) -> {ok, gleam@option:option({binary(), binary()})} | {error, envie@error:parse_error()}. parse_line(Line_num, Line) -> Trimmed = gleam@string:trim(Line), case Trimmed of <<""/utf8>> -> {ok, none}; _ -> case gleam_stdlib:string_starts_with(Trimmed, <<"#"/utf8>>) of true -> {ok, none}; false -> parse_assignment(Line_num, Trimmed) end end. -file("src/envie/parse.gleam", 22). -spec parse_lines( list({integer(), binary()}), list({binary(), binary()}), list(envie@error:parse_error()) ) -> {ok, list({binary(), binary()})} | {error, list(envie@error:parse_error())}. parse_lines(Lines, Vars, Errors) -> case Lines of [] -> case Errors of [] -> {ok, lists:reverse(Vars)}; _ -> {error, lists:reverse(Errors)} end; [{Line_num, Line} | Rest] -> case parse_line(Line_num, Line) of {ok, none} -> parse_lines(Rest, Vars, Errors); {ok, {some, Pair}} -> parse_lines(Rest, [Pair | Vars], Errors); {error, Err} -> parse_lines(Rest, Vars, [Err | Errors]) end end. -file("src/envie/parse.gleam", 13). ?DOC( " Parse a string in .env format into a list of `#(key, value)` pairs.\n" "\n" " Lines that are empty or start with `#` are silently skipped.\n" " Lines that do not contain an `=` produce a `ParseError`.\n" ). -spec parse_dotenv(binary()) -> {ok, list({binary(), binary()})} | {error, list(envie@error:parse_error())}. parse_dotenv(Content) -> _pipe = Content, _pipe@1 = gleam@string:split(_pipe, <<"\n"/utf8>>), _pipe@2 = gleam@list:index_map( _pipe@1, fun(Line, Idx) -> {Idx + 1, Line} end ), parse_lines(_pipe@2, [], []).