-module(dream@router@parser). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/dream/router/parser.gleam"). -export([parse_pattern/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. ?MODULEDOC( " Pattern Parser - Convert string paths to structured segments\n" "\n" " Parses route pattern strings (like \"/users/:id/posts/**path\") into\n" " structured segment lists that the radix trie can use for matching.\n" "\n" " ## Pattern Syntax\n" "\n" " - **Literal**: `users` - Matches exactly \"users\"\n" " - **Parameter**: `:id` - Captures one segment as \"id\"\n" " - **Single wildcard**: `*` or `*name` - Matches one segment\n" " - **Multi wildcard**: `**` or `**path` - Matches zero or more segments\n" " - **Extension**: `*.jpg` - Matches filenames ending in .jpg\n" " - **Multiple extensions**: `*.{jpg,png,gif}` - Matches any of the listed extensions\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " parse_pattern(\"/users/:id\")\n" " // [Literal(\"users\"), Param(\"id\")]\n" "\n" " parse_pattern(\"/files/**path\")\n" " // [Literal(\"files\"), MultiWildcard(Some(\"path\"))]\n" "\n" " parse_pattern(\"/images/*.{jpg,png}\")\n" " // [Literal(\"images\"), ExtensionPattern([\"jpg\", \"png\"])]\n" " ```\n" "\n" " ## Pattern Precedence\n" "\n" " When multiple routes could match the same path, the most specific wins:\n" " 1. Literal segments (highest priority)\n" " 2. Parameters\n" " 3. Single wildcards\n" " 4. Extension patterns\n" " 5. Multi-segment wildcards (lowest priority)\n" ). -file("src/dream/router/parser.gleam", 104). ?DOC( " Check if a segment is non-empty\n" "\n" " Used to filter out empty segments from multiple slashes or leading/trailing slashes.\n" ). -spec is_non_empty_segment(binary()) -> boolean(). is_non_empty_segment(Seg) -> Seg /= <<""/utf8>>. -file("src/dream/router/parser.gleam", 137). ?DOC( " Parse a segment that might be a literal or a literal with extension pattern\n" "\n" " Handles patterns like \"products.{json,xml}\" which should match\n" " \"products.json\" or \"products.xml\"\n" ). -spec parse_literal_or_extension(binary()) -> dream@router@trie:segment(). parse_literal_or_extension(Segment) -> case gleam@string:split_once(Segment, <<".{"/utf8>>) of {ok, {Base_name, Rest}} -> case gleam_stdlib:string_ends_with(Rest, <<"}"/utf8>>) of true -> Inner = gleam@string:drop_end(Rest, 1), Extensions = begin _pipe = Inner, _pipe@1 = gleam@string:split(_pipe, <<","/utf8>>), gleam@list:map(_pipe@1, fun gleam@string:trim/1) end, {literal_extension, Base_name, Extensions}; false -> {literal, Segment} end; {error, _} -> {literal, Segment} end. -file("src/dream/router/parser.gleam", 179). ?DOC( " Parse brace-delimited extensions: {jpg,png,gif}\n" "\n" " Extracts the comma-separated extensions from inside the braces,\n" " trimming whitespace from each extension.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " parse_brace_extensions(\"{jpg, png, gif}\")\n" " // ExtensionPattern([\"jpg\", \"png\", \"gif\"])\n" " ```\n" ). -spec parse_brace_extensions(binary()) -> dream@router@trie:segment(). parse_brace_extensions(Brace_pattern) -> Inner = begin _pipe = Brace_pattern, _pipe@1 = gleam@string:drop_start(_pipe, 1), gleam@string:drop_end(_pipe@1, 1) end, Extensions = begin _pipe@2 = Inner, _pipe@3 = gleam@string:split(_pipe@2, <<","/utf8>>), gleam@list:map(_pipe@3, fun gleam@string:trim/1) end, {extension_pattern, Extensions}. -file("src/dream/router/parser.gleam", 161). ?DOC( " Parse extension pattern from the part after \"*.\"\n" "\n" " Handles both single extensions (*.jpg) and multiple extensions (*.{jpg,png}).\n" ). -spec parse_extension_pattern(binary()) -> dream@router@trie:segment(). parse_extension_pattern(Ext_pattern) -> case gleam_stdlib:string_starts_with(Ext_pattern, <<"{"/utf8>>) of true -> parse_brace_extensions(Ext_pattern); false -> {extension_pattern, [Ext_pattern]} end. -file("src/dream/router/parser.gleam", 112). ?DOC( " Parse a single segment string into a Segment type\n" "\n" " Determines the segment type based on its prefix and structure.\n" " Handles all pattern syntaxes: parameters, wildcards, extensions, and literals.\n" ). -spec parse_segment(binary()) -> dream@router@trie:segment(). parse_segment(Segment) -> case Segment of <<":"/utf8, Name/binary>> -> {param, Name}; <<"**"/utf8>> -> {multi_wildcard, none}; <<"**"/utf8, Name@1/binary>> -> {multi_wildcard, {some, Name@1}}; <<"*."/utf8, Ext_pattern/binary>> -> parse_extension_pattern(Ext_pattern); <<"*"/utf8>> -> {single_wildcard, none}; <<"*"/utf8, Name@2/binary>> -> {single_wildcard, {some, Name@2}}; _ -> parse_literal_or_extension(Segment) end. -file("src/dream/router/parser.gleam", 90). ?DOC( " Parse a path pattern string into a list of segments\n" "\n" " Converts a string path pattern into structured segments that can be\n" " inserted into the radix trie. Handles all pattern types including\n" " literals, parameters, wildcards, and extension patterns.\n" "\n" " ## Parameters\n" "\n" " - `pattern`: Path pattern string (e.g., \"/users/:id\")\n" "\n" " ## Returns\n" "\n" " List of segments representing the pattern structure\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " // Simple literal path\n" " parse_pattern(\"/users\")\n" " // [Literal(\"users\")]\n" "\n" " // Parameter capture\n" " parse_pattern(\"/users/:id\")\n" " // [Literal(\"users\"), Param(\"id\")]\n" "\n" " // Multi-segment wildcard\n" " parse_pattern(\"/public/**filepath\")\n" " // [Literal(\"public\"), MultiWildcard(Some(\"filepath\"))]\n" "\n" " // Extension matching\n" " parse_pattern(\"/images/*.{jpg,png}\")\n" " // [Literal(\"images\"), ExtensionPattern([\"jpg\", \"png\"])]\n" " ```\n" "\n" " ## Pattern Details\n" "\n" " - Leading and trailing slashes are ignored\n" " - Empty segments (from multiple slashes) are filtered out\n" " - Parameter names start with `:` (e.g., `:id`)\n" " - Wildcards start with `*` for single-segment or `**` for multi-segment\n" " - Extensions use `*.ext` or `*.{ext1,ext2,ext3}` syntax\n" ). -spec parse_pattern(binary()) -> list(dream@router@trie:segment()). parse_pattern(Pattern) -> _pipe = Pattern, _pipe@1 = gleam@string:split(_pipe, <<"/"/utf8>>), _pipe@2 = gleam@list:filter(_pipe@1, fun is_non_empty_segment/1), gleam@list:map(_pipe@2, fun parse_segment/1).