(* Auto-generated from "builtin_types.atd" by atdml. *)

type all_types = {
  a_unit: unit;
  a_bool: bool;
  a_int: int;
  a_float: float;
  a_string: string;
  a_list: int list;
  a_option: string option;
  a_nullable: bool option;
  a_abstract: Yojson.Safe.t;
  a_tuple: (int * string * bool);
  a_nested: (float list) option;
}

val create_all_types : a_unit:unit -> a_bool:bool -> a_int:int -> a_float:float -> a_string:string -> a_list:int list -> a_option:string option -> a_nullable:bool option -> a_abstract:Yojson.Safe.t -> a_tuple:(int * string * bool) -> a_nested:(float list) option -> unit -> all_types
val all_types_of_yojson : Yojson.Safe.t -> all_types
val all_types_of_jsonlike : Atd_jsonlike.AST.t -> all_types
val yojson_of_all_types : all_types -> Yojson.Safe.t
val all_types_of_json : string -> all_types
val json_of_all_types : all_types -> string

module All_types : sig
  type nonrec t = all_types
  val create : a_unit:unit -> a_bool:bool -> a_int:int -> a_float:float -> a_string:string -> a_list:int list -> a_option:string option -> a_nullable:bool option -> a_abstract:Yojson.Safe.t -> a_tuple:(int * string * bool) -> a_nested:(float list) option -> unit -> t
  val of_yojson : Yojson.Safe.t -> t
  val of_jsonlike : Atd_jsonlike.AST.t -> t
  val to_yojson : t -> Yojson.Safe.t
  val of_json : string -> t
  val to_json : t -> string
end

--- ml ---
(* Auto-generated from "builtin_types.atd" by atdml. *)
[@@@ocaml.warning "-27-32-33-35-39"]

