-module(dream@http@header). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/dream/http/header.gleam"). -export([header_name/1, header_value/1, get_header/2, set_header/3, add_header/3, remove_header/2]). -export_type([header/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 header types and utilities\n" "\n" " Types and functions for working with HTTP headers. Headers can be used\n" " with both requests and responses.\n" "\n" " ## Common Operations\n" "\n" " ```gleam\n" " import dream/http/header.{Header}\n" "\n" " // Create headers\n" " let headers = [\n" " Header(\"Content-Type\", \"application/json\"),\n" " Header(\"Cache-Control\", \"max-age=3600\"),\n" " ]\n" "\n" " // Get a header value\n" " header.get_header(headers, \"content-type\") // Some(\"application/json\")\n" "\n" " // Set or replace a header\n" " header.set_header(headers, \"X-Custom\", \"value\")\n" "\n" " // Add a header without replacing\n" " header.add_header(headers, \"Set-Cookie\", \"session=abc\")\n" " ```\n" ). -type header() :: {header, binary(), binary()}. -file("src/dream/http/header.gleam", 62). ?DOC( " Get the name of a header\n" "\n" " Extracts the name field from a Header.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " import dream/http/header.{Header}\n" "\n" " let h = Header(\"Content-Type\", \"application/json\")\n" " header.header_name(h) // \"Content-Type\"\n" " ```\n" ). -spec header_name(header()) -> binary(). header_name(Header) -> case Header of {header, Name, _} -> Name end. -file("src/dream/http/header.gleam", 80). ?DOC( " Get the value of a header\n" "\n" " Extracts the value field from a Header.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " import dream/http/header.{Header}\n" "\n" " let h = Header(\"Content-Type\", \"application/json\")\n" " header.header_value(h) // \"application/json\"\n" " ```\n" ). -spec header_value(header()) -> binary(). header_value(Header) -> case Header of {header, _, Value} -> Value end. -file("src/dream/http/header.gleam", 111). -spec find_header(list(header()), binary()) -> gleam@option:option(binary()). find_header(Headers, Normalized_name) -> case Headers of [] -> none; [{header, Header_name, Value} | Rest] -> Matches = string:lowercase(Header_name) =:= Normalized_name, case Matches of true -> {some, Value}; false -> find_header(Rest, Normalized_name) end end. -file("src/dream/http/header.gleam", 106). ?DOC( " Get the value of a header by name (case-insensitive)\n" "\n" " Searches a list of headers for one matching the given name.\n" " Header name comparison is case-insensitive (\"content-type\" matches \"Content-Type\").\n" " Returns the first matching header's value.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " import dream/http/header.{Header}\n" "\n" " let headers = [\n" " Header(\"Content-Type\", \"application/json\"),\n" " Header(\"Cache-Control\", \"max-age=3600\"),\n" " ]\n" "\n" " header.get_header(headers, \"content-type\") // Some(\"application/json\")\n" " header.get_header(headers, \"CACHE-CONTROL\") // Some(\"max-age=3600\")\n" " header.get_header(headers, \"X-Missing\") // None\n" " ```\n" ). -spec get_header(list(header()), binary()) -> gleam@option:option(binary()). get_header(Headers, Name) -> Normalized_name = string:lowercase(Name), find_header(Headers, Normalized_name). -file("src/dream/http/header.gleam", 171). -spec filter_headers_recursive(list(header()), binary(), list(header())) -> list(header()). filter_headers_recursive(Headers, Normalized_name, Acc) -> case Headers of [] -> lists:reverse(Acc); [Header | Rest] -> Header_normalized = string:lowercase(header_name(Header)), Should_keep = Header_normalized /= Normalized_name, case Should_keep of true -> filter_headers_recursive( Rest, Normalized_name, [Header | Acc] ); false -> filter_headers_recursive(Rest, Normalized_name, Acc) end end. -file("src/dream/http/header.gleam", 164). -spec filter_matching_headers(list(header()), binary()) -> list(header()). filter_matching_headers(Headers, Normalized_name) -> filter_headers_recursive(Headers, Normalized_name, []). -file("src/dream/http/header.gleam", 154). ?DOC( " Set or replace a header\n" "\n" " If a header with this name exists (case-insensitive), replaces its value.\n" " If not, adds a new header. Only one header with the given name will exist\n" " in the result.\n" "\n" " Use this when you want to ensure exactly one value for a header (most cases).\n" " For headers that can appear multiple times (like Set-Cookie), use `add_header` instead.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " import dream/http/header.{Header}\n" "\n" " let headers = [Header(\"Content-Type\", \"text/plain\")]\n" "\n" " // Replace existing header\n" " let updated = header.set_header(headers, \"content-type\", \"application/json\")\n" " // Result: [Header(\"content-type\", \"application/json\")]\n" "\n" " // Add new header\n" " let with_cache = header.set_header(updated, \"Cache-Control\", \"max-age=3600\")\n" " // Result: [\n" " // Header(\"Cache-Control\", \"max-age=3600\"),\n" " // Header(\"content-type\", \"application/json\")\n" " // ]\n" " ```\n" ). -spec set_header(list(header()), binary(), binary()) -> list(header()). set_header(Headers, Name, Value) -> Normalized_name = string:lowercase(Name), Filtered = filter_matching_headers(Headers, Normalized_name), [{header, Name, Value} | Filtered]. -file("src/dream/http/header.gleam", 214). ?DOC( " Add a header without removing existing ones with the same name\n" "\n" " Adds a new header even if one with the same name already exists.\n" " This allows multiple headers with the same name, which is valid for\n" " some headers like Set-Cookie, Warning, or Accept.\n" "\n" " Use this when you need multiple values for the same header name.\n" " For most headers, use `set_header` instead to ensure only one value.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " import dream/http/header.{Header}\n" "\n" " let headers = []\n" "\n" " // Add multiple Set-Cookie headers\n" " headers\n" " |> header.add_header(\"Set-Cookie\", \"session=abc; HttpOnly\")\n" " |> header.add_header(\"Set-Cookie\", \"preferences=dark; Path=/\")\n" " // Result: [\n" " // Header(\"Set-Cookie\", \"preferences=dark; Path=/\"),\n" " // Header(\"Set-Cookie\", \"session=abc; HttpOnly\")\n" " // ]\n" " ```\n" ). -spec add_header(list(header()), binary(), binary()) -> list(header()). add_header(Headers, Name, Value) -> [{header, Name, Value} | Headers]. -file("src/dream/http/header.gleam", 244). ?DOC( " Remove a header by name (case-insensitive)\n" "\n" " Removes all headers with the given name (case-insensitive).\n" " Returns a new list with matching headers filtered out.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " import dream/http/header.{Header}\n" "\n" " let headers = [\n" " Header(\"Content-Type\", \"application/json\"),\n" " Header(\"Cache-Control\", \"max-age=3600\"),\n" " Header(\"X-Debug\", \"true\"),\n" " ]\n" "\n" " header.remove_header(headers, \"x-debug\")\n" " // Result: [\n" " // Header(\"Content-Type\", \"application/json\"),\n" " // Header(\"Cache-Control\", \"max-age=3600\")\n" " // ]\n" " ```\n" ). -spec remove_header(list(header()), binary()) -> list(header()). remove_header(Headers, Name) -> Normalized_name = string:lowercase(Name), filter_matching_headers(Headers, Normalized_name).