;;
#directory "../../../lib/deriving_json/"

;;
#directory "../../../lib/deriving_json/.js_of_ocaml_deriving.objs/byte/"

let test name v = Format.printf "%s = %s\n%!" name v

let test' json name v =
  let b = Buffer.create 17 in
  let () = Deriving_Json.write json b v in
  let str = Buffer.contents b in
  let lex = Deriving_Json_lexer.init_lexer (Lexing.from_string str) in
  let v' = Deriving_Json.read json lex in
  Format.printf "%s = %s\n%!" name str;
  if v' <> v then Format.eprintf "Round-trip error\n%!"

open Deriving_Json

[%%expect
{|
val test : string -> string -> unit = <fun>
val test' : 'a Deriving_Json.t -> string -> 'a -> unit = <fun>
|}]

let () = test "char" (Json_char.to_string '4')

[%%expect {|
char = 52
|}]

let () = test "bool" (Json_bool.to_string true)

[%%expect {|
bool = 1
|}]

let () = test "bool" (Json_bool.to_string false)

[%%expect {|
bool = 0
|}]

let () = test "unit" (Json_unit.to_string ())

[%%expect {|
unit = 0
|}]

let () = test "int" (Json_int.to_string 42)

[%%expect {|
int = 42
|}]

let () = test "int32" (Json_int32.to_string 42l)

[%%expect {|
int32 = 42
|}]

let () = test "int64" (Json_int64.to_string 42L)

[%%expect {|
int64 = [255,42,0,0]
|}]

let () = test "nativeint" (Json_nativeint.to_string 42n)

[%%expect {|
Exception: Failure "Unimplemented".
|}]

(* let () = test "nativeint" (Json_nativeint.to_string 42n);; *)

let () = test "float" (Json_float.to_string 42.42)

[%%expect {|
float = 42.42
|}]

let () = test "string" (Json_string.to_string "42")

let () = test "string" (Json_string.to_string "\\\n\x0C")

[%%expect {|
string = "42"
string = "\\\n\f"
|}]

module Int_list = Json_list (Json_int)

module Int_ref = Json_ref (Json_int)

module Int_option = Json_option (Json_int)

module Int_array = Json_array (Json_int)

[%%expect
{|
module Int_list :
  sig
    type a = Deriving_Json.Json_int.a list
    val t : a Deriving_Json.t
    val write : Buffer.t -> a -> unit
    val read : Deriving_Json_lexer.lexbuf -> a
    val to_string : a -> string
    val from_string : string -> a
    val match_variant : [ `Cst of int | `NCst of int ] -> bool
    val read_variant :
      Deriving_Json_lexer.lexbuf -> [ `Cst of int | `NCst of int ] -> a
  end
module Int_ref :
  sig
    type a = Deriving_Json.Json_int.a ref
    val t : a Deriving_Json.t
    val write : Buffer.t -> a -> unit
    val read : Deriving_Json_lexer.lexbuf -> a
    val to_string : a -> string
    val from_string : string -> a
    val match_variant : [ `Cst of int | `NCst of int ] -> bool
    val read_variant :
      Deriving_Json_lexer.lexbuf -> [ `Cst of int | `NCst of int ] -> a
  end
module Int_option :
  sig
    type a = Deriving_Json.Json_int.a option
    val t : a Deriving_Json.t
    val write : Buffer.t -> a -> unit
    val read : Deriving_Json_lexer.lexbuf -> a
    val to_string : a -> string
    val from_string : string -> a
    val match_variant : [ `Cst of int | `NCst of int ] -> bool
    val read_variant :
      Deriving_Json_lexer.lexbuf -> [ `Cst of int | `NCst of int ] -> a
  end
module Int_array :
  sig
    type a = Deriving_Json.Json_int.a array
    val t : a Deriving_Json.t
    val write : Buffer.t -> a -> unit
    val read : Deriving_Json_lexer.lexbuf -> a
    val to_string : a -> string
    val from_string : string -> a
    val match_variant : [ `Cst of int | `NCst of int ] -> bool
    val read_variant :
      Deriving_Json_lexer.lexbuf -> [ `Cst of int | `NCst of int ] -> a
  end
|}]

let () = test "int_list" (Int_list.to_string [4; 2; 42])

[%%expect {|
int_list = [0,4,[0,2,[0,42,0]]]
|}]

let () = test "int_ref" (Int_ref.to_string (ref 42))

[%%expect {|
int_ref = [0,42]
|}]

let () = test "int_option" (Int_option.to_string (Some 42))

