private def toString (uri : URI) : String :=
++ if let some user := uri.userInfo then s!"{user}@"
++ if let some port := uri.port then s!":{port}"
++ if let some query := uri.query then s!"?{query}"
++ if let some fragment := uri.fragment then s!"#{fragment}"
instance : ToString URI := ⟨toString⟩
def schemeParser : Parsec Scheme :=
Scheme.mk <$> manyChars (asciiLetter <|> oneOf ['+', '-', '.'])
def hostName : Parsec Hostname := do
let name := many1Chars (asciiLetter <|> digit <|> pchar '-')
let start := name ++ pstring "."
many1Strings start ++ name
def parseDigit! (c : Char) : Nat :=
| _ => panic! "Not a digit"
def parseUInt16 : Parsec UInt16 := do
for (i, c) in as.toList.reverse.enum do
def maybePort : Parsec (Option UInt16) := do
def psegment : Parsec String :=
many1Chars <| oneOf ['-', '%', '_', '+', '$', '.', ':', '*', '@' ] <|> asciiLetter <|> digit
partial def pathParser : Parsec Path := do
let rec comp : Parsec Path := do
if ← test <| pstring "/" then
def userInfoParser : Parsec UserInfo := do
let username ← many1Chars <| asciiLetter <|> digit
let password ← option do skipChar ':'; many1Chars <| asciiLetter <|> digit
return { username, password : UserInfo }
partial def queryParser : Parsec Query := do
if ← test $ skipChar '&' then
pure <| (k, v) :: (← entries)
partial def fragmentParser : Parsec Fragment := do
if ← test $ skipChar '&' then
pure <| (k, v) :: (← entries)
def url : Parsec URI := do
let scheme ← schemeParser
let userInfo ← option userInfoParser
let query ← option queryParser
let fragment ← option fragmentParser
return { scheme, host, port := optPort, path, query, fragment, userInfo : URI }
def parse (s : String) : Except String URI := Parser.url.parse s