(* Inlined runtime — no external dependency needed. *)
module Atdml_runtime = struct
  (* Returns true iff the list has strictly more than [n] elements,
     without traversing past element n+1. *)
  let rec list_length_gt n = function
    | _ :: rest -> if n = 0 then true else list_length_gt (n - 1) rest
    | [] -> false

  module Yojson = struct
    let bad_type expected_type x =
      Printf.ksprintf failwith "expected %s, got: %s"
        expected_type (Yojson.Safe.to_string x)

    let bad_sum type_name x =
      Printf.ksprintf failwith "invalid variant for type '%s': %s"
        type_name (Yojson.Safe.to_string x)

    let missing_field type_name field_name =
      Printf.ksprintf failwith "missing field '%s' in object of type '%s'"
        field_name type_name

    let bool_of_yojson = function
      | `Bool b -> b
      | x -> bad_type "bool" x

    let yojson_of_bool b = `Bool b

    let int_of_yojson = function
      | `Int n -> n
      | x -> bad_type "int" x

    let yojson_of_int n = `Int n

    let float_of_yojson = function
      | `Float f -> f
      | `Int n -> Float.of_int n
      | x -> bad_type "float" x

    let yojson_of_float f = `Float f

    let string_of_yojson = function
      | `String s -> s
      | x -> bad_type "string" x

    let yojson_of_string s = `String s

    let unit_of_yojson = function
      | `Null -> ()
      | x -> bad_type "null" x

    let yojson_of_unit () = `Null

    let list_of_yojson f = function
      | `List xs -> List.map f xs
      | x -> bad_type "array" x

    let yojson_of_list f xs = `List (List.map f xs)

    let option_of_yojson f = function
      | `String "None" -> None
      | `List [`String "Some"; x] -> Some (f x)
      | x -> bad_type "option" x

    let yojson_of_option f = function
      | None -> `String "None"
      | Some x -> `List [`String "Some"; f x]

    let nullable_of_yojson f = function
      | `Null -> None
      | x -> Some (f x)

    let yojson_of_nullable f = function
      | None -> `Null
      | Some x -> f x

    let assoc_of_yojson f = function
      | `Assoc pairs -> List.map (fun (k, v) -> (k, f v)) pairs
      | x -> bad_type "object" x

    let yojson_of_assoc f xs =
      `Assoc (List.map (fun (k, v) -> (k, f v)) xs)
  end

  module Jsonlike = struct
    let bad_type expected_type x =
      Printf.ksprintf failwith "%sexpected %s"
        (Atd_jsonlike.AST.loc_msg x) expected_type

    let bad_sum type_name x =
      Printf.ksprintf failwith "%sinvalid variant for type '%s'"
        (Atd_jsonlike.AST.loc_msg x) type_name

    let missing_field node type_name field_name =
      Printf.ksprintf failwith "%smissing field '%s' in object of type '%s'"
        (Atd_jsonlike.AST.loc_msg node) field_name type_name

    let bool_of_jsonlike = function
      | Atd_jsonlike.AST.Bool (_, b) -> b
      | x -> bad_type "bool" x

    let int_of_jsonlike = function
      | Atd_jsonlike.AST.Number (_, n) as node ->
          (match n.Atd_jsonlike.Number.int with
          | Some i -> i
          | None -> bad_type "integer" node)
      | x -> bad_type "int" x

    let float_of_jsonlike = function
      | Atd_jsonlike.AST.Number (_, n) as node ->
          (match n.Atd_jsonlike.Number.float with
          | Some f -> f
          | None -> bad_type "float" node)
      | x -> bad_type "float" x

    let string_of_jsonlike = function
      | Atd_jsonlike.AST.String (_, s) -> s
      | x -> bad_type "string" x

    let unit_of_jsonlike = function
      | Atd_jsonlike.AST.Null _ -> ()
      | x -> bad_type "null" x

    let list_of_jsonlike f = function
      | Atd_jsonlike.AST.Array (_, xs) -> List.map f xs
      | x -> bad_type "array" x

    let option_of_jsonlike f = function
      | Atd_jsonlike.AST.String (_, "None") -> None
      | Atd_jsonlike.AST.Array (_, [Atd_jsonlike.AST.String (_, "Some"); x]) -> Some (f x)
      | x -> bad_type "option" x

    let nullable_of_jsonlike f = function
      | Atd_jsonlike.AST.Null _ -> None
      | x -> Some (f x)

    let assoc_of_jsonlike f = function
      | Atd_jsonlike.AST.Object (_, pairs) ->
          List.map (fun (_, k, v) -> (k, f v)) pairs
      | x -> bad_type "object" x
  end
end

type all_types = {
  a_unit: unit;
  a_bool: bool;
  a_int: int;
  a_float: float;
  a_string: string;
  a_list: int list;
  a_option: string option;
  a_nullable: bool option;
  a_abstract: Yojson.Safe.t;
  a_tuple: (int * string * bool);
  a_nested: (float list) option;
}

let create_all_types ~a_unit ~a_bool ~a_int ~a_float ~a_string ~a_list ~a_option ~a_nullable ~a_abstract ~a_tuple ~a_nested () : all_types =
  { a_unit; a_bool; a_int; a_float; a_string; a_list; a_option; a_nullable; a_abstract; a_tuple; a_nested }

let all_types_of_yojson (x : Yojson.Safe.t) : all_types =
  match x with
  | `Assoc fields ->
    (* Duplicate JSON keys: behavior is unspecified (RFC 8259 §4 says keys SHOULD
       be unique). Below the threshold, List.assoc_opt returns the first binding;
       above it, the hashtable returns the last. *)
    let assoc_ =
      if Atdml_runtime.list_length_gt 5 fields then
        let tbl = Hashtbl.create 16 in
        List.iter (fun (k, v) -> Hashtbl.add tbl k v) fields;
        (fun key -> Hashtbl.find_opt tbl key)
      else (fun key -> List.assoc_opt key fields)
    in
    let a_unit =
      match assoc_ "a_unit" with
      | Some v -> Atdml_runtime.Yojson.unit_of_yojson v
      | None -> Atdml_runtime.Yojson.missing_field "all_types" "a_unit"
    in
    let a_bool =
      match assoc_ "a_bool" with
      | Some v -> Atdml_runtime.Yojson.bool_of_yojson v
      | None -> Atdml_runtime.Yojson.missing_field "all_types" "a_bool"
    in
    let a_int =
      match assoc_ "a_int" with
      | Some v -> Atdml_runtime.Yojson.int_of_yojson v
      | None -> Atdml_runtime.Yojson.missing_field "all_types" "a_int"
    in
    let a_float =
      match assoc_ "a_float" with
      | Some v -> Atdml_runtime.Yojson.float_of_yojson v
      | None -> Atdml_runtime.Yojson.missing_field "all_types" "a_float"
    in
    let a_string =
      match assoc_ "a_string" with
      | Some v -> Atdml_runtime.Yojson.string_of_yojson v
      | None -> Atdml_runtime.Yojson.missing_field "all_types" "a_string"
    in
    let a_list =
      match assoc_ "a_list" with
      | Some v -> (Atdml_runtime.Yojson.list_of_yojson Atdml_runtime.Yojson.int_of_yojson) v
      | None -> Atdml_runtime.Yojson.missing_field "all_types" "a_list"
    in
    let a_option =
      match assoc_ "a_option" with
      | Some v -> (Atdml_runtime.Yojson.option_of_yojson Atdml_runtime.Yojson.string_of_yojson) v
      | None -> Atdml_runtime.Yojson.missing_field "all_types" "a_option"
    in
    let a_nullable =
      match assoc_ "a_nullable" with
      | Some v -> (Atdml_runtime.Yojson.nullable_of_yojson Atdml_runtime.Yojson.bool_of_yojson) v
      | None -> Atdml_runtime.Yojson.missing_field "all_types" "a_nullable"
    in
    let a_abstract =
      match assoc_ "a_abstract" with
      | Some v -> (fun x -> x) v
      | None -> Atdml_runtime.Yojson.missing_field "all_types" "a_abstract"
    in
    let a_tuple =
      match assoc_ "a_tuple" with
      | Some v -> (fun x -> match x with | `List lst when List.length lst = 3 -> (Atdml_runtime.Yojson.int_of_yojson (List.nth lst 0), Atdml_runtime.Yojson.string_of_yojson (List.nth lst 1), Atdml_runtime.Yojson.bool_of_yojson (List.nth lst 2)) | _ -> Atdml_runtime.Yojson.bad_type "tuple" x) v
      | None -> Atdml_runtime.Yojson.missing_field "all_types" "a_tuple"
    in
    let a_nested =
      match assoc_ "a_nested" with
      | Some v -> (Atdml_runtime.Yojson.option_of_yojson (fun x -> match x with | `List lst when List.length lst = 1 -> ((Atdml_runtime.Yojson.list_of_yojson Atdml_runtime.Yojson.float_of_yojson) (List.nth lst 0)) | _ -> Atdml_runtime.Yojson.bad_type "tuple" x)) v
      | None -> Atdml_runtime.Yojson.missing_field "all_types" "a_nested"
    in
    { a_unit; a_bool; a_int; a_float; a_string; a_list; a_option; a_nullable; a_abstract; a_tuple; a_nested }
  | _ -> Atdml_runtime.Yojson.bad_type "all_types" x

