let parse_item0
    ?(allow_sharp_comments=true)
    ?(allow_semicolon_comments=false)
    ?(allow_empty_lines=false)
    ?max_line_length
    ?alphabet
    line
    =
  let open Result.Monad_infix in
  let s = (line : Line.t :> string) in
  let n = String.length s in

  (match max_line_length with
  | None -> Ok ()
  | Some x ->
    if x <= n then Ok ()
    else error
      "max_line_length exceeded"
      (x,n) <:sexp_of< int * int >>
  ) >>= fun () ->

  if allow_empty_lines && (String.for_all s ~f:Char.is_whitespace) then
    Ok `Empty_line

  else if (not allow_empty_lines && n = 0) then
    Or_error.error_string "allow_empty_lines is false but got empty line"

  (* n > 0 if we got here *)

  else if s.[0] = '>' then
    Ok (`Description (String.slice s 1 n))

  else
    match allow_sharp_comments, allow_semicolon_comments, s.[0] with
    | true,true,(';' | '#')
    | true,false,'#'
    | false,true,';' ->
      Ok (`Comment (String.slice s 1 n))
    | false,false,(';' | '#'->
      Or_error.error_string "comments lines are not allowed"
    | _ ->
      (match alphabet with
      | None -> Ok (`Partial_sequence s)
      | Some alphabet ->
        if String.for_all s ~f:(String.mem alphabet) then
          Ok (`Partial_sequence s)
        else
          (* TODO: report which character is outside alphabet *)
          error "sequence contains string outside allowed alphabet"
            (s,alphabet) <:sexp_of< string * string >>
      )