type t = { localpart : string option; domainpart : string; resourcepart : string option; } exception InvalidUTF8 type uchars = Uchar.t list let uchars_of_string s : uchars = let open Uchar in let len = String.length s in let rec loop acc i = if i >= len then List.rev acc else let c = String.get_utf_8_uchar s i in if not (utf_decode_is_valid c) then raise InvalidUTF8 else let k = utf_decode_length c in let u = utf_decode_uchar c in loop (u :: acc) (i + k) in loop [] (String.length s) let of_string (jid : string) : t = let open Uchar in let len = String.length jid in let rec to_localpart i = if i <= 0 then None else let c = String.get_utf_8_uchar jid i in if not (utf_decode_is_valid c) then raise InvalidUTF8 else let k = utf_decode_length c in match utf_decode_uchar c |> to_char with | '@' -> Some (i + 1) | _ -> to_localpart (i - k) and to_resourcepart i = if i >= len then None else let c = String.get_utf_8_uchar jid i in if not (utf_decode_is_valid c) then raise InvalidUTF8 else let k = utf_decode_length c in match utf_decode_uchar c |> to_char with | '/' -> Some i | _ -> to_resourcepart (i + k) in let r = to_resourcepart 0 in let rv = Option.value ~default:len r in let l = to_localpart (rv-1) in let lv = Option.value ~default:0 l in { localpart = Option.map (fun i -> String.sub jid 0 (i-1)) l; resourcepart = Option.map (fun i -> String.sub jid (i+1) (len-i-1)) r; domainpart = String.sub jid lv (rv-lv); } let to_string ({ localpart; domainpart; resourcepart } : t) = let local = Option.fold ~none:"" ~some:(fun l -> l ^ "@") localpart and resource = Option.fold ~none:"" ~some:(fun r -> "/" ^ r) resourcepart in local ^ domainpart ^ resource