[%%expect {|
int_option = [0,42]
|}]

let () = test "int_option" (Int_option.to_string None)

[%%expect {|
int_option = 0
|}]

let () = test "int_array" (Int_array.to_string [|4; 2; 42; 24|])

[%%expect {|
int_array = [0,4,2,42,24]
|}]

type tuple1 = int * string [@@deriving json]

[%%expect
{|
type tuple1 = int * string
val tuple1_of_json : Deriving_Json_lexer.lexbuf -> tuple1 = <fun>
val tuple1_to_json : Buffer.t -> tuple1 -> unit = <fun>
val tuple1_json : tuple1 Deriving_Json.t = <abstr>
|}]

type variant1 =
  | A
  | B
  | C
  | D of variant1
[@@deriving json]

[%%expect
{|
type variant1 = A | B | C | D of variant1
val variant1_of_json : Deriving_Json_lexer.lexbuf -> variant1 = <fun>
val variant1_to_json : Buffer.t -> variant1 -> unit = <fun>
val variant1_json : variant1 Deriving_Json.t = <abstr>
|}]

type variant2 =
  | D of string
  | E of variant1
[@@deriving json]

[%%expect
{|
type variant2 = D of string | E of variant1
val variant2_of_json : Deriving_Json_lexer.lexbuf -> variant2 = <fun>
val variant2_to_json : Buffer.t -> variant2 -> unit = <fun>
val variant2_json : variant2 Deriving_Json.t = <abstr>
|}]

type record1 =
  { f : variant1
  ; g : variant2
  ; h : record1 option }
[@@deriving json]

[%%expect
{|
type record1 = { f : variant1; g : variant2; h : record1 option; }
val record1_of_json : Deriving_Json_lexer.lexbuf -> record1 = <fun>
val record1_to_json : Buffer.t -> record1 -> unit = <fun>
val record1_json : record1 Deriving_Json.t = <abstr>
|}]

type poly1 =
  [ `A
  | `B of string ]
[@@deriving json]

[%%expect
{|
type poly1 = [ `A | `B of string ]
val poly1_recognize : [ `Cst of int | `NCst of int ] -> bool = <fun>
val poly1_of_json_with_tag :
  Deriving_Json_lexer.lexbuf -> [ `Cst of int | `NCst of int ] -> poly1 =
  <fun>
val poly1_of_json : Deriving_Json_lexer.lexbuf -> poly1 = <fun>
val poly1_to_json : Buffer.t -> poly1 -> unit = <fun>
val poly1_json : poly1 Deriving_Json.t = <abstr>
|}]

type poly2 =
  [ poly1
  | `C of int ]
[@@deriving json]

[%%expect
{|
type poly2 = [ `A | `B of string | `C of int ]
val poly2_recognize : [ `Cst of int | `NCst of int ] -> bool = <fun>
val poly2_of_json_with_tag :
  Deriving_Json_lexer.lexbuf -> [ `Cst of int | `NCst of int ] -> poly2 =
  <fun>
val poly2_of_json : Deriving_Json_lexer.lexbuf -> poly2 = <fun>
val poly2_to_json : Buffer.t -> poly2 -> unit = <fun>
val poly2_json : poly2 Deriving_Json.t = <abstr>
|}]

type poly3 =
  [ poly1
  | `C of [`p1 of poly1 | `p2 of poly2 | `p3 of poly3] ]

and poly3_c = [`p1 of poly1 | `p2 of poly2 | `p3 of poly3]

and poly3_sep = [ poly1 | `C of poly3_c]
[@@deriving json]

