OCaml percent-encode and split URL query strings

OCaml
let percent_encode s =
  let safe = function
    | 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' | '-' | '_' | '.' | '~' -> true
    | _ -> false
  in
  String.to_seq s
  |> Seq.map (fun c -> if safe c then String.make 1 c else Printf.sprintf "%%%02X" (Char.code c))
  |> List.of_seq |> String.concat ""

let build_query params =
  params
  |> List.map (fun (k, v) -> percent_encode k ^ "=" ^ percent_encode v)
  |> String.concat "&"

let parse_query query =
  query |> String.split_on_char '&'
  |> List.filter_map (fun pair ->
      match String.index_opt pair '=' with
      | None -> None
      | Some i -> Some (String.sub pair 0 i, String.sub pair (i + 1) (String.length pair - i - 1)))