Skip to content
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
5f3babe
feat: headers data structure
algebraic-dev Jan 23, 2026
d3cddf9
fix: Headers.Basic comment
algebraic-dev Jan 23, 2026
8ba86c2
fix: case and usage of native_decide
algebraic-dev Jan 23, 2026
bd28a8f
fix: tests and type class
algebraic-dev Jan 23, 2026
65883f8
fix: levenshtein test is using the new Decidable instance
algebraic-dev Jan 24, 2026
44f0868
feat: connection values
algebraic-dev Jan 24, 2026
8c34350
fix: apply suggestions
algebraic-dev Jan 25, 2026
7141144
fix: remove native_decide
algebraic-dev Jan 25, 2026
990e21e
fix: namespace
algebraic-dev Jan 25, 2026
81c67c8
revert: levenshtein test
algebraic-dev Jan 25, 2026
6e26b90
fix: encoding
algebraic-dev Jan 25, 2026
dbe0140
fix: enforce validations
algebraic-dev Feb 9, 2026
e2fd8a5
Merge branch 'sofia/async-http-data' of https://github.com/leanprover…
algebraic-dev Feb 9, 2026
63984c8
fix: header value
algebraic-dev Feb 9, 2026
f4aad3a
Merge branch 'sofia/async-http-data' of https://github.com/leanprover…
algebraic-dev Feb 10, 2026
b567713
Merge branch 'sofia/async-http-data' of https://github.com/leanprover…
algebraic-dev Feb 10, 2026
1cec975
fix: imports
algebraic-dev Feb 10, 2026
c2f657a
Merge branch 'sofia/async-http-data' of https://github.com/leanprover…
algebraic-dev Feb 10, 2026
3568464
Merge branch 'sofia/async-http-data' of https://github.com/leanprover…
algebraic-dev Feb 11, 2026
eeff484
Merge branch 'sofia/async-http-data' of https://github.com/leanprover…
algebraic-dev Feb 12, 2026
9ce1821
feat: add trailers some type of headers
algebraic-dev Feb 12, 2026
9430840
Merge branch 'sofia/async-http-data' of https://github.com/leanprover…
algebraic-dev Feb 13, 2026
153513d
fix: typos
algebraic-dev Feb 13, 2026
1f2671d
merge: branch 'sofia/async-http-data'
algebraic-dev Feb 13, 2026
e0d5596
fix: typo
algebraic-dev Feb 13, 2026
87c5488
merge: 'sofia/async-http-headers'
algebraic-dev Feb 13, 2026
6b0f05d
Merge branch 'sofia/async-http-data' into sofia/async-http-headers
algebraic-dev Feb 17, 2026
a521ba3
fix: typos and connection token
algebraic-dev Feb 17, 2026
157e3b0
Merge branch 'sofia/async-http-data' into sofia/async-http-headers
algebraic-dev Feb 18, 2026
3fdaf2d
Merge branch 'sofia/async-http-data' into sofia/async-http-headers
algebraic-dev Feb 18, 2026
ad47193
fix: trailing peridos
algebraic-dev Feb 18, 2026
dd09289
fix: duplication
algebraic-dev Feb 18, 2026
78316b9
fix: isToken
algebraic-dev Feb 18, 2026
c8c92fc
Merge branch 'sofia/async-http-data' into sofia/async-http-headers
algebraic-dev Feb 19, 2026
0d3f6e5
Merge branch 'sofia/async-http-data' into sofia/async-http-headers
algebraic-dev Feb 20, 2026
5a53207
feat: remove direct access
algebraic-dev Feb 20, 2026
a9ac33d
Merge branch 'sofia/async-http-data' into sofia/async-http-headers
algebraic-dev Feb 20, 2026
f97d86c
feat: trim headers
algebraic-dev Feb 20, 2026
2e1bdd9
algebraic-dev Feb 24, 2026
5914fe3
Merge branch 'sofia/async-http-data' into sofia/async-http-headers
algebraic-dev Feb 24, 2026
36f87f9
fix: char
algebraic-dev Feb 24, 2026
ff99979
Merge branch 'sofia/async-http-data' into sofia/async-http-headers
algebraic-dev Feb 24, 2026
89c992a
Merge branch 'sofia/async-http-data' into sofia/async-http-headers
algebraic-dev Feb 24, 2026
de7c029
feat: field content
algebraic-dev Feb 24, 2026
e65e20e
feat: field content
algebraic-dev Feb 24, 2026
8492e58
fix: comments
algebraic-dev Feb 24, 2026
e9eed5c
Merge branch 'sofia/async-http-data' into sofia/async-http-headers
algebraic-dev Feb 25, 2026
49d00ae
fix: comments and small formatting errors
algebraic-dev Feb 26, 2026
a889085
Merge branch 'sofia/async-http-data' into sofia/async-http-headers
algebraic-dev Feb 26, 2026
228f0d2
fix: remove unused headers
algebraic-dev Feb 26, 2026
ba36c1d
fix: comments
algebraic-dev Feb 26, 2026
91c5b71
Merge branch 'sofia/async-http-data' into sofia/async-http-headers
algebraic-dev Feb 26, 2026
caf19b8
feat: order
algebraic-dev Feb 26, 2026
2049542
feat: order
algebraic-dev Feb 26, 2026
a0dc1db
fix: test
algebraic-dev Feb 26, 2026
b7be572
feat: forIn
algebraic-dev Feb 26, 2026
292f297
feat: small comments
algebraic-dev Feb 26, 2026
563189f
Merge branch 'sofia/async-http-data' into sofia/async-http-headers
algebraic-dev Feb 27, 2026
ff44193
Merge branch 'sofia/async-http-data' into sofia/async-http-headers
algebraic-dev Feb 27, 2026
57a4d9a
Merge branch 'sofia/async-http-data' into sofia/async-http-headers
algebraic-dev Feb 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Init/Data/String/Defs.lean
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ Examples:
* `"empty".isEmpty = false`
* `" ".isEmpty = false`
-/
@[inline] def isEmpty (s : String) : Bool :=
@[inline, expose] def isEmpty (s : String) : Bool :=
s.utf8ByteSize == 0

