irc message parser/printer
This commit is contained in:
parent
e92507b33c
commit
56dc7f2602
|
@ -0,0 +1,4 @@
|
||||||
|
(library
|
||||||
|
(name ircd_msg)
|
||||||
|
(inline_tests)
|
||||||
|
(preprocess (pps ppx_expect ppx_deriving.show)))
|
|
@ -0,0 +1,127 @@
|
||||||
|
type t = {
|
||||||
|
prefix : string option;
|
||||||
|
command : string;
|
||||||
|
params : string list;
|
||||||
|
trailing : bool;
|
||||||
|
} [@@deriving show { with_path = false }]
|
||||||
|
|
||||||
|
let rec is_params_trailing = function
|
||||||
|
| [] -> false
|
||||||
|
| [tr] -> String.contains tr ' '
|
||||||
|
| _ :: tl -> is_params_trailing tl
|
||||||
|
|
||||||
|
let make ?prefix ?(always_trailing = false) command params =
|
||||||
|
let trailing = always_trailing || is_params_trailing params in
|
||||||
|
{ prefix; command; params; trailing }
|
||||||
|
|
||||||
|
let write buf t =
|
||||||
|
Option.iter
|
||||||
|
(fun pre ->
|
||||||
|
Buffer.add_char buf ':';
|
||||||
|
Buffer.add_string buf pre;
|
||||||
|
Buffer.add_char buf ' ')
|
||||||
|
t.prefix;
|
||||||
|
Buffer.add_string buf t.command;
|
||||||
|
let rec add_params = function
|
||||||
|
| [] -> ()
|
||||||
|
| [tr] when t.trailing ->
|
||||||
|
Buffer.add_string buf " :";
|
||||||
|
Buffer.add_string buf tr
|
||||||
|
| hd::tl ->
|
||||||
|
Buffer.add_char buf ' ';
|
||||||
|
Buffer.add_string buf hd;
|
||||||
|
add_params tl
|
||||||
|
in
|
||||||
|
add_params t.params;
|
||||||
|
Buffer.add_string buf "\r\n"
|
||||||
|
|
||||||
|
exception Empty_message
|
||||||
|
|
||||||
|
let parse str i endp =
|
||||||
|
let startswith f i = if i >= endp then false else f str.[i] in
|
||||||
|
let rec span f i = if startswith f i then span f (i + 1) else i in
|
||||||
|
let cl = function ':' -> true | _ -> false in
|
||||||
|
let sp = function ' ' -> true | _ -> false in
|
||||||
|
let nosp = function ' ' -> false | _ -> true in
|
||||||
|
|
||||||
|
let i, prefix =
|
||||||
|
if startswith cl i then
|
||||||
|
let i_s = i + 1 in
|
||||||
|
let i_e = span nosp i_s in
|
||||||
|
let prefix = String.sub str i_s (i_e - i_s) in
|
||||||
|
i_e, Some prefix
|
||||||
|
else
|
||||||
|
i, None
|
||||||
|
in
|
||||||
|
|
||||||
|
let i, command =
|
||||||
|
let i_s = span sp i in
|
||||||
|
if i_s >= endp then raise_notrace Empty_message;
|
||||||
|
let i_e = span nosp i_s in
|
||||||
|
let command = String.sub str i_s (i_e - i_s) |> String.uppercase_ascii in
|
||||||
|
i_e, command
|
||||||
|
in
|
||||||
|
|
||||||
|
let rec params acc i =
|
||||||
|
let i_s = span sp i in
|
||||||
|
if i_s >= endp then
|
||||||
|
List.rev acc, false
|
||||||
|
else if startswith cl i_s then
|
||||||
|
let i_s = i_s + 1 in
|
||||||
|
let i_e = endp in
|
||||||
|
let trailing = String.sub str i_s (i_e - i_s) in
|
||||||
|
List.rev (trailing :: acc), true
|
||||||
|
else
|
||||||
|
let i_e = span nosp i_s in
|
||||||
|
let param = String.sub str i_s (i_e - i_s) in
|
||||||
|
params (param :: acc) i_e
|
||||||
|
in
|
||||||
|
|
||||||
|
let params, always_trailing = params [] i in
|
||||||
|
make command params ?prefix ~always_trailing
|
||||||
|
|
||||||
|
let parse ?(ofs = 0) ?(len = max_int) str =
|
||||||
|
let endp = ofs + min len (String.length str) in
|
||||||
|
try Some (parse str ofs endp)
|
||||||
|
with Empty_message -> None
|
||||||
|
|
||||||
|
;;
|
||||||
|
|
||||||
|
let%expect_test _ =
|
||||||
|
let print_msg_nl m =
|
||||||
|
let buf = Buffer.create 64 in
|
||||||
|
write buf m;
|
||||||
|
Printf.printf "%S\n" (Buffer.contents buf)
|
||||||
|
in
|
||||||
|
let print_parsed_msg_nl = function
|
||||||
|
| Some m -> Format.kasprintf print_string "%a\n" pp m
|
||||||
|
| None -> print_endline "empty"
|
||||||
|
in
|
||||||
|
|
||||||
|
make "NICK" ["tali"] |> print_msg_nl;
|
||||||
|
[%expect {| "NICK tali\r\n" |}];
|
||||||
|
|
||||||
|
make "USER" ["milo"; "0"; "*"; "milo"] ~always_trailing:true |> print_msg_nl;
|
||||||
|
[%expect {| "USER milo 0 * :milo\r\n" |}];
|
||||||
|
|
||||||
|
make "001" ["tali"; "Welcome to the IRC Network"] ~prefix:"localhost" |> print_msg_nl;
|
||||||
|
[%expect {| ":localhost 001 tali :Welcome to the IRC Network\r\n" |}];
|
||||||
|
|
||||||
|
parse ":source usEr tali 0 * iitalics" |> print_parsed_msg_nl;
|
||||||
|
[%expect {|
|
||||||
|
{ prefix = (Some "source"); command = "USER";
|
||||||
|
params = ["tali"; "0"; "*"; "iitalics"]; trailing = false }
|
||||||
|
|}];
|
||||||
|
|
||||||
|
parse "PRIVMSG #lol :Hello world" |> print_parsed_msg_nl;
|
||||||
|
[%expect {|
|
||||||
|
{ prefix = None; command = "PRIVMSG"; params = ["#lol"; "Hello world"];
|
||||||
|
trailing = true }
|
||||||
|
|}];
|
||||||
|
|
||||||
|
parse " " |> print_parsed_msg_nl;
|
||||||
|
[%expect {| empty |}];
|
||||||
|
|
||||||
|
parse ":source " |> print_parsed_msg_nl;
|
||||||
|
[%expect {| empty |}];
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
type t = {
|
||||||
|
prefix : string option;
|
||||||
|
command : string;
|
||||||
|
params : string list;
|
||||||
|
trailing : bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
val pp : Format.formatter -> t -> unit
|
||||||
|
|
||||||
|
val make : ?prefix:string -> ?always_trailing:bool ->
|
||||||
|
string -> string list -> t
|
||||||
|
|
||||||
|
val write : Buffer.t -> t -> unit
|
||||||
|
val parse : ?ofs:int -> ?len:int -> string -> t option
|
Loading…
Reference in New Issue