let all_types_of_jsonlike (x : Atd_jsonlike.AST.t) : all_types =
  match x with
  | Atd_jsonlike.AST.Object (_, fields) ->
    let atdml_node_ = x in
    let assoc_ =
      if Atdml_runtime.list_length_gt 5 fields then
        let tbl = Hashtbl.create 16 in
        List.iter (fun (_, k, v) -> Hashtbl.add tbl k v) fields;
        (fun key -> Hashtbl.find_opt tbl key)
      else
        (fun key ->
          match List.find_opt (fun (_, k, _) -> k = key) fields with
          | None -> None | Some (_, _, v) -> Some v)
    in
    let a_unit =
      match assoc_ "a_unit" with
      | Some v -> Atdml_runtime.Jsonlike.unit_of_jsonlike v
      | None -> Atdml_runtime.Jsonlike.missing_field atdml_node_ "all_types" "a_unit"
    in
    let a_bool =
      match assoc_ "a_bool" with
      | Some v -> Atdml_runtime.Jsonlike.bool_of_jsonlike v
      | None -> Atdml_runtime.Jsonlike.missing_field atdml_node_ "all_types" "a_bool"
    in
    let a_int =
      match assoc_ "a_int" with
      | Some v -> Atdml_runtime.Jsonlike.int_of_jsonlike v
      | None -> Atdml_runtime.Jsonlike.missing_field atdml_node_ "all_types" "a_int"
    in
    let a_float =
      match assoc_ "a_float" with
      | Some v -> Atdml_runtime.Jsonlike.float_of_jsonlike v
      | None -> Atdml_runtime.Jsonlike.missing_field atdml_node_ "all_types" "a_float"
    in
    let a_string =
      match assoc_ "a_string" with
      | Some v -> Atdml_runtime.Jsonlike.string_of_jsonlike v
      | None -> Atdml_runtime.Jsonlike.missing_field atdml_node_ "all_types" "a_string"
    in
    let a_list =
      match assoc_ "a_list" with
      | Some v -> (Atdml_runtime.Jsonlike.list_of_jsonlike Atdml_runtime.Jsonlike.int_of_jsonlike) v
      | None -> Atdml_runtime.Jsonlike.missing_field atdml_node_ "all_types" "a_list"
    in
    let a_option =
      match assoc_ "a_option" with
      | Some v -> (Atdml_runtime.Jsonlike.option_of_jsonlike Atdml_runtime.Jsonlike.string_of_jsonlike) v
      | None -> Atdml_runtime.Jsonlike.missing_field atdml_node_ "all_types" "a_option"
    in
    let a_nullable =
      match assoc_ "a_nullable" with
      | Some v -> (Atdml_runtime.Jsonlike.nullable_of_jsonlike Atdml_runtime.Jsonlike.bool_of_jsonlike) v
      | None -> Atdml_runtime.Jsonlike.missing_field atdml_node_ "all_types" "a_nullable"
    in
    let a_abstract =
      match assoc_ "a_abstract" with
      | Some v -> (fun _ -> failwith "abstract type is not supported in jsonlike mode") v
      | None -> Atdml_runtime.Jsonlike.missing_field atdml_node_ "all_types" "a_abstract"
    in
    let a_tuple =
      match assoc_ "a_tuple" with
      | Some v -> (fun x -> match x with | Atd_jsonlike.AST.Array (_, lst) when List.length lst = 3 -> (Atdml_runtime.Jsonlike.int_of_jsonlike (List.nth lst 0), Atdml_runtime.Jsonlike.string_of_jsonlike (List.nth lst 1), Atdml_runtime.Jsonlike.bool_of_jsonlike (List.nth lst 2)) | _ -> Atdml_runtime.Jsonlike.bad_type "tuple" x) v
      | None -> Atdml_runtime.Jsonlike.missing_field atdml_node_ "all_types" "a_tuple"
    in
    let a_nested =
      match assoc_ "a_nested" with
      | Some v -> (Atdml_runtime.Jsonlike.option_of_jsonlike (fun x -> match x with | Atd_jsonlike.AST.Array (_, lst) when List.length lst = 1 -> ((Atdml_runtime.Jsonlike.list_of_jsonlike Atdml_runtime.Jsonlike.float_of_jsonlike) (List.nth lst 0)) | _ -> Atdml_runtime.Jsonlike.bad_type "tuple" x)) v
      | None -> Atdml_runtime.Jsonlike.missing_field atdml_node_ "all_types" "a_nested"
    in
    { a_unit; a_bool; a_int; a_float; a_string; a_list; a_option; a_nullable; a_abstract; a_tuple; a_nested }
  | _ -> Atdml_runtime.Jsonlike.bad_type "all_types" x