@[export lean_string_isempty]
Expand Down
10 changes: 10 additions & 0 deletions src/Init/Data/String/Lemmas/Modify.lean
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,14 @@ theorem length_map {f : Char → Char} {s : String} : (s.map f).length = s.lengt
theorem map_eq_empty {f : Char → Char} {s : String} : s.map f = "" ↔ s = "" := by
simp [← toList_eq_nil_iff]

@[simp]
theorem map_map {f g : Char → Char} {s : String} : String.map g (String.map f s) = String.map (g ∘ f) s := by
apply String.ext
simp [List.map_map]

@[simp]
theorem map_id {s : String} : String.map id s = s := by
apply String.ext
simp [List.map_id]

end String
2 changes: 1 addition & 1 deletion src/Init/Data/String/Modify.lean
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ Examples:
* `"Orange".toLower = "orange"`
* `"ABc123".toLower = "abc123"`
-/
@[inline] def toLower (s : String) : String :=
@[inline, expose] def toLower (s : String) : String :=
s.map Char.toLower

/--
Expand Down
1 change: 1 addition & 0 deletions src/Std/Internal/Http/Data.lean
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public import Std.Internal.Http.Data.Request
public import Std.Internal.Http.Data.Response
public import Std.Internal.Http.Data.Status
public import Std.Internal.Http.Data.Chunk
public import Std.Internal.Http.Data.Headers

/-!
# HTTP Data Types
Expand Down
250 changes: 250 additions & 0 deletions src/Std/Internal/Http/Data/Headers.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sofia Rodrigues
-/
module

prelude
public import Init.Data.String
public import Std.Data.HashMap
public import Std.Internal.Http.Internal
public import Std.Internal.Http.Data.Headers.Basic
public import Std.Internal.Http.Data.Headers.Name
public import Std.Internal.Http.Data.Headers.Value

public section

/-!
# Headers

This module defines the `Headers` type, which represents an collection of HTTP header name-value pairs.
-/

namespace Std.Http

set_option linter.all true

open Internal

/--
A structure for managing HTTP headers as key-value pairs.
-/
structure Headers where

/--
The underlying multimap that stores headers.
-/
map : MultiMap Header.Name Header.Value
deriving Inhabited, Repr

instance : Membership Header.Name Headers where
mem s h := h ∈ s.map

instance (name : Header.Name) (h : Headers) : Decidable (name ∈ h) :=
inferInstanceAs (Decidable (name ∈ h.map))

namespace Headers

/--
Retrieves the first `Header.Value` for the given key.
-/
@[inline]
def get (headers : Headers) (name : Header.Name) (h : name ∈ headers) : Header.Value :=
headers.map.get name h

/--
Retrieves all `Header.Value` entries for the given key.
-/
@[inline]
def getAll (headers : Headers) (name : Header.Name) (h : name ∈ headers) : Array Header.Value :=
headers.map.getAll name h

/--
Retrieves all `Header.Value` entries for the given key.
Returns `none` if the header is absent.
-/
@[inline]
def getAll? (headers : Headers) (name : Header.Name) : Option (Array Header.Value) :=
headers.map.getAll? name

/--
Retrieves the first `Header.Value` for the given key.
Returns `none` if the header is absent.
-/
@[inline]
def get? (headers : Headers) (name : Header.Name) : Option Header.Value :=
headers.map.get? name

/--
Checks if the entry is present in the `Headers`.
-/
@[inline]
def hasEntry (headers : Headers) (name : Header.Name) (value : Header.Value) : Bool :=
headers.map.data.get? name
|>.bind (fun x => x.val.find? (· == value))
|>.isSome

/--
Retrieves the last header value for the given key.
Returns `none` if the header is absent.
-/
@[inline]
def getLast? (headers : Headers) (name : Header.Name) : Option Header.Value :=
headers.map.getLast? name

