summaryrefslogblamecommitdiff
path: root/gemini/mimeType.ml
blob: 168241625b1c6d1f334f9e05bef76ac74355e29b (plain) (tree)

























                                                  







































                                                                        







                                   
module type MIME_TYPE =
sig
  type t

  exception ParseError of string

  val from_string : string -> t

  val to_type : t -> string

  val to_subtype : t -> string

  val to_parameter : t -> (string * string) option
end

module MimeType : MIME_TYPE =
struct

  type t =
    { mime_type : string;
      mime_subtype : string;
      mime_param : (string * string) option;
    }

  exception ParseError of string

  let create_mimetype mime_type mime_subtype mime_param =
    { mime_type; mime_subtype; mime_param; }

  let from_string (s : string) =
    (* RFC 2045 § 5.1 *)
    (* RFC 6838 § 4.2 *)
    (* FIXME: parameter does not handle quoted strings and
       does not try to ensure that everything is well-formed *)
    let open Angstrom in
    let lift_or p q b = p b || q b in
    let is_letter = function
      | 'a' .. 'z' | 'A' .. 'Z' -> true
      | _ -> false in
    let is_digit = function
      | '0' .. '9' -> true
      | _ -> false in
    let is_symbol = function
      | '!' | '#' | '$' | '&' | '-' | '^' | '_' | '.' | '+' -> true
      | _ -> false in
    let is_space = function
      | ' ' | '\t' -> true
      | _ -> false in
    let take_all = take_while (fun _ -> true) in
    let maybe p =
      option None (lift (fun s -> Some s) p) in
    let restricted_name =
      lift2 (fun c -> fun s -> (Char.escaped c) ^ s)
        (satisfy (lift_or is_letter is_digit))
        (take_while (lift_or is_letter (lift_or is_digit is_symbol))) in
    let parameter = lift2 (fun p -> fun v -> (p, v))
        (take_till (fun c -> c = '='))
        (char '=' *> take_all) <?> "parameter" in
    let parse = lift3 create_mimetype
        (restricted_name <?> "type")
        ((char '/' *> restricted_name) <?> "subtype")
        (maybe (char ';' *> skip_while is_space *> parameter))
        <?> "mimetype"
    in match Angstrom.parse_string ~consume:All parse s with
    | Ok x -> x
    | Error msg -> raise (ParseError msg)

  let to_type m = m.mime_type

  let to_subtype m = m.mime_subtype

  let to_parameter m = m.mime_param

end