Yaml encoder/decoder for OCaml jsont codecs

simplify

Changed files
+105 -162
lib
+105 -162
lib/yamlt.ml
···
(* Decoder helpers *)
let check_depth d ~nest =
if nest > d.max_depth then
-
Jsont.Error.msgf Jsont.Meta.none "Maximum nesting depth %d exceeded"
-
d.max_depth
let check_nodes d =
d.node_count <- d.node_count + 1;
if d.node_count > d.max_nodes then
-
Jsont.Error.msgf Jsont.Meta.none "Maximum node count %d exceeded"
-
d.max_nodes
let meta_of_span d span =
if not d.locs then d.meta_none
···
| Some ev ->
let span = ev.Event.span in
let meta = meta_of_span d span in
-
Jsont.Error.msgf meta "Expected %s but found %a" name Event.pp
-
ev.Event.event
-
| None ->
-
Jsont.Error.msgf Jsont.Meta.none "Expected %s but reached end of stream"
-
name
(* Error helpers *)
let _err_expected_scalar d ev =
let meta = meta_of_span d ev.Event.span in
-
Jsont.Error.msgf meta "Expected scalar but found %a" Event.pp ev.Event.event
let err_type_mismatch d span t ~fnd =
let meta = meta_of_span d span in
-
Jsont.Error.msgf meta "Expected %s but found %s" (Jsont.Repr.kinded_sort t)
-
fnd
(* YAML scalar resolution *)
···
fun d ~nest t ->
check_depth d ~nest;
match peek_event d with
-
| None -> Jsont.Error.msgf Jsont.Meta.none "Unexpected end of YAML stream"
| Some ev -> (
match (ev.Event.event, t) with
(* Scalar events *)
···
| Event.Mapping_start _, _ -> err_type_mismatch d ev.span t ~fnd:"mapping"
(* Unexpected events *)
| Event.Sequence_end, _ ->
-
Jsont.Error.msgf (meta_of_span d ev.span) "Unexpected sequence end"
| Event.Mapping_end, _ ->
-
Jsont.Error.msgf (meta_of_span d ev.span) "Unexpected mapping end"
| Event.Document_start _, _ ->
-
Jsont.Error.msgf (meta_of_span d ev.span) "Unexpected document start"
| Event.Document_end _, _ ->
-
Jsont.Error.msgf (meta_of_span d ev.span) "Unexpected document end"
| Event.Stream_start _, _ ->
-
Jsont.Error.msgf (meta_of_span d ev.span) "Unexpected stream start"
| Event.Stream_end, _ ->
-
Jsont.Error.msgf (meta_of_span d ev.span) "Unexpected stream end")
and decode_scalar : type a.
decoder -> nest:int -> Event.spanned -> string -> Scalar_style.t -> a t -> a
···
a =
fun d ev value style t map ->
check_nodes d;
(* Determine which decoder to use based on scalar content *)
if is_null_scalar value then
match map.dec_null with
| Some t' -> decode_scalar_as d ev value style t'
-
| None ->
-
Jsont.Repr.type_error (meta_of_span d ev.span) t ~fnd:Jsont.Sort.Null
else if style = `Plain then
(* Try bool, then number, then string *)
match bool_of_scalar_opt value with
···
| None -> (
match map.dec_string with
| Some t' -> decode_scalar_as d ev value style t'
-
| None ->
-
Jsont.Repr.type_error (meta_of_span d ev.span) t
-
~fnd:Jsont.Sort.Bool))
| None -> (
match float_of_scalar_opt value with
| Some _ -> (
···
| None -> (
match map.dec_string with
| Some t' -> decode_scalar_as d ev value style t'
-
| None ->
-
Jsont.Repr.type_error (meta_of_span d ev.span) t
-
~fnd:Jsont.Sort.Number))
| None -> (
(* Plain scalar that's not bool/number -> string *)
match map.dec_string with
| Some t' -> decode_scalar_as d ev value style t'
-
| None ->
-
Jsont.Repr.type_error (meta_of_span d ev.span) t
-
~fnd:Jsont.Sort.String))
else
(* Quoted scalars are strings *)
match map.dec_string with
| Some t' -> decode_scalar_as d ev value style t'
-
| None ->
-
Jsont.Repr.type_error (meta_of_span d ev.span) t ~fnd:Jsont.Sort.String
and decode_alias : type a. decoder -> Event.spanned -> string -> a t -> a =
fun d ev anchor t ->
···
match Hashtbl.find_opt d._anchors anchor with
| None ->
let meta = meta_of_span d ev.span in
-
Jsont.Error.msgf meta "Unknown anchor: %s" anchor
-
| Some json -> (
(* Decode the stored JSON value through the type *)
let t' = Jsont.Repr.unsafe_to_t t in
-
match Jsont.Json.decode' t' json with
| Ok v -> v
-
| Error e -> raise (Jsont.Error e))
and decode_array : type a elt b.
decoder -> nest:int -> Event.spanned -> (a, elt, b) array_map -> a =
-
fun d ~nest start_ev map ->
skip_event d;
(* consume Sequence_start *)
check_nodes d;
let meta = meta_of_span d start_ev.span in
-
let builder = ref (map.dec_empty ()) in
let idx = ref 0 in
let rec loop () =
match peek_event d with
| Some { Event.event = Event.Sequence_end; span } ->
skip_event d;
let end_meta = meta_of_span d span in
-
map.dec_finish end_meta !idx !builder
| Some _ ->
let i = !idx in
(try
-
if map.dec_skip i !builder then begin
(* Skip this element by decoding as ignore *)
let _ : unit =
decode d ~nest:(nest + 1) (Jsont.Repr.of_t Jsont.ignore)
···
()
end
else begin
-
let elt = decode d ~nest:(nest + 1) map.elt in
-
builder := map.dec_add i elt !builder
end
with Jsont.Error e ->
-
let imeta = Jsont.Meta.none in
-
Jsont.Repr.error_push_array meta map (i, imeta) e);
incr idx;
loop ()
-
| None -> Jsont.Error.msgf meta "Unclosed sequence"
in
loop ()
···
mem_dec String_map.t ->
Dict.t ->
Dict.t =
-
fun d ~nest obj_meta map umems mem_miss dict ->
let ubuilder =
ref
(match umems with
···
| Some { Event.event = Event.Mapping_end; _ } ->
skip_event d;
(* Finalize *)
-
finish_object obj_meta map umems !ubuilder !mem_miss !dict
| Some ev ->
(* Expect a scalar key *)
let name, name_meta = decode_mapping_key d ev in
(* Look up member decoder *)
-
(match String_map.find_opt name map.mem_decs with
| Some (Mem_dec mem) -> (
mem_miss := String_map.remove name !mem_miss;
try
let v = decode d ~nest:(nest + 1) mem.type' in
dict := Dict.add mem.id v !dict
with Jsont.Error e ->
-
Jsont.Repr.error_push_object obj_meta map (name, name_meta) e)
| None -> (
(* Unknown member *)
match umems with
···
in
()
| Unknown_error ->
-
Jsont.Repr.unexpected_mems_error obj_meta map
~fnd:[ (name, name_meta) ]
| Unknown_keep (mmap, _) -> (
try
let v = decode d ~nest:(nest + 1) mmap.mems_type in
ubuilder := mmap.dec_add name_meta name v !ubuilder
with Jsont.Error e ->
-
Jsont.Repr.error_push_object obj_meta map (name, name_meta) e)
-
));
loop ()
-
| None -> Jsont.Error.msgf obj_meta "Unclosed mapping"
in
loop ()
···
Dict.t ->
Dict.t =
fun meta map umems ubuilder mem_miss dict ->
let dict = Dict.add object_meta_arg meta dict in
let dict =
match umems with
···
with Exit ->
let no_default _ (Mem_dec mm) = Option.is_none mm.dec_absent in
let exp = String_map.filter no_default mem_miss in
-
Jsont.Repr.missing_mems_error meta map ~exp ~fnd:[]
and decode_object_cases : type o cases tag.
decoder ->
···
(Jsont.name * Jsont.json) list ->
Dict.t ->
Dict.t =
-
fun d ~nest obj_meta map umems cases mem_miss delayed dict ->
match peek_event d with
| Some { Event.event = Event.Mapping_end; _ } -> (
skip_event d;
(* No tag found - use dec_absent if available *)
match cases.tag.dec_absent with
| Some tag ->
-
decode_with_case_tag d ~nest obj_meta map umems cases tag mem_miss
-
delayed dict
| None ->
(* Missing required case tag *)
let exp = String_map.singleton cases.tag.name (Mem_dec cases.tag) in
let fnd = List.map (fun ((n, _), _) -> n) delayed in
-
Jsont.Repr.missing_mems_error obj_meta map ~exp ~fnd)
| Some ev ->
let name, name_meta = decode_mapping_key d ev in
if String.equal name cases.tag.name then begin
(* Found the case tag *)
let tag = decode d ~nest:(nest + 1) cases.tag.type' in
-
decode_with_case_tag d ~nest obj_meta map umems cases tag mem_miss
-
delayed dict
end
else begin
(* Not the case tag - check if known member or delay *)
-
match String_map.find_opt name map.mem_decs with
| Some (Mem_dec mem) -> (
let mem_miss = String_map.remove name mem_miss in
try
let v = decode d ~nest:(nest + 1) mem.type' in
let dict = Dict.add mem.id v dict in
-
decode_object_cases d ~nest obj_meta map umems cases mem_miss
-
delayed dict
with Jsont.Error e ->
-
Jsont.Repr.error_push_object obj_meta map (name, name_meta) e)
| None ->
(* Unknown member - decode as generic JSON and delay *)
-
let v = decode d ~nest:(nest + 1) (Jsont.Repr.of_t Jsont.json) in
let delayed = ((name, name_meta), v) :: delayed in
-
decode_object_cases d ~nest obj_meta map umems cases mem_miss
-
delayed dict
end
-
| None -> Jsont.Error.msgf obj_meta "Unclosed mapping"
and decode_with_case_tag : type o cases tag.
decoder ->
···
Dict.t ->
Dict.t =
fun d ~nest obj_meta map umems cases tag mem_miss delayed dict ->
let eq_tag (Case c) = cases.tag_compare c.tag tag = 0 in
match List.find_opt eq_tag cases.cases with
-
| None -> Jsont.Repr.unexpected_case_tag_error obj_meta map cases tag
| Some (Case case) ->
(* Continue decoding with the case's object map *)
let case_dict =
···
let mem_miss = String_map.union u mem_miss case_map.mem_decs in
let dict, mem_miss =
List.fold_left
-
(fun (dict, mem_miss) ((name, meta), json) ->
match String_map.find_opt name case_map.mem_decs with
| Some (Mem_dec mem) -> (
let t' = Jsont.Repr.unsafe_to_t mem.type' in
-
match Jsont.Json.decode' t' json with
| Ok v ->
let dict = Dict.add mem.id v dict in
let mem_miss = String_map.remove name mem_miss in
···
(value, meta)
| _ ->
let meta = meta_of_span d ev.span in
-
Jsont.Error.msgf meta "Mapping keys must be scalars (strings), found %a"
-
Event.pp ev.event
(* Skip stream/document wrappers *)
let skip_to_content d =
···
| None -> ()
| Some ev ->
let meta = meta_of_span d ev.span in
-
Jsont.Error.msgf meta "Expected end of document but found %a" Event.pp
-
ev.event
in
loop ()
···
| Error.Yamlrw_error err ->
skip_to_document_end d;
let msg = Error.to_string err in
-
let e =
-
Jsont.Error.make_msg Jsont.Error.Context.empty Jsont.Meta.none msg
-
in
Seq.Cons (Error e, next_doc))
in
next_doc
···
| Jsont.Error e -> Error e
| Error.Yamlrw_error err ->
let msg = Error.to_string err in
-
Error (Jsont.Error.make_msg Jsont.Error.Context.empty Jsont.Meta.none msg)
let decode ?layout ?locs ?file ?max_depth ?max_nodes t reader =
Result.map_error Jsont.Error.to_string
···
else if String.contains s '\n' then `Literal
else if String.length s > 80 then `Folded
else `Plain
(* Encode null *)
-
let encode_null e _meta =
-
Emitter.emit e.emitter
-
(Event.Scalar
-
{
-
anchor = None;
-
tag = None;
-
value = "null";
-
plain_implicit = true;
-
quoted_implicit = true;
-
style = `Plain;
-
})
(* Encode boolean *)
let encode_bool e _meta b =
-
Emitter.emit e.emitter
-
(Event.Scalar
-
{
-
anchor = None;
-
tag = None;
-
value = (if b then "true" else "false");
-
plain_implicit = true;
-
quoted_implicit = true;
-
style = `Plain;
-
})
(* Encode number *)
let encode_number e _meta f =
···
if Float.is_integer f && Float.abs f < 1e15 then Printf.sprintf "%.0f" f
else Printf.sprintf "%g" f
in
-
Emitter.emit e.emitter
-
(Event.Scalar
-
{
-
anchor = None;
-
tag = None;
-
value;
-
plain_implicit = true;
-
quoted_implicit = true;
-
style = `Plain;
-
})
(* Encode string *)
let encode_string e _meta s =
let style = choose_scalar_style ~preferred:e.scalar_style s in
-
Emitter.emit e.emitter
-
(Event.Scalar
-
{
-
anchor = None;
-
tag = None;
-
value = s;
-
plain_implicit = true;
-
quoted_implicit = true;
-
style;
-
})
let rec encode : type a. encoder -> a t -> a -> unit =
fun e t v ->
···
and encode_array : type a elt b. encoder -> (a, elt, b) array_map -> a -> unit =
fun e map v ->
let style = layout_style_of_format e.format in
-
Emitter.emit e.emitter
(Event.Sequence_start { anchor = None; tag = None; implicit = true; style });
let _ =
map.enc
···
())
() v
in
-
Emitter.emit e.emitter Event.Sequence_end
and encode_object : type o. encoder -> (o, o) object_map -> o -> unit =
fun e map v ->
let style = layout_style_of_format e.format in
-
Emitter.emit e.emitter
-
(Event.Mapping_start { anchor = None; tag = None; implicit = true; style });
(* Encode each member *)
List.iter
(fun (Mem_enc mem) ->
let mem_v = mem.enc v in
if not (mem.enc_omit mem_v) then begin
(* Emit key *)
-
Emitter.emit e.emitter
-
(Event.Scalar
-
{
-
anchor = None;
-
tag = None;
-
value = mem.name;
-
plain_implicit = true;
-
quoted_implicit = true;
-
style = `Plain;
-
});
(* Emit value *)
encode e mem.type' mem_v
end)
···
let (Case_value (case_map, case_v)) = cases.enc_case (cases.enc v) in
(* Emit case tag *)
if not (cases.tag.enc_omit case_map.tag) then begin
-
Emitter.emit e.emitter
-
(Event.Scalar
-
{
-
anchor = None;
-
tag = None;
-
value = cases.tag.name;
-
plain_implicit = true;
-
quoted_implicit = true;
-
style = `Plain;
-
});
encode e cases.tag.type' case_map.tag
end;
(* Emit case members *)
···
(fun (Mem_enc mem) ->
let mem_v = mem.enc case_v in
if not (mem.enc_omit mem_v) then begin
-
Emitter.emit e.emitter
-
(Event.Scalar
-
{
-
anchor = None;
-
tag = None;
-
value = mem.name;
-
plain_implicit = true;
-
quoted_implicit = true;
-
style = `Plain;
-
});
encode e mem.type' mem_v
end)
case_map.object_map.mem_encs);
-
Emitter.emit e.emitter Event.Mapping_end
(* Public encode API *)
···
let emitter = Emitter.of_writer ~config writer in
let e = make_encoder ?format ?indent ?explicit_doc ?scalar_style emitter in
try
-
Emitter.emit e.emitter (Event.Stream_start { encoding = `Utf8 });
-
Emitter.emit e.emitter
-
(Event.Document_start { version = None; implicit = not e.explicit_doc });
let t' = Jsont.Repr.of_t t in
encode e t' v;
-
Emitter.emit e.emitter
-
(Event.Document_end { implicit = not e.explicit_doc });
-
Emitter.emit e.emitter Event.Stream_end;
if eod then Emitter.flush e.emitter;
Ok ()
with
| Jsont.Error err -> Error err
| Error.Yamlrw_error err ->
let msg = Error.to_string err in
-
Error (Jsont.Error.make_msg Jsont.Error.Context.empty Jsont.Meta.none msg)
let encode ?buf ?format ?indent ?explicit_doc ?scalar_style t v ~eod writer =
Result.map_error Jsont.Error.to_string
···
(* Decoder helpers *)
+
(* Local helper to reduce Jsont.Error.msgf boilerplate *)
+
let err_msg meta fmt = Jsont.Error.msgf meta fmt
+
let err_msg_none fmt = Jsont.Error.msgf Jsont.Meta.none fmt
+
let check_depth d ~nest =
if nest > d.max_depth then
+
err_msg_none "Maximum nesting depth %d exceeded" d.max_depth
let check_nodes d =
d.node_count <- d.node_count + 1;
if d.node_count > d.max_nodes then
+
err_msg_none "Maximum node count %d exceeded" d.max_nodes
let meta_of_span d span =
if not d.locs then d.meta_none
···
| Some ev ->
let span = ev.Event.span in
let meta = meta_of_span d span in
+
err_msg meta "Expected %s but found %a" name Event.pp ev.Event.event
+
| None -> err_msg_none "Expected %s but reached end of stream" name
(* Error helpers *)
let _err_expected_scalar d ev =
let meta = meta_of_span d ev.Event.span in
+
err_msg meta "Expected scalar but found %a" Event.pp ev.Event.event
let err_type_mismatch d span t ~fnd =
+
let open Jsont.Repr in
let meta = meta_of_span d span in
+
err_msg meta "Expected %s but found %s" (kinded_sort t) fnd
(* YAML scalar resolution *)
···
fun d ~nest t ->
check_depth d ~nest;
match peek_event d with
+
| None -> err_msg_none "Unexpected end of YAML stream"
| Some ev -> (
match (ev.Event.event, t) with
(* Scalar events *)
···
| Event.Mapping_start _, _ -> err_type_mismatch d ev.span t ~fnd:"mapping"
(* Unexpected events *)
| Event.Sequence_end, _ ->
+
err_msg (meta_of_span d ev.span) "Unexpected sequence end"
| Event.Mapping_end, _ ->
+
err_msg (meta_of_span d ev.span) "Unexpected mapping end"
| Event.Document_start _, _ ->
+
err_msg (meta_of_span d ev.span) "Unexpected document start"
| Event.Document_end _, _ ->
+
err_msg (meta_of_span d ev.span) "Unexpected document end"
| Event.Stream_start _, _ ->
+
err_msg (meta_of_span d ev.span) "Unexpected stream start"
| Event.Stream_end, _ ->
+
err_msg (meta_of_span d ev.span) "Unexpected stream end")
and decode_scalar : type a.
decoder -> nest:int -> Event.spanned -> string -> Scalar_style.t -> a t -> a
···
a =
fun d ev value style t map ->
check_nodes d;
+
let meta = meta_of_span d ev.span in
+
let type_err fnd = Jsont.Repr.type_error meta t ~fnd in
(* Determine which decoder to use based on scalar content *)
if is_null_scalar value then
match map.dec_null with
| Some t' -> decode_scalar_as d ev value style t'
+
| None -> type_err Jsont.Sort.Null
else if style = `Plain then
(* Try bool, then number, then string *)
match bool_of_scalar_opt value with
···
| None -> (
match map.dec_string with
| Some t' -> decode_scalar_as d ev value style t'
+
| None -> type_err Jsont.Sort.Bool))
| None -> (
match float_of_scalar_opt value with
| Some _ -> (
···
| None -> (
match map.dec_string with
| Some t' -> decode_scalar_as d ev value style t'
+
| None -> type_err Jsont.Sort.Number))
| None -> (
(* Plain scalar that's not bool/number -> string *)
match map.dec_string with
| Some t' -> decode_scalar_as d ev value style t'
+
| None -> type_err Jsont.Sort.String))
else
(* Quoted scalars are strings *)
match map.dec_string with
| Some t' -> decode_scalar_as d ev value style t'
+
| None -> type_err Jsont.Sort.String
and decode_alias : type a. decoder -> Event.spanned -> string -> a t -> a =
fun d ev anchor t ->
···
match Hashtbl.find_opt d._anchors anchor with
| None ->
let meta = meta_of_span d ev.span in
+
err_msg meta "Unknown anchor: %s" anchor
+
| Some json_value ->
(* Decode the stored JSON value through the type *)
let t' = Jsont.Repr.unsafe_to_t t in
+
match Jsont.Json.decode' t' json_value with
| Ok v -> v
+
| Error e -> raise (Jsont.Error e)
and decode_array : type a elt b.
decoder -> nest:int -> Event.spanned -> (a, elt, b) array_map -> a =
+
fun d ~nest start_ev array_map ->
skip_event d;
(* consume Sequence_start *)
check_nodes d;
let meta = meta_of_span d start_ev.span in
+
let builder = ref (array_map.dec_empty ()) in
let idx = ref 0 in
let rec loop () =
match peek_event d with
| Some { Event.event = Event.Sequence_end; span } ->
skip_event d;
let end_meta = meta_of_span d span in
+
array_map.dec_finish end_meta !idx !builder
| Some _ ->
let i = !idx in
(try
+
if array_map.dec_skip i !builder then begin
(* Skip this element by decoding as ignore *)
let _ : unit =
decode d ~nest:(nest + 1) (Jsont.Repr.of_t Jsont.ignore)
···
()
end
else begin
+
let elt = decode d ~nest:(nest + 1) array_map.elt in
+
builder := array_map.dec_add i elt !builder
end
with Jsont.Error e ->
+
Jsont.Repr.error_push_array meta array_map (i, Jsont.Meta.none) e);
incr idx;
loop ()
+
| None -> err_msg meta "Unclosed sequence"
in
loop ()
···
mem_dec String_map.t ->
Dict.t ->
Dict.t =
+
fun d ~nest obj_meta object_map umems mem_miss dict ->
let ubuilder =
ref
(match umems with
···
| Some { Event.event = Event.Mapping_end; _ } ->
skip_event d;
(* Finalize *)
+
finish_object obj_meta object_map umems !ubuilder !mem_miss !dict
| Some ev ->
(* Expect a scalar key *)
let name, name_meta = decode_mapping_key d ev in
(* Look up member decoder *)
+
(match String_map.find_opt name object_map.mem_decs with
| Some (Mem_dec mem) -> (
mem_miss := String_map.remove name !mem_miss;
try
let v = decode d ~nest:(nest + 1) mem.type' in
dict := Dict.add mem.id v !dict
with Jsont.Error e ->
+
Jsont.Repr.error_push_object obj_meta object_map (name, name_meta)
+
e)
| None -> (
(* Unknown member *)
match umems with
···
in
()
| Unknown_error ->
+
Jsont.Repr.unexpected_mems_error obj_meta object_map
~fnd:[ (name, name_meta) ]
| Unknown_keep (mmap, _) -> (
try
let v = decode d ~nest:(nest + 1) mmap.mems_type in
ubuilder := mmap.dec_add name_meta name v !ubuilder
with Jsont.Error e ->
+
Jsont.Repr.error_push_object obj_meta object_map
+
(name, name_meta) e)));
loop ()
+
| None -> err_msg obj_meta "Unclosed mapping"
in
loop ()
···
Dict.t ->
Dict.t =
fun meta map umems ubuilder mem_miss dict ->
+
let open Jsont.Repr in
let dict = Dict.add object_meta_arg meta dict in
let dict =
match umems with
···
with Exit ->
let no_default _ (Mem_dec mm) = Option.is_none mm.dec_absent in
let exp = String_map.filter no_default mem_miss in
+
missing_mems_error meta map ~exp ~fnd:[]
and decode_object_cases : type o cases tag.
decoder ->
···
(Jsont.name * Jsont.json) list ->
Dict.t ->
Dict.t =
+
fun d ~nest obj_meta object_map umems cases mem_miss delayed dict ->
match peek_event d with
| Some { Event.event = Event.Mapping_end; _ } -> (
skip_event d;
(* No tag found - use dec_absent if available *)
match cases.tag.dec_absent with
| Some tag ->
+
decode_with_case_tag d ~nest obj_meta object_map umems cases tag
+
mem_miss delayed dict
| None ->
(* Missing required case tag *)
let exp = String_map.singleton cases.tag.name (Mem_dec cases.tag) in
let fnd = List.map (fun ((n, _), _) -> n) delayed in
+
Jsont.Repr.missing_mems_error obj_meta object_map ~exp ~fnd)
| Some ev ->
let name, name_meta = decode_mapping_key d ev in
if String.equal name cases.tag.name then begin
(* Found the case tag *)
let tag = decode d ~nest:(nest + 1) cases.tag.type' in
+
decode_with_case_tag d ~nest obj_meta object_map umems cases tag
+
mem_miss delayed dict
end
else begin
(* Not the case tag - check if known member or delay *)
+
match String_map.find_opt name object_map.mem_decs with
| Some (Mem_dec mem) -> (
let mem_miss = String_map.remove name mem_miss in
try
let v = decode d ~nest:(nest + 1) mem.type' in
let dict = Dict.add mem.id v dict in
+
decode_object_cases d ~nest obj_meta object_map umems cases
+
mem_miss delayed dict
with Jsont.Error e ->
+
Jsont.Repr.error_push_object obj_meta object_map (name, name_meta)
+
e)
| None ->
(* Unknown member - decode as generic JSON and delay *)
+
let v =
+
decode d ~nest:(nest + 1) (Jsont.Repr.of_t Jsont.json)
+
in
let delayed = ((name, name_meta), v) :: delayed in
+
decode_object_cases d ~nest obj_meta object_map umems cases
+
mem_miss delayed dict
end
+
| None -> err_msg obj_meta "Unclosed mapping"
and decode_with_case_tag : type o cases tag.
decoder ->
···
Dict.t ->
Dict.t =
fun d ~nest obj_meta map umems cases tag mem_miss delayed dict ->
+
let open Jsont.Repr in
let eq_tag (Case c) = cases.tag_compare c.tag tag = 0 in
match List.find_opt eq_tag cases.cases with
+
| None -> unexpected_case_tag_error obj_meta map cases tag
| Some (Case case) ->
(* Continue decoding with the case's object map *)
let case_dict =
···
let mem_miss = String_map.union u mem_miss case_map.mem_decs in
let dict, mem_miss =
List.fold_left
+
(fun (dict, mem_miss) ((name, meta), json_value) ->
match String_map.find_opt name case_map.mem_decs with
| Some (Mem_dec mem) -> (
let t' = Jsont.Repr.unsafe_to_t mem.type' in
+
match Jsont.Json.decode' t' json_value with
| Ok v ->
let dict = Dict.add mem.id v dict in
let mem_miss = String_map.remove name mem_miss in
···
(value, meta)
| _ ->
let meta = meta_of_span d ev.span in
+
err_msg meta "Mapping keys must be scalars (strings), found %a" Event.pp
+
ev.event
(* Skip stream/document wrappers *)
let skip_to_content d =
···
| None -> ()
| Some ev ->
let meta = meta_of_span d ev.span in
+
err_msg meta "Expected end of document but found %a" Event.pp ev.event
in
loop ()
···
| Error.Yamlrw_error err ->
skip_to_document_end d;
let msg = Error.to_string err in
+
let e = Jsont.(Error.make_msg Error.Context.empty Meta.none msg) in
Seq.Cons (Error e, next_doc))
in
next_doc
···
| Jsont.Error e -> Error e
| Error.Yamlrw_error err ->
let msg = Error.to_string err in
+
Error Jsont.(Error.make_msg Error.Context.empty Meta.none msg)
let decode ?layout ?locs ?file ?max_depth ?max_nodes t reader =
Result.map_error Jsont.Error.to_string
···
else if String.contains s '\n' then `Literal
else if String.length s > 80 then `Folded
else `Plain
+
+
(* Helper to create scalar events with common defaults *)
+
let scalar_event ?(anchor = None) ?(tag = None) ~value ~style () =
+
Event.Scalar
+
{
+
anchor;
+
tag;
+
value;
+
plain_implicit = true;
+
quoted_implicit = true;
+
style;
+
}
+
+
(* Helper to emit events *)
+
let emit e = Emitter.emit e.emitter
(* Encode null *)
+
let encode_null e _meta = emit e (scalar_event ~value:"null" ~style:`Plain ())
(* Encode boolean *)
let encode_bool e _meta b =
+
emit e (scalar_event ~value:(if b then "true" else "false") ~style:`Plain ())
(* Encode number *)
let encode_number e _meta f =
···
if Float.is_integer f && Float.abs f < 1e15 then Printf.sprintf "%.0f" f
else Printf.sprintf "%g" f
in
+
emit e (scalar_event ~value ~style:`Plain ())
(* Encode string *)
let encode_string e _meta s =
let style = choose_scalar_style ~preferred:e.scalar_style s in
+
emit e (scalar_event ~value:s ~style ())
let rec encode : type a. encoder -> a t -> a -> unit =
fun e t v ->
···
and encode_array : type a elt b. encoder -> (a, elt, b) array_map -> a -> unit =
fun e map v ->
let style = layout_style_of_format e.format in
+
emit e
(Event.Sequence_start { anchor = None; tag = None; implicit = true; style });
let _ =
map.enc
···
())
() v
in
+
emit e Event.Sequence_end
and encode_object : type o. encoder -> (o, o) object_map -> o -> unit =
fun e map v ->
let style = layout_style_of_format e.format in
+
emit e (Event.Mapping_start { anchor = None; tag = None; implicit = true; style });
(* Encode each member *)
List.iter
(fun (Mem_enc mem) ->
let mem_v = mem.enc v in
if not (mem.enc_omit mem_v) then begin
(* Emit key *)
+
emit e (scalar_event ~value:mem.name ~style:`Plain ());
(* Emit value *)
encode e mem.type' mem_v
end)
···
let (Case_value (case_map, case_v)) = cases.enc_case (cases.enc v) in
(* Emit case tag *)
if not (cases.tag.enc_omit case_map.tag) then begin
+
emit e (scalar_event ~value:cases.tag.name ~style:`Plain ());
encode e cases.tag.type' case_map.tag
end;
(* Emit case members *)
···
(fun (Mem_enc mem) ->
let mem_v = mem.enc case_v in
if not (mem.enc_omit mem_v) then begin
+
emit e (scalar_event ~value:mem.name ~style:`Plain ());
encode e mem.type' mem_v
end)
case_map.object_map.mem_encs);
+
emit e Event.Mapping_end
(* Public encode API *)
···
let emitter = Emitter.of_writer ~config writer in
let e = make_encoder ?format ?indent ?explicit_doc ?scalar_style emitter in
try
+
emit e (Event.Stream_start { encoding = `Utf8 });
+
emit e (Event.Document_start { version = None; implicit = not e.explicit_doc });
let t' = Jsont.Repr.of_t t in
encode e t' v;
+
emit e (Event.Document_end { implicit = not e.explicit_doc });
+
emit e Event.Stream_end;
if eod then Emitter.flush e.emitter;
Ok ()
with
| Jsont.Error err -> Error err
| Error.Yamlrw_error err ->
let msg = Error.to_string err in
+
Error Jsont.(Error.make_msg Error.Context.empty Meta.none msg)
let encode ?buf ?format ?indent ?explicit_doc ?scalar_style t v ~eod writer =
Result.map_error Jsont.Error.to_string