-module(dream@http@request). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/dream/http/request.gleam"). -export([method_to_string/1, parse_method/1, get_query_param/2, has_content_type/2, is_method/2, set_params/2, get_param/2, get_int_param/2, get_string_param/2]). -export_type([method/0, protocol/0, version/0, request/0, path_param/0]). -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( " HTTP request types and utilities\n" "\n" " Core request types and functions for working with HTTP requests in Dream.\n" " Includes request methods, path parameters, query parameters, and request inspection.\n" "\n" " ## Quick Example\n" "\n" " ```gleam\n" " import dream/http/request.{type Request}\n" "\n" " pub fn show_user(request: Request, context, services) {\n" " // Extract path parameter\n" " case request.get_int_param(request, \"id\") {\n" " Ok(id) -> {\n" " // Get query parameter\n" " let format = request.get_query_param(request.query, \"format\")\n" " \n" " // Access request data\n" " let method = request.method // Get, Post, etc.\n" " let path = request.path // \"/users/123\"\n" " let body = request.body // Request body as string\n" " \n" " // Build response...\n" " }\n" " Error(msg) -> // Handle error\n" " }\n" " }\n" " ```\n" ). -type method() :: post | get | put | delete | patch | options | head. -type protocol() :: http | https. -type version() :: http1 | http2 | http3. -type request() :: {request, method(), protocol(), version(), binary(), binary(), list({binary(), binary()}), gleam@option:option(binary()), gleam@option:option(integer()), gleam@option:option(binary()), binary(), list(dream@http@header:header()), list(dream@http@cookie:cookie()), gleam@option:option(binary()), gleam@option:option(integer())}. -type path_param() :: {path_param, binary(), binary(), gleam@option:option(binary()), {ok, integer()} | {error, nil}, {ok, float()} | {error, nil}}. -file("src/dream/http/request.gleam", 185). ?DOC(" Convert Method to its string representation\n"). -spec method_to_string(method()) -> binary(). method_to_string(Method) -> case Method of get -> <<"GET"/utf8>>; post -> <<"POST"/utf8>>; put -> <<"PUT"/utf8>>; delete -> <<"DELETE"/utf8>>; patch -> <<"PATCH"/utf8>>; options -> <<"OPTIONS"/utf8>>; head -> <<"HEAD"/utf8>> end. -file("src/dream/http/request.gleam", 198). ?DOC(" Parse a string to Method (case-insensitive)\n"). -spec parse_method(binary()) -> gleam@option:option(method()). parse_method(Str) -> case string:lowercase(Str) of <<"get"/utf8>> -> {some, get}; <<"post"/utf8>> -> {some, post}; <<"put"/utf8>> -> {some, put}; <<"delete"/utf8>> -> {some, delete}; <<"patch"/utf8>> -> {some, patch}; <<"options"/utf8>> -> {some, options}; <<"head"/utf8>> -> {some, head}; _ -> none end. -file("src/dream/http/request.gleam", 254). -spec match_query_key(binary(), binary(), binary()) -> gleam@option:option(binary()). match_query_key(Key, Name, Value) -> case Key =:= Name of true -> {some, Value}; false -> none end. -file("src/dream/http/request.gleam", 269). ?DOC( " Decode a URL-encoded component (key or value)\n" " \n" " Handles percent-encoded sequences (e.g., %20, %26) and plus signs (+ → space)\n" " Falls back to original string if decoding fails\n" ). -spec decode_url_component(binary()) -> binary(). decode_url_component(Component) -> With_spaces = gleam@string:replace(Component, <<"+"/utf8>>, <<" "/utf8>>), case gleam_stdlib:percent_decode(With_spaces) of {ok, Decoded} -> Decoded; {error, _} -> With_spaces end. -file("src/dream/http/request.gleam", 237). -spec parse_query_pair(binary(), binary()) -> gleam@option:option(binary()). parse_query_pair(Param, Name) -> case gleam@string:split(Param, <<"="/utf8>>) of [Key, Value] -> Decoded_key = decode_url_component(Key), Decoded_value = decode_url_component(Value), match_query_key(Decoded_key, Name, Decoded_value); [Key@1] -> Decoded_key@1 = decode_url_component(Key@1), match_query_key(Decoded_key@1, Name, <<""/utf8>>); _ -> none end. -file("src/dream/http/request.gleam", 221). -spec get_query_param_recursive(list(binary()), binary()) -> gleam@option:option(binary()). get_query_param_recursive(Params, Name) -> case Params of [] -> none; [Param | Rest] -> Result = parse_query_pair(Param, Name), case Result of {some, _} -> Result; none -> get_query_param_recursive(Rest, Name) end end. -file("src/dream/http/request.gleam", 217). ?DOC( " Get a query parameter value from the raw query string\n" " \n" " Properly decodes URL-encoded values (e.g., %20 → space, %26 → &)\n" " Returns None if the parameter is not found.\n" ). -spec get_query_param(binary(), binary()) -> gleam@option:option(binary()). get_query_param(Query, Name) -> get_query_param_recursive(gleam@string:split(Query, <<"&"/utf8>>), Name). -file("src/dream/http/request.gleam", 281). ?DOC(" Check if request has a specific content type\n"). -spec has_content_type(request(), binary()) -> boolean(). has_content_type(Request, Content_type) -> case erlang:element(14, Request) of {some, Actual_content_type} -> gleam_stdlib:contains_string(Actual_content_type, Content_type); none -> false end. -file("src/dream/http/request.gleam", 290). ?DOC(" Check if request method matches\n"). -spec is_method(request(), method()) -> boolean(). is_method(Request, Method) -> erlang:element(2, Request) =:= Method. -file("src/dream/http/request.gleam", 295). ?DOC(" Create a new request with updated params\n"). -spec set_params(request(), list({binary(), binary()})) -> request(). set_params(Request, New_params) -> {request, erlang:element(2, Request), erlang:element(3, Request), erlang:element(4, Request), erlang:element(5, Request), erlang:element(6, Request), New_params, erlang:element(8, Request), erlang:element(9, Request), erlang:element(10, Request), erlang:element(11, Request), erlang:element(12, Request), erlang:element(13, Request), erlang:element(14, Request), erlang:element(15, Request)}. -file("src/dream/http/request.gleam", 319). -spec extract_format(list(binary()), binary()) -> {binary(), gleam@option:option(binary())}. extract_format(Parts, Raw) -> case Parts of [Val, Ext] -> {Val, {some, Ext}}; _ -> {Raw, none} end. -file("src/dream/http/request.gleam", 305). ?DOC(" Parse a path parameter string into PathParam with format detection\n"). -spec parse_path_param(binary()) -> path_param(). parse_path_param(Raw) -> Parts = gleam@string:split(Raw, <<"."/utf8>>), {Value, Format} = extract_format(Parts, Raw), {path_param, Raw, Value, Format, gleam_stdlib:parse_int(Value), gleam_stdlib:parse_float(Value)}. -file("src/dream/http/request.gleam", 372). ?DOC( " Extract a path parameter by name\n" "\n" " Returns a `PathParam` with automatic type conversion and format detection.\n" " If the parameter doesn't exist, returns an error message.\n" "\n" " For common cases, use `get_int_param()` or `get_string_param()` instead,\n" " which return `Result(Int, String)` or `Result(String, String)` with\n" " custom error messages.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " // Route: /users/:id\n" " // Request: /users/123\n" " case get_param(request, \"id\") {\n" " Ok(param) -> {\n" " param.value // \"123\"\n" " param.as_int // Ok(123)\n" " }\n" " Error(msg) -> // handle error\n" " }\n" " ```\n" "\n" " ```gleam\n" " // Route: /users/:id\n" " // Request: /users/123.json\n" " case get_param(request, \"id\") {\n" " Ok(param) -> {\n" " param.value // \"123\"\n" " param.format // Some(\"json\")\n" " param.as_int // Ok(123)\n" " }\n" " Error(msg) -> // handle error\n" " }\n" " ```\n" "\n" " ```gleam\n" " // For simple integer extraction, use get_int_param:\n" " case get_int_param(request, \"id\") {\n" " Ok(id) -> show_user(id)\n" " Error(msg) -> json_response(status.bad_request, error_json(msg))\n" " }\n" " ```\n" ). -spec get_param(request(), binary()) -> {ok, path_param()} | {error, binary()}. get_param(Request, Name) -> case gleam@list:key_find(erlang:element(7, Request), Name) of {ok, Value} -> {ok, parse_path_param(Value)}; {error, _} -> {error, <<"Missing required path parameter: "/utf8, Name/binary>>} end. -file("src/dream/http/request.gleam", 401). -spec param_to_int(path_param(), binary()) -> {ok, integer()} | {error, binary()}. param_to_int(Param, Name) -> case erlang:element(5, Param) of {ok, Id} -> {ok, Id}; {error, _} -> {error, <>} end. -file("src/dream/http/request.gleam", 394). ?DOC( " Extract a path parameter as an integer\n" "\n" " Returns a Result with a custom error message if the parameter is missing\n" " or cannot be converted to an integer.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " // Route: /users/:id\n" " // Request: /users/123\n" " case get_int_param(request, \"id\") {\n" " Ok(id) -> show_user(id)\n" " Error(msg) -> json_response(status.bad_request, error_json(msg))\n" " }\n" " ```\n" ). -spec get_int_param(request(), binary()) -> {ok, integer()} | {error, binary()}. get_int_param(Request, Name) -> case get_param(Request, Name) of {ok, Param} -> param_to_int(Param, Name); {error, _} -> {error, <<<<"Missing "/utf8, Name/binary>>/binary, " parameter"/utf8>>} end. -file("src/dream/http/request.gleam", 432). -spec param_to_string(path_param(), binary()) -> {ok, binary()} | {error, binary()}. param_to_string(Param, _) -> {ok, erlang:element(3, Param)}. -file("src/dream/http/request.gleam", 422). ?DOC( " Extract a path parameter as a string\n" "\n" " Returns a Result with a custom error message if the parameter is missing.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " // Route: /users/:name\n" " // Request: /users/john\n" " case get_string_param(request, \"name\") {\n" " Ok(name) -> show_user_by_name(name)\n" " Error(msg) -> json_response(status.bad_request, error_json(msg))\n" " }\n" " ```\n" ). -spec get_string_param(request(), binary()) -> {ok, binary()} | {error, binary()}. get_string_param(Request, Name) -> case get_param(Request, Name) of {ok, Param} -> param_to_string(Param, Name); {error, _} -> {error, <<<<"Missing "/utf8, Name/binary>>/binary, " parameter"/utf8>>} end.