/--
Like `get?`, but returns a default value if absent.
-/
@[inline]
def getD (headers : Headers) (name : Header.Name) (d : Header.Value) : Header.Value :=
headers.map.getD name d

/--
Like `get?`, but panics if absent.
-/
@[inline]
def get! (headers : Headers) (name : Header.Name) : Header.Value :=
headers.map.get! name

/--
Inserts a new key-value pair into the headers.
-/
@[inline]
def insert (headers : Headers) (key : Header.Name) (value : Header.Value) : Headers :=
{ map := headers.map.insert key value }

/--
Adds a header from string name and value, panicking if either is invalid.
-/
@[inline]
def insert! (headers : Headers) (name : String) (value : String) : Headers :=
headers.insert (Header.Name.ofString! name) (Header.Value.ofString! value)

/--
Inserts a new key with an array of values.
-/
@[inline]
def insertMany (headers : Headers) (key : Header.Name) (value : Array Header.Value) (p : value.size > 0) : Headers :=
{ map := headers.map.insertMany key value p }

/--
Creates empty headers.
-/
def empty : Headers :=
{ map := ∅ }

/--
Creates headers from a list of key-value pairs.
-/
def ofList (pairs : List (Header.Name × Header.Value)) : Headers :=
{ map := MultiMap.ofList pairs }

/--
Checks if a header with the given name exists.
-/
@[inline]
def contains (headers : Headers) (name : Header.Name) : Bool :=
headers.map.contains name

/--
Removes a header with the given name.
-/
@[inline]
def erase (headers : Headers) (name : Header.Name) : Headers :=
{ map := headers.map.erase name }

/--
Gets the number of headers.
-/
@[inline]
def size (headers : Headers) : Nat :=
headers.map.size

/--
Checks if the headers are empty.
-/
@[inline]
def isEmpty (headers : Headers) : Bool :=
headers.map.isEmpty

/--
Merges two headers, with the second taking precedence for duplicate keys.
-/
def merge (headers1 headers2 : Headers) : Headers :=
{ map := headers1.map ∪ headers2.map }

/--
Converts the headers to a list of key-value pairs (flattened). Each header with multiple values produces
multiple pairs.
-/
def toList (headers : Headers) : List (Header.Name × Header.Value) :=
headers.map.toList

/--
Converts the headers to an array of key-value pairs (flattened). Each header with multiple values
produces multiple pairs.
-/
def toArray (headers : Headers) : Array (Header.Name × Header.Value) :=
headers.map.toArray

/--
Folds over all key-value pairs in the headers.
-/
def fold (headers : Headers) (init : α) (f : α → Header.Name → Header.Value → α) : α :=
headers.map.toArray.foldl (fun acc (k, v) => f acc k v) init

/--
Maps a function over all header values, producing new headers.
-/
def mapValues (headers : Headers) (f : Header.Name → Header.Value → Header.Value) : Headers :=
let pairs := headers.map.toArray.map (fun (k, v) => (k, f k v))
{ map := pairs.foldl (fun acc (k, v) => acc.insert k v) MultiMap.empty }

/--
Filters and maps over header key-value pairs. Returns only the pairs for which the function returns `some`.
-/
def filterMap (headers : Headers) (f : Header.Name → Header.Value → Option Header.Value) : Headers :=
let pairs := headers.map.toArray.filterMap (fun (k, v) =>
match f k v with
| some v' => some (k, v')
| none => none)
{ map := pairs.foldl (fun acc (k, v) => acc.insert k v) MultiMap.empty }

/--
Filters header key-value pairs, keeping only those that satisfy the predicate.
-/
def filter (headers : Headers) (f : Header.Name → Header.Value → Bool) : Headers :=
headers.filterMap (fun k v => if f k v then some v else none)

/--
Updates the first value of a header if it exists, or inserts if it doesn't. Replaces all existing values
for that header with the new value.
-/
def update (headers : Headers) (name : Header.Name) (f : Option Header.Value → Header.Value) : Headers :=
let newValue := f (headers.get? name)
{ map := headers.map.erase name |>.insert name newValue }

instance : ToString Headers where
toString headers :=
let pairs := headers.map.toArray.map (fun (k, v) => s!"{k}: {v.value}")
String.intercalate "\r\n" pairs.toList

instance : Encode .v11 Headers where
encode buffer headers :=
headers.fold buffer (fun buf name value =>
buf.writeString s!"{name}: {value}\r\n")

instance : EmptyCollection Headers :=
⟨Headers.empty⟩

instance : Singleton (Header.Name × Header.Value) Headers :=
⟨fun ⟨a, b⟩ => (∅ : Headers).insert a b⟩

instance : Insert (Header.Name × Header.Value) Headers :=
⟨fun ⟨a, b⟩ s => s.insert a b⟩

instance : Union Headers :=
⟨merge⟩

end Std.Http.Headers
Loading
Loading