[%%expect
{|
type poly3 =
    [ `A
    | `B of string
    | `C of [ `p1 of poly1 | `p2 of poly2 | `p3 of poly3 ] ]
and poly3_c = [ `p1 of poly1 | `p2 of poly2 | `p3 of poly3 ]
and poly3_sep = [ `A | `B of string | `C of poly3_c ]
val poly3_recognize : [ `Cst of int | `NCst of int ] -> bool = <fun>
val poly3_c_recognize : [ `Cst of int | `NCst of int ] -> bool = <fun>
val poly3_sep_recognize : [ `Cst of int | `NCst of int ] -> bool = <fun>
val poly3_of_json_with_tag :
  Deriving_Json_lexer.lexbuf -> [ `Cst of int | `NCst of int ] -> poly3 =
  <fun>
val poly3_c_of_json_with_tag :
  Deriving_Json_lexer.lexbuf -> [ `Cst of int | `NCst of int ] -> poly3_c =
  <fun>
val poly3_sep_of_json_with_tag :
  Deriving_Json_lexer.lexbuf -> [ `Cst of int | `NCst of int ] -> poly3_sep =
  <fun>
val poly3_of_json : Deriving_Json_lexer.lexbuf -> poly3 = <fun>
val poly3_c_of_json : Deriving_Json_lexer.lexbuf -> poly3_c = <fun>
val poly3_sep_of_json : Deriving_Json_lexer.lexbuf -> poly3_sep = <fun>
val poly3_to_json : Buffer.t -> poly3 -> unit = <fun>
val poly3_c_to_json : Buffer.t -> poly3_c -> unit = <fun>
val poly3_sep_to_json : Buffer.t -> poly3_sep -> unit = <fun>
val poly3_json : poly3 Deriving_Json.t = <abstr>
val poly3_c_json : poly3_c Deriving_Json.t = <abstr>
val poly3_sep_json : poly3_sep Deriving_Json.t = <abstr>
|}]

let () = test' tuple1_json "tuple1" (42, "42")


[%%expect {|
tuple1 = [0,42,"42"]
|}]

let () = test' variant1_json "variant1 A" A

[%%expect {|
variant1 A = 0
|}]

let () = test' variant1_json "variant1 B" B

[%%expect {|
variant1 B = 1
|}]

let () = test' variant1_json "variant1 C" C

[%%expect {|
variant1 C = 2
|}]

let () = test' variant2_json "variant2 D 'hello'" (D "hello")

[%%expect {|
variant2 D 'hello' = [0,"hello"]
|}]

let () = test' variant2_json "variant2 E A" (E A)

[%%expect {|
variant2 E A = [1,0]
|}]

let () = test' record1_json "record1" {f = A; g = D "d"; h = None}

[%%expect {|
record1 = [0,0,[0,"d"],0]
|}]

let () =
  test' record1_json "record1" {f = A; g = D "d"; h = Some {f = B; g = E (D B); h = None}}

[%%expect {|
record1 = [0,0,[0,"d"],[0,[0,1,[1,[0,1]],0]]]
|}]

let () = test' poly1_json "poly1 `A" `A

[%%expect {|
poly1 `A = 65
|}]

let () = test' poly1_json "poly1 `B str" (`B "str")

[%%expect {|
poly1 `B str = [0,66,"str"]
|}]

let () = test' poly2_json "poly2 `A" `A

[%%expect {|
poly2 `A = 65
|}]

let () = test' poly2_json "poly2 `B str" (`B "str")

[%%expect {|
poly2 `B str = [0,66,"str"]
|}]

let () = test' poly2_json "poly2 `C 42" (`C 42)

[%%expect {|
poly2 `C 42 = [0,67,42]
|}]

let () = test' poly3_json "poly3" (`C (`p3 (`C (`p1 `A))))
let () = test' poly3_sep_json "poly3" (`C (`p3 (`C (`p1 `A))))

[%%expect {|
poly3 = [0,67,[0,25027,[0,67,[0,25025,65]]]]
poly3 = [0,67,[0,25027,[0,67,[0,25025,65]]]]
|}]

let () = test' poly3_json "poly3" (`A)
let () = test' poly3_sep_json "poly3" (`A)
[%%expect {|
poly3 = 65
poly3 = 65
|}]

type inline_record =
  | I of
      { name : string
      ; age : int }
  | J of {empty : unit}
[@@deriving json]

let () = test' inline_record_json "inline_record 1" (I {name = "bob"; age = 0})

[%%expect
{|
type inline_record =
    I of { name : string; age : int; }
  | J of { empty : unit; }
val inline_record_of_json : Deriving_Json_lexer.lexbuf -> inline_record =
  <fun>
val inline_record_to_json : Buffer.t -> inline_record -> unit = <fun>
val inline_record_json : inline_record Deriving_Json.t = <abstr>
inline_record 1 = [0,"bob",0]
|}]

let () = test' inline_record_json "inline_record 2" (J {empty = ()})

[%%expect {|
inline_record 2 = [1,0]
|}]

type mutable_record =
  { mutable i : int }
[@@deriving json]

[%%expect
{|
type mutable_record = { mutable i : int; }
val mutable_record_of_json : Deriving_Json_lexer.lexbuf -> mutable_record =
  <fun>
val mutable_record_to_json : Buffer.t -> mutable_record -> unit = <fun>
val mutable_record_json : mutable_record Deriving_Json.t = <abstr>
|}]