let yojson_of_all_types (x : all_types) : Yojson.Safe.t =
  `Assoc (List.concat [
    [("a_unit", Atdml_runtime.Yojson.yojson_of_unit x.a_unit)];
    [("a_bool", Atdml_runtime.Yojson.yojson_of_bool x.a_bool)];
    [("a_int", Atdml_runtime.Yojson.yojson_of_int x.a_int)];
    [("a_float", Atdml_runtime.Yojson.yojson_of_float x.a_float)];
    [("a_string", Atdml_runtime.Yojson.yojson_of_string x.a_string)];
    [("a_list", (Atdml_runtime.Yojson.yojson_of_list Atdml_runtime.Yojson.yojson_of_int) x.a_list)];
    [("a_option", (Atdml_runtime.Yojson.yojson_of_option Atdml_runtime.Yojson.yojson_of_string) x.a_option)];
    [("a_nullable", (Atdml_runtime.Yojson.yojson_of_nullable Atdml_runtime.Yojson.yojson_of_bool) x.a_nullable)];
    [("a_abstract", (fun x -> x) x.a_abstract)];
    [("a_tuple", (fun (x0, x1, x2) -> `List [Atdml_runtime.Yojson.yojson_of_int x0; Atdml_runtime.Yojson.yojson_of_string x1; Atdml_runtime.Yojson.yojson_of_bool x2]) x.a_tuple)];
    [("a_nested", (Atdml_runtime.Yojson.yojson_of_option (fun (x0) -> `List [(Atdml_runtime.Yojson.yojson_of_list Atdml_runtime.Yojson.yojson_of_float) x0])) x.a_nested)];
  ])

let all_types_of_json s =
  all_types_of_yojson (Yojson.Safe.from_string s)

let json_of_all_types x =
  Yojson.Safe.to_string (yojson_of_all_types x)

module All_types = struct
  type nonrec t = all_types
  let create = create_all_types
  let of_yojson = all_types_of_yojson
  let of_jsonlike = all_types_of_jsonlike
  let to_yojson = yojson_of_all_types
  let of_json = all_types_of_json
  let to_json = json_of_all_types
end

--- Input:
{
  "a_unit": null,
  "a_bool": false,
  "a_int": -2,
  "a_float": 9.6,
  "a_string": "x y",
  "a_list": [ 1, 2, 3 ],
  "a_option": "None",
  "a_nullable": null,
  "a_abstract": { "key": "val" },
  "a_tuple": [ 12, "ddd", true ],
  "a_nested": [ "Some", [ [ 1, 2.3 ] ] ]
}
--- Output:
{
  "a_unit": null,
  "a_bool": false,
  "a_int": -2,
  "a_float": 9.6,
  "a_string": "x y",
  "a_list": [ 1, 2, 3 ],
  "a_option": "None",
  "a_nullable": null,
  "a_abstract": { "key": "val" },
  "a_tuple": [ 12, "ddd", true ],
  "a_nested": [ "Some", [ [ 1.0, 2.3 ] ] ]
}
