let parse_tag_value s =
  let parse_tag s =
    if (String.length s = 2)
      && (match s.[0] with 'A' .. 'Z' | 'a' .. 'z' -> true | _ -> false)
      && (match s.[1] with
          | 'A' .. 'Z' | 'a' .. 'z' | '0' .. '9' -> true
          | _ -> false
      )
    then
      Ok s
    else
      error "invalid tag" s sexp_of_string
  in
  let parse_value tag s =
    if (s <> "")
      && (String.for_all s ~f:(function ' ' .. '~' -> true | _ -> false))
    then
      Ok s
    else
      error "tag has invalid value" (tag,s)
      <:sexp_of< string * string >>
  in
  match String.lsplit2 s ~on:':' with
  | None ->
    error "tag-value not colon separated" s sexp_of_string
  | Some (tag,value) ->
    parse_tag tag >>= fun tag ->
    parse_value tag value >>= fun value ->
    Ok (tag, value)