Pure OCaml Yaml 1.2 reader and writer using Bytesrw

Consolidate duplicate hex utilities and crypto functions

- Replace local hex_of_string/string_of_hex with ohex in tests:
- ocaml-spake2/test/test_spake2.ml
- ocaml-matter/test/test_tlv.ml
- ocaml-cbort/test/test_cbort.ml

- Make pase.ml re-export Crypto.sha256/hmac_sha256 instead of duplicating

- Remove odoc-xo rules from ocaml-yamlrw/doc/dune

- Apply ocamlformat to yamlrw, yamlt, bytesrw-eio

+213 -249
-29
doc/dune
··· 5 5 (documentation 6 6 (package yamlrw) 7 7 (mld_files index tutorial)) 8 - 9 - ; Standalone tutorial HTML generation 10 - (rule 11 - (alias tutorial) 12 - (deps tutorial.mld) 13 - (targets page-tutorial.odoc) 14 - (action 15 - (run odoc compile %{deps}))) 16 - 17 - (rule 18 - (alias tutorial) 19 - (deps page-tutorial.odoc) 20 - (targets 21 - tutorial.html 22 - index.html 23 - odoc.css 24 - highlight.pack.js 25 - katex.min.css 26 - katex.min.js 27 - odoc_search.js 28 - (dir fonts)) 29 - (mode 30 - (promote (until-clean))) 31 - (action 32 - (progn 33 - (run odoc html-generate %{deps} -o .) 34 - (run odoc support-files -o .) 35 - (with-stdout-to index.html 36 - (run odoc-xo tutorial.html)))))
+1 -1
dune-project
··· 1 - (lang dune 3.18) 1 + (lang dune 3.21) 2 2 (name yamlrw) 3 3 4 4 (generate_opam_files true)
+1 -1
lib/chomping.mli
··· 6 6 (** Block scalar chomping indicators *) 7 7 8 8 type t = 9 - | Strip (** Remove final line break and trailing empty lines *) 9 + | Strip (** Remove final line break and trailing empty lines *) 10 10 | Clip (** Keep final line break, remove trailing empty lines (default) *) 11 11 | Keep (** Keep final line break and trailing empty lines *) 12 12
+3 -2
lib/document.mli
··· 14 14 } 15 15 16 16 val make : 17 - ?version:(int * int) -> 17 + ?version:int * int -> 18 18 ?tags:(string * string) list -> 19 19 ?implicit_start:bool -> 20 20 ?implicit_end:bool -> 21 - Yaml.t option -> t 21 + Yaml.t option -> 22 + t 22 23 (** Create a document *) 23 24 24 25 (** {2 Accessors} *)
+2 -2
lib/emitter.mli
··· 5 5 6 6 (** Emitter - converts YAML data structures to string output 7 7 8 - The emitter can write to either a Buffer (default) or directly to a 9 - bytesrw Bytes.Writer for streaming output. *) 8 + The emitter can write to either a Buffer (default) or directly to a bytesrw 9 + Bytes.Writer for streaming output. *) 10 10 11 11 (** {1 Configuration} *) 12 12
+2 -9
lib/encoding.mli
··· 5 5 6 6 (** Character encoding detection and handling *) 7 7 8 - type t = [ 9 - | `Utf8 10 - | `Utf16be 11 - | `Utf16le 12 - | `Utf32be 13 - | `Utf32le 14 - ] 8 + type t = [ `Utf8 | `Utf16be | `Utf16le | `Utf32be | `Utf32le ] 15 9 16 10 val to_string : t -> string 17 11 (** Convert encoding to string representation *) ··· 20 14 (** Pretty-print an encoding *) 21 15 22 16 val detect : string -> t * int 23 - (** Detect encoding from BOM or first bytes. 24 - Returns (encoding, bom_length) *) 17 + (** Detect encoding from BOM or first bytes. Returns (encoding, bom_length) *) 25 18 26 19 val equal : t -> t -> bool 27 20 (** Test equality of two encodings *)
+2 -6
lib/error.mli
··· 7 7 8 8 Comprehensive error reporting for YAML parsing and emission. 9 9 10 - This module provides detailed error types that correspond to various 11 - failure modes in YAML processing, as specified in the 10 + This module provides detailed error types that correspond to various failure 11 + modes in YAML processing, as specified in the 12 12 {{:https://yaml.org/spec/1.2.2/}YAML 1.2.2 specification}. *) 13 13 14 14 (** {2 Error Classification} *) ··· 39 39 | Reserved_directive of string 40 40 | Illegal_flow_key_line 41 41 | Block_sequence_disallowed 42 - 43 42 (* Parser errors *) 44 43 | Unexpected_token of string 45 44 | Expected_document_start ··· 56 55 | Alias_cycle of string 57 56 | Multiple_documents 58 57 | Mapping_key_too_long 59 - 60 58 (* Loader errors *) 61 59 | Invalid_scalar_conversion of string * string 62 60 | Type_mismatch of string * string ··· 64 62 | Key_not_found of string 65 63 | Alias_expansion_node_limit of int 66 64 | Alias_expansion_depth_limit of int 67 - 68 65 (* Emitter errors *) 69 66 | Invalid_encoding of string 70 67 | Scalar_contains_invalid_chars of string 71 68 | Anchor_not_set 72 69 | Invalid_state of string 73 - 74 70 (* Generic *) 75 71 | Custom of string 76 72
+4 -2
lib/event.ml
··· 57 57 Format.fprintf fmt "scalar(anchor=%s, tag=%s, style=%a, value=%S)" 58 58 (pp_opt_str anchor) (pp_opt_str tag) Scalar_style.pp style value 59 59 | Sequence_start { anchor; tag; implicit; style } -> 60 - Format.fprintf fmt "sequence-start(anchor=%s, tag=%s, implicit=%b, style=%a)" 60 + Format.fprintf fmt 61 + "sequence-start(anchor=%s, tag=%s, implicit=%b, style=%a)" 61 62 (pp_opt_str anchor) (pp_opt_str tag) implicit Layout_style.pp style 62 63 | Sequence_end -> Format.fprintf fmt "sequence-end" 63 64 | Mapping_start { anchor; tag; implicit; style } -> 64 - Format.fprintf fmt "mapping-start(anchor=%s, tag=%s, implicit=%b, style=%a)" 65 + Format.fprintf fmt 66 + "mapping-start(anchor=%s, tag=%s, implicit=%b, style=%a)" 65 67 (pp_opt_str anchor) (pp_opt_str tag) implicit Layout_style.pp style 66 68 | Mapping_end -> Format.fprintf fmt "mapping-end" 67 69
+2 -8
lib/event.mli
··· 8 8 type t = 9 9 | Stream_start of { encoding : Encoding.t } 10 10 | Stream_end 11 - | Document_start of { 12 - version : (int * int) option; 13 - implicit : bool; 14 - } 11 + | Document_start of { version : (int * int) option; implicit : bool } 15 12 | Document_end of { implicit : bool } 16 13 | Alias of { anchor : string } 17 14 | Scalar of { ··· 37 34 } 38 35 | Mapping_end 39 36 40 - type spanned = { 41 - event : t; 42 - span : Span.t; 43 - } 37 + type spanned = { event : t; span : Span.t } 44 38 45 39 val pp : Format.formatter -> t -> unit 46 40 (** Pretty-print an event *)
+2 -2
lib/input.mli
··· 5 5 6 6 (** Character input source with lookahead, based on Bytes.Reader.t 7 7 8 - This module wraps a bytesrw [Bytes.Reader.t] to provide character-by-character 9 - access with lookahead for the YAML scanner. *) 8 + This module wraps a bytesrw [Bytes.Reader.t] to provide 9 + character-by-character access with lookahead for the YAML scanner. *) 10 10 11 11 (** {2 Re-exported Character Classification} *) 12 12
+4 -5
lib/layout_style.mli
··· 5 5 6 6 (** Collection layout styles *) 7 7 8 - type t = [ 9 - | `Any (** Let emitter choose *) 10 - | `Block (** Indentation-based *) 11 - | `Flow (** Inline with brackets *) 12 - ] 8 + type t = 9 + [ `Any (** Let emitter choose *) 10 + | `Block (** Indentation-based *) 11 + | `Flow (** Inline with brackets *) ] 13 12 14 13 val to_string : t -> string 15 14 (** Convert style to string representation *)
+6 -9
lib/loader.ml
··· 131 131 let single_document_or_error docs ~empty = 132 132 match docs with 133 133 | [] -> empty 134 - | [doc] -> doc 134 + | [ doc ] -> doc 135 135 | _ -> Error.raise Multiple_documents 136 136 137 137 (** Load single document as Value. ··· 167 167 else yaml 168 168 169 169 (** Load all documents *) 170 - let documents_of_string s = 171 - parse_all_documents (Parser.of_string s) 170 + let documents_of_string s = parse_all_documents (Parser.of_string s) 172 171 173 172 (** {2 Reader-based loading} *) 174 173 ··· 205 204 else yaml 206 205 207 206 (** Load all documents from a Bytes.Reader *) 208 - let documents_of_reader reader = 209 - parse_all_documents (Parser.of_reader reader) 207 + let documents_of_reader reader = parse_all_documents (Parser.of_reader reader) 210 208 211 209 (** {2 Parser-function based loading} 212 210 213 - These functions accept a [unit -> Event.spanned option] function 214 - instead of a [Parser.t], allowing them to work with any event source 215 - (e.g., streaming parsers). *) 211 + These functions accept a [unit -> Event.spanned option] function instead of 212 + a [Parser.t], allowing them to work with any event source (e.g., streaming 213 + parsers). *) 216 214 217 215 (** Generic document loader using event source function *) 218 216 let load_generic_fn extract next_event = ··· 282 280 match load_document parser with None -> acc | Some doc -> loop (f acc doc) 283 281 in 284 282 loop init 285 - 286 283 287 284 (** Load single Value from event source. 288 285
+18 -17
lib/loader.mli
··· 8 8 (** {1 String-based loading} *) 9 9 10 10 val value_of_string : 11 - ?resolve_aliases:bool -> 12 - ?max_nodes:int -> 13 - ?max_depth:int -> 14 - string -> Value.t 11 + ?resolve_aliases:bool -> ?max_nodes:int -> ?max_depth:int -> string -> Value.t 15 12 (** Load single document as Value. 16 13 17 14 @param resolve_aliases Whether to resolve aliases (default true) ··· 19 16 @param max_depth Maximum alias nesting depth (default 100) *) 20 17 21 18 val yaml_of_string : 22 - ?resolve_aliases:bool -> 23 - ?max_nodes:int -> 24 - ?max_depth:int -> 25 - string -> Yaml.t 19 + ?resolve_aliases:bool -> ?max_nodes:int -> ?max_depth:int -> string -> Yaml.t 26 20 (** Load single document as Yaml. 27 21 28 22 @param resolve_aliases Whether to resolve aliases (default false) ··· 38 32 ?resolve_aliases:bool -> 39 33 ?max_nodes:int -> 40 34 ?max_depth:int -> 41 - Bytesrw.Bytes.Reader.t -> Value.t 35 + Bytesrw.Bytes.Reader.t -> 36 + Value.t 42 37 (** Load single document as Value from a Bytes.Reader *) 43 38 44 39 val yaml_of_reader : 45 40 ?resolve_aliases:bool -> 46 41 ?max_nodes:int -> 47 42 ?max_depth:int -> 48 - Bytesrw.Bytes.Reader.t -> Yaml.t 43 + Bytesrw.Bytes.Reader.t -> 44 + Yaml.t 49 45 (** Load single document as Yaml from a Bytes.Reader *) 50 46 51 47 val documents_of_reader : Bytesrw.Bytes.Reader.t -> Document.t list ··· 57 53 ?resolve_aliases:bool -> 58 54 ?max_nodes:int -> 59 55 ?max_depth:int -> 60 - Parser.t -> Value.t option 56 + Parser.t -> 57 + Value.t option 61 58 (** Load single Value from parser *) 62 59 63 60 val load_yaml : Parser.t -> Yaml.t option ··· 74 71 75 72 (** {1 Event function-based loading} 76 73 77 - These functions accept a [unit -> Event.spanned option] function 78 - instead of a [Parser.t], allowing them to work with any event source. *) 74 + These functions accept a [unit -> Event.spanned option] function instead of 75 + a [Parser.t], allowing them to work with any event source. *) 79 76 80 77 val value_of_parser : 81 78 ?resolve_aliases:bool -> 82 79 ?max_nodes:int -> 83 80 ?max_depth:int -> 84 - (unit -> Event.spanned option) -> Value.t 81 + (unit -> Event.spanned option) -> 82 + Value.t 85 83 (** Load single Value from event source function *) 86 84 87 85 val yaml_of_parser : 88 86 ?resolve_aliases:bool -> 89 87 ?max_nodes:int -> 90 88 ?max_depth:int -> 91 - (unit -> Event.spanned option) -> Yaml.t 89 + (unit -> Event.spanned option) -> 90 + Yaml.t 92 91 (** Load single Yaml from event source function *) 93 92 94 93 val document_of_parser : (unit -> Event.spanned option) -> Document.t option ··· 97 96 val documents_of_parser : (unit -> Event.spanned option) -> Document.t list 98 97 (** Load all documents from event source function *) 99 98 100 - val iter_documents_parser : (Document.t -> unit) -> (unit -> Event.spanned option) -> unit 99 + val iter_documents_parser : 100 + (Document.t -> unit) -> (unit -> Event.spanned option) -> unit 101 101 (** Iterate over documents from event source function *) 102 102 103 - val fold_documents_parser : ('a -> Document.t -> 'a) -> 'a -> (unit -> Event.spanned option) -> 'a 103 + val fold_documents_parser : 104 + ('a -> Document.t -> 'a) -> 'a -> (unit -> Event.spanned option) -> 'a 104 105 (** Fold over documents from event source function *)
+11 -4
lib/mapping.mli
··· 12 12 ?tag:string -> 13 13 ?implicit:bool -> 14 14 ?style:Layout_style.t -> 15 - ('k * 'v) list -> ('k, 'v) t 15 + ('k * 'v) list -> 16 + ('k, 'v) t 16 17 (** Create a mapping *) 17 18 18 19 (** {2 Accessors} *) ··· 49 50 val pp : 50 51 (Format.formatter -> 'k -> unit) -> 51 52 (Format.formatter -> 'v -> unit) -> 52 - Format.formatter -> ('k, 'v) t -> unit 53 - val equal : ('k -> 'k -> bool) -> ('v -> 'v -> bool) -> ('k, 'v) t -> ('k, 'v) t -> bool 54 - val compare : ('k -> 'k -> int) -> ('v -> 'v -> int) -> ('k, 'v) t -> ('k, 'v) t -> int 53 + Format.formatter -> 54 + ('k, 'v) t -> 55 + unit 56 + 57 + val equal : 58 + ('k -> 'k -> bool) -> ('v -> 'v -> bool) -> ('k, 'v) t -> ('k, 'v) t -> bool 59 + 60 + val compare : 61 + ('k -> 'k -> int) -> ('v -> 'v -> int) -> ('k, 'v) t -> ('k, 'v) t -> int
-4
lib/parser.ml
··· 85 85 let check t pred = 86 86 match peek_token t with Some tok -> pred tok.token | None -> false 87 87 88 - 89 88 (** Push state onto stack *) 90 89 let push_state t s = t.states <- s :: t.states 91 90 ··· 620 619 Error.raise_span tok.span 621 620 (Unexpected_token "unexpected token at document start") 622 621 | _ -> parse_document_start t ~implicit:true) 623 - 624 622 | Document_content -> 625 623 if 626 624 check t (function ··· 659 657 (Unexpected_token "content not allowed after document value") 660 658 end 661 659 | Document_end -> parse_document_end t 662 - 663 660 | Block_sequence_first_entry -> 664 661 t.state <- Block_sequence_entry; 665 662 parse_block_sequence_entry t ··· 679 676 | Flow_mapping_first_key -> parse_flow_mapping_key t ~first:true 680 677 | Flow_mapping_key -> parse_flow_mapping_key t ~first:false 681 678 | Flow_mapping_value -> parse_flow_mapping_value t ~empty:false 682 - 683 679 | End -> 684 680 let span = Span.point Position.initial in 685 681 t.finished <- true;
+4 -3
lib/position.mli
··· 6 6 (** Position tracking for source locations *) 7 7 8 8 type t = { 9 - index : int; (** Byte offset from start *) 10 - line : int; (** 1-indexed line number *) 9 + index : int; (** Byte offset from start *) 10 + line : int; (** 1-indexed line number *) 11 11 column : int; (** 1-indexed column number *) 12 12 } 13 13 ··· 24 24 (** Advance by one character, handling newlines appropriately *) 25 25 26 26 val advance_utf8 : Uchar.t -> t -> t 27 - (** Advance by one Unicode character, handling newlines and multi-byte characters *) 27 + (** Advance by one Unicode character, handling newlines and multi-byte 28 + characters *) 28 29 29 30 val advance_bytes : int -> t -> t 30 31 (** Advance by n bytes *)
+4 -4
lib/quoting.mli
··· 6 6 (** YAML scalar quoting detection *) 7 7 8 8 val needs_quoting : string -> bool 9 - (** Check if a string value needs quoting in YAML output. 10 - Returns true if the string: 9 + (** Check if a string value needs quoting in YAML output. Returns true if the 10 + string: 11 11 - Is empty 12 12 - Starts with an indicator character 13 13 - Is a reserved word (null, true, false, yes, no, etc.) ··· 15 15 - Looks like a number *) 16 16 17 17 val needs_double_quotes : string -> bool 18 - (** Check if a string requires double quotes (vs single quotes). 19 - Returns true if the string contains characters that need escape sequences. *) 18 + (** Check if a string requires double quotes (vs single quotes). Returns true if 19 + the string contains characters that need escape sequences. *) 20 20 21 21 val choose_style : string -> [> `Plain | `Single_quoted | `Double_quoted ] 22 22 (** Choose the appropriate quoting style for a string value *)
+2 -1
lib/scalar.mli
··· 13 13 ?plain_implicit:bool -> 14 14 ?quoted_implicit:bool -> 15 15 ?style:Scalar_style.t -> 16 - string -> t 16 + string -> 17 + t 17 18 (** Create a scalar value *) 18 19 19 20 (** {2 Accessors} *)
+7 -8
lib/scalar_style.mli
··· 5 5 6 6 (** Scalar formatting styles *) 7 7 8 - type t = [ 9 - | `Any (** Let emitter choose *) 10 - | `Plain (** Unquoted: foo *) 11 - | `Single_quoted (** 'foo' *) 12 - | `Double_quoted (** "foo" *) 13 - | `Literal (** | block *) 14 - | `Folded (** > block *) 15 - ] 8 + type t = 9 + [ `Any (** Let emitter choose *) 10 + | `Plain (** Unquoted: foo *) 11 + | `Single_quoted (** 'foo' *) 12 + | `Double_quoted (** "foo" *) 13 + | `Literal (** | block *) 14 + | `Folded (** > block *) ] 16 15 17 16 val to_string : t -> string 18 17 (** Convert style to string representation *)
+1 -4
lib/scanner.ml
··· 13 13 } 14 14 (** Simple key tracking for mapping key disambiguation *) 15 15 16 - type indent = { 17 - indent : int; 18 - needs_block_end : bool; 19 - } 16 + type indent = { indent : int; needs_block_end : bool } 20 17 (** Indent level tracking *) 21 18 22 19 type t = {
+2 -1
lib/sequence.mli
··· 12 12 ?tag:string -> 13 13 ?implicit:bool -> 14 14 ?style:Layout_style.t -> 15 - 'a list -> 'a t 15 + 'a list -> 16 + 'a t 16 17 (** Create a sequence *) 17 18 18 19 (** {2 Accessors} *)
+4 -2
lib/serialize.ml
··· 120 120 | `A items -> 121 121 (* Force flow style for empty sequences, otherwise use config *) 122 122 let style = 123 - if items = [] || config.Emitter.layout_style = `Flow then `Flow else `Block 123 + if items = [] || config.Emitter.layout_style = `Flow then `Flow 124 + else `Block 124 125 in 125 126 emit 126 127 (Event.Sequence_start ··· 130 131 | `O pairs -> 131 132 (* Force flow style for empty mappings, otherwise use config *) 132 133 let style = 133 - if pairs = [] || config.Emitter.layout_style = `Flow then `Flow else `Block 134 + if pairs = [] || config.Emitter.layout_style = `Flow then `Flow 135 + else `Block 134 136 in 135 137 emit 136 138 (Event.Mapping_start
+36 -36
lib/serialize.mli
··· 14 14 (** Emit a YAML node to an emitter *) 15 15 16 16 val emit_yaml : Emitter.t -> Yaml.t -> unit 17 - (** Emit a complete YAML document to an emitter (includes stream/document markers) *) 17 + (** Emit a complete YAML document to an emitter (includes stream/document 18 + markers) *) 18 19 19 20 val emit_value_node : Emitter.t -> Value.t -> unit 20 21 (** Emit a Value node to an emitter *) 21 22 22 23 val emit_value : Emitter.t -> Value.t -> unit 23 - (** Emit a complete Value document to an emitter (includes stream/document markers) *) 24 + (** Emit a complete Value document to an emitter (includes stream/document 25 + markers) *) 24 26 25 27 val emit_document : ?resolve_aliases:bool -> Emitter.t -> Document.t -> unit 26 28 (** Emit a document to an emitter 27 29 28 - @param resolve_aliases Whether to resolve aliases before emission (default true) *) 30 + @param resolve_aliases 31 + Whether to resolve aliases before emission (default true) *) 29 32 30 33 (** {1 Buffer-based API} *) 31 34 32 35 val value_to_buffer : 33 - ?config:Emitter.config -> 34 - ?buffer:Buffer.t -> 35 - Value.t -> Buffer.t 36 + ?config:Emitter.config -> ?buffer:Buffer.t -> Value.t -> Buffer.t 36 37 (** Serialize a Value to a buffer 37 38 38 39 @param config Emitter configuration (default: {!Emitter.default_config}) 39 - @param buffer Optional buffer to append to; creates new one if not provided *) 40 + @param buffer Optional buffer to append to; creates new one if not provided 41 + *) 40 42 41 43 val yaml_to_buffer : 42 - ?config:Emitter.config -> 43 - ?buffer:Buffer.t -> 44 - Yaml.t -> Buffer.t 44 + ?config:Emitter.config -> ?buffer:Buffer.t -> Yaml.t -> Buffer.t 45 45 (** Serialize a Yaml.t to a buffer *) 46 46 47 47 val documents_to_buffer : 48 48 ?config:Emitter.config -> 49 49 ?resolve_aliases:bool -> 50 50 ?buffer:Buffer.t -> 51 - Document.t list -> Buffer.t 51 + Document.t list -> 52 + Buffer.t 52 53 (** Serialize documents to a buffer 53 54 54 - @param resolve_aliases Whether to resolve aliases before emission (default true) *) 55 + @param resolve_aliases 56 + Whether to resolve aliases before emission (default true) *) 55 57 56 58 (** {1 String-based API} *) 57 59 ··· 62 64 (** Serialize a Yaml.t to a string *) 63 65 64 66 val documents_to_string : 65 - ?config:Emitter.config -> 66 - ?resolve_aliases:bool -> 67 - Document.t list -> string 67 + ?config:Emitter.config -> ?resolve_aliases:bool -> Document.t list -> string 68 68 (** Serialize documents to a string *) 69 69 70 70 (** {1 Writer-based API} 71 71 72 - These functions write directly to a bytesrw [Bytes.Writer.t], 73 - enabling true streaming output without intermediate string allocation. *) 72 + These functions write directly to a bytesrw [Bytes.Writer.t], enabling true 73 + streaming output without intermediate string allocation. *) 74 74 75 75 val value_to_writer : 76 76 ?config:Emitter.config -> 77 77 ?eod:bool -> 78 - Bytesrw.Bytes.Writer.t -> Value.t -> unit 78 + Bytesrw.Bytes.Writer.t -> 79 + Value.t -> 80 + unit 79 81 (** Serialize a Value directly to a Bytes.Writer 80 82 81 - @param eod Whether to write end-of-data after serialization (default true) *) 83 + @param eod Whether to write end-of-data after serialization (default true) 84 + *) 82 85 83 86 val yaml_to_writer : 84 87 ?config:Emitter.config -> 85 88 ?eod:bool -> 86 - Bytesrw.Bytes.Writer.t -> Yaml.t -> unit 89 + Bytesrw.Bytes.Writer.t -> 90 + Yaml.t -> 91 + unit 87 92 (** Serialize a Yaml.t directly to a Bytes.Writer *) 88 93 89 94 val documents_to_writer : 90 95 ?config:Emitter.config -> 91 96 ?resolve_aliases:bool -> 92 97 ?eod:bool -> 93 - Bytesrw.Bytes.Writer.t -> Document.t list -> unit 98 + Bytesrw.Bytes.Writer.t -> 99 + Document.t list -> 100 + unit 94 101 (** Serialize documents directly to a Bytes.Writer *) 95 102 96 103 (** {1 Function-based API} 97 104 98 - These functions accept an emit function [Event.t -> unit] instead of 99 - an {!Emitter.t}, allowing them to work with any event sink. *) 105 + These functions accept an emit function [Event.t -> unit] instead of an 106 + {!Emitter.t}, allowing them to work with any event sink. *) 100 107 101 108 val emit_yaml_node_fn : emitter:(Event.t -> unit) -> Yaml.t -> unit 102 109 (** Emit a YAML node using an emitter function *) 103 110 104 111 val emit_yaml_fn : 105 - emitter:(Event.t -> unit) -> 106 - config:Emitter.config -> 107 - Yaml.t -> unit 112 + emitter:(Event.t -> unit) -> config:Emitter.config -> Yaml.t -> unit 108 113 (** Emit a complete YAML stream using an emitter function *) 109 114 110 115 val emit_value_node_fn : 111 - emitter:(Event.t -> unit) -> 112 - config:Emitter.config -> 113 - Value.t -> unit 116 + emitter:(Event.t -> unit) -> config:Emitter.config -> Value.t -> unit 114 117 (** Emit a Value node using an emitter function *) 115 118 116 119 val emit_value_fn : 117 - emitter:(Event.t -> unit) -> 118 - config:Emitter.config -> 119 - Value.t -> unit 120 + emitter:(Event.t -> unit) -> config:Emitter.config -> Value.t -> unit 120 121 (** Emit a complete Value stream using an emitter function *) 121 122 122 123 val emit_document_fn : 123 - ?resolve_aliases:bool -> 124 - emitter:(Event.t -> unit) -> 125 - Document.t -> unit 124 + ?resolve_aliases:bool -> emitter:(Event.t -> unit) -> Document.t -> unit 126 125 (** Emit a document using an emitter function *) 127 126 128 127 val emit_documents : 129 128 emitter:(Event.t -> unit) -> 130 129 config:Emitter.config -> 131 130 ?resolve_aliases:bool -> 132 - Document.t list -> unit 131 + Document.t list -> 132 + unit 133 133 (** Emit multiple documents using an emitter function *)
+1 -4
lib/span.mli
··· 5 5 6 6 (** Source spans representing ranges in input *) 7 7 8 - type t = { 9 - start : Position.t; 10 - stop : Position.t; 11 - } 8 + type t = { start : Position.t; stop : Position.t } 12 9 13 10 val make : start:Position.t -> stop:Position.t -> t 14 11 (** Create a span from start and stop positions *)
+12 -15
lib/token.mli
··· 10 10 | Stream_end 11 11 | Version_directive of { major : int; minor : int } 12 12 | Tag_directive of { handle : string; prefix : string } 13 - | Document_start (** --- *) 14 - | Document_end (** ... *) 13 + | Document_start (** --- *) 14 + | Document_end (** ... *) 15 15 | Block_sequence_start 16 16 | Block_mapping_start 17 - | Block_entry (** [-] *) 18 - | Block_end (** implicit, from dedent *) 17 + | Block_entry (** [-] *) 18 + | Block_end (** implicit, from dedent *) 19 19 | Flow_sequence_start (** \[ *) 20 - | Flow_sequence_end (** \] *) 21 - | Flow_mapping_start (** \{ *) 22 - | Flow_mapping_end (** \} *) 23 - | Flow_entry (** , *) 24 - | Key (** ? or implicit key *) 25 - | Value (** : *) 20 + | Flow_sequence_end (** \] *) 21 + | Flow_mapping_start (** \{ *) 22 + | Flow_mapping_end (** \} *) 23 + | Flow_entry (** , *) 24 + | Key (** ? or implicit key *) 25 + | Value (** : *) 26 26 | Anchor of string (** &name *) 27 - | Alias of string (** *name *) 27 + | Alias of string (** *name *) 28 28 | Tag of { handle : string; suffix : string } 29 29 | Scalar of { style : Scalar_style.t; value : string } 30 30 31 - type spanned = { 32 - token : t; 33 - span : Span.t; 34 - } 31 + type spanned = { token : t; span : Span.t } 35 32 36 33 val pp_token : Format.formatter -> t -> unit 37 34 (** Pretty-print a token *)
+3 -4
lib/value.mli
··· 5 5 6 6 (** JSON-compatible YAML value representation *) 7 7 8 - type t = [ 9 - | `Null 8 + type t = 9 + [ `Null 10 10 | `Bool of bool 11 11 | `Float of float 12 12 | `String of string 13 13 | `A of t list 14 - | `O of (string * t) list 15 - ] 14 + | `O of (string * t) list ] 16 15 17 16 (** {2 Constructors} *) 18 17
+6 -2
lib/yaml.ml
··· 131 131 resolved_members) 132 132 in 133 133 (* Register anchor with resolved node *) 134 - Option.iter (fun name -> register_anchor name resolved) (Sequence.anchor seq); 134 + Option.iter 135 + (fun name -> register_anchor name resolved) 136 + (Sequence.anchor seq); 135 137 resolved 136 138 | `O map -> 137 139 (* Process key-value pairs in document order *) ··· 150 152 resolved_pairs) 151 153 in 152 154 (* Register anchor with resolved node *) 153 - Option.iter (fun name -> register_anchor name resolved) (Mapping.anchor map); 155 + Option.iter 156 + (fun name -> register_anchor name resolved) 157 + (Mapping.anchor map); 154 158 resolved 155 159 in 156 160 resolve ~depth:0 root
+7 -6
lib/yaml.mli
··· 5 5 6 6 (** Full YAML representation with anchors, tags, and aliases *) 7 7 8 - type t = [ 9 - | `Scalar of Scalar.t 8 + type t = 9 + [ `Scalar of Scalar.t 10 10 | `Alias of string 11 11 | `A of t Sequence.t 12 - | `O of (t, t) Mapping.t 13 - ] 12 + | `O of (t, t) Mapping.t ] 14 13 15 14 (** {2 Pretty Printing} *) 16 15 ··· 46 45 ?resolve_aliases_first:bool -> 47 46 ?max_nodes:int -> 48 47 ?max_depth:int -> 49 - t -> Value.t 48 + t -> 49 + Value.t 50 50 (** Convert to JSON-compatible Value. 51 51 52 - @param resolve_aliases_first Whether to resolve aliases before conversion (default true) 52 + @param resolve_aliases_first 53 + Whether to resolve aliases before conversion (default true) 53 54 @param max_nodes Maximum nodes during alias expansion 54 55 @param max_depth Maximum alias nesting depth 55 56 @raise Error.Yamlrw_error if unresolved aliases encountered *)
+3 -4
lib/yamlrw.mli
··· 34 34 35 35 {2 Related Libraries} 36 36 37 - {ul 38 - {- [Yamlrw_unix] - Unix file I/O integration} 39 - {- [Yamlrw_eio] - Eio async file and flow operations} 40 - {- [Yamlt] - YAML codec using Jsont type descriptions (higher-level)}} *) 37 + - [Yamlrw_unix] - Unix file I/O integration 38 + - [Yamlrw_eio] - Eio async file and flow operations 39 + - [Yamlt] - YAML codec using Jsont type descriptions (higher-level) *) 41 40 42 41 (** {2 Error Handling} *) 43 42
+60 -51
tests/test_yamlrw.ml
··· 377 377 ("resolve_aliases false", `Quick, test_resolve_aliases_false); 378 378 ] 379 379 380 - (** Bug fix regression tests 381 - These tests verify that issues fixed in ocaml-yaml don't occur in ocaml-yamlrw *) 380 + (** Bug fix regression tests These tests verify that issues fixed in ocaml-yaml 381 + don't occur in ocaml-yamlrw *) 382 382 383 383 (* Test for roundtrip of special string values (ocaml-yaml fix 225387d) 384 384 Strings like "true", "1.0", "null" etc. must be quoted on output so that ··· 431 431 let value = `Float 42.0 in 432 432 let result = to_string value in 433 433 (* Check the result doesn't contain "42." or "42.0" *) 434 - Alcotest.(check bool) "no trailing dot" 435 - true (not (String.length result >= 3 && 436 - result.[0] = '4' && result.[1] = '2' && result.[2] = '.')) 434 + Alcotest.(check bool) 435 + "no trailing dot" true 436 + (not 437 + (String.length result >= 3 438 + && result.[0] = '4' 439 + && result.[1] = '2' 440 + && result.[2] = '.')) 437 441 438 442 let test_emit_negative_integer_float () = 439 443 let value = `Float (-17.0) in ··· 445 449 let test_parse_special_floats () = 446 450 let inf_result = of_string ".inf" in 447 451 (match inf_result with 448 - | `Float f when Float.is_infinite f && f > 0.0 -> () 449 - | _ -> Alcotest.fail "expected positive infinity"); 452 + | `Float f when Float.is_infinite f && f > 0.0 -> () 453 + | _ -> Alcotest.fail "expected positive infinity"); 450 454 let neg_inf_result = of_string "-.inf" in 451 455 (match neg_inf_result with 452 - | `Float f when Float.is_infinite f && f < 0.0 -> () 453 - | _ -> Alcotest.fail "expected negative infinity"); 456 + | `Float f when Float.is_infinite f && f < 0.0 -> () 457 + | _ -> Alcotest.fail "expected negative infinity"); 454 458 let nan_result = of_string ".nan" in 455 - (match nan_result with 456 - | `Float f when Float.is_nan f -> () 457 - | _ -> Alcotest.fail "expected NaN") 459 + match nan_result with 460 + | `Float f when Float.is_nan f -> () 461 + | _ -> Alcotest.fail "expected NaN" 458 462 459 463 (* Test that bare "inf", "nan", "infinity" are NOT parsed as floats 460 464 (ocaml-yaml issue - OCaml's Float.of_string accepts these but YAML doesn't) *) 461 465 let test_bare_inf_nan_are_strings () = 462 466 let inf_result = of_string "inf" in 463 467 (match inf_result with 464 - | `String "inf" -> () 465 - | `Float _ -> Alcotest.fail "'inf' should be string, not float" 466 - | _ -> Alcotest.fail "expected string 'inf'"); 468 + | `String "inf" -> () 469 + | `Float _ -> Alcotest.fail "'inf' should be string, not float" 470 + | _ -> Alcotest.fail "expected string 'inf'"); 467 471 let nan_result = of_string "nan" in 468 472 (match nan_result with 469 - | `String "nan" -> () 470 - | `Float _ -> Alcotest.fail "'nan' should be string, not float" 471 - | _ -> Alcotest.fail "expected string 'nan'"); 473 + | `String "nan" -> () 474 + | `Float _ -> Alcotest.fail "'nan' should be string, not float" 475 + | _ -> Alcotest.fail "expected string 'nan'"); 472 476 let infinity_result = of_string "infinity" in 473 - (match infinity_result with 474 - | `String "infinity" -> () 475 - | `Float _ -> Alcotest.fail "'infinity' should be string, not float" 476 - | _ -> Alcotest.fail "expected string 'infinity'") 477 + match infinity_result with 478 + | `String "infinity" -> () 479 + | `Float _ -> Alcotest.fail "'infinity' should be string, not float" 480 + | _ -> Alcotest.fail "expected string 'infinity'" 477 481 478 482 (* Test for quoted scalar preservation *) 479 483 let test_quoted_scalar_preserved () = 480 484 (* When a scalar is quoted, it should be preserved as a string even if 481 485 it looks like a number/boolean *) 482 - check_value "double-quoted true is string" 483 - (`String "true") (of_string {|"true"|}); 484 - check_value "single-quoted 42 is string" 485 - (`String "42") (of_string "'42'"); 486 - check_value "double-quoted null is string" 487 - (`String "null") (of_string {|"null"|}) 486 + check_value "double-quoted true is string" (`String "true") 487 + (of_string {|"true"|}); 488 + check_value "single-quoted 42 is string" (`String "42") (of_string "'42'"); 489 + check_value "double-quoted null is string" (`String "null") 490 + (of_string {|"null"|}) 488 491 489 492 (* Test complex roundtrip with mixed types *) 490 493 let test_complex_roundtrip () = 491 - let original = `O [ 492 - ("string_true", `String "true"); 493 - ("bool_true", `Bool true); 494 - ("string_42", `String "42"); 495 - ("int_42", `Float 42.0); 496 - ("string_null", `String "null"); 497 - ("actual_null", `Null); 498 - ] in 494 + let original = 495 + `O 496 + [ 497 + ("string_true", `String "true"); 498 + ("bool_true", `Bool true); 499 + ("string_42", `String "42"); 500 + ("int_42", `Float 42.0); 501 + ("string_null", `String "null"); 502 + ("actual_null", `Null); 503 + ] 504 + in 499 505 let emitted = to_string original in 500 506 let parsed = of_string emitted in 501 507 check_value "complex roundtrip preserves types" original parsed 502 508 503 - let bugfix_regression_tests = [ 504 - "roundtrip string 'true'", `Quick, test_roundtrip_string_true; 505 - "roundtrip string 'false'", `Quick, test_roundtrip_string_false; 506 - "roundtrip string 'null'", `Quick, test_roundtrip_string_null; 507 - "roundtrip string '1.0'", `Quick, test_roundtrip_string_number; 508 - "roundtrip string '42'", `Quick, test_roundtrip_string_integer; 509 - "roundtrip string 'yes'", `Quick, test_roundtrip_string_yes; 510 - "roundtrip string 'no'", `Quick, test_roundtrip_string_no; 511 - "emit integer float without decimal", `Quick, test_emit_integer_float; 512 - "emit negative integer float", `Quick, test_emit_negative_integer_float; 513 - "parse special floats (.inf, -.inf, .nan)", `Quick, test_parse_special_floats; 514 - "bare inf/nan/infinity are strings", `Quick, test_bare_inf_nan_are_strings; 515 - "quoted scalars preserved as strings", `Quick, test_quoted_scalar_preserved; 516 - "complex roundtrip preserves types", `Quick, test_complex_roundtrip; 517 - ] 509 + let bugfix_regression_tests = 510 + [ 511 + ("roundtrip string 'true'", `Quick, test_roundtrip_string_true); 512 + ("roundtrip string 'false'", `Quick, test_roundtrip_string_false); 513 + ("roundtrip string 'null'", `Quick, test_roundtrip_string_null); 514 + ("roundtrip string '1.0'", `Quick, test_roundtrip_string_number); 515 + ("roundtrip string '42'", `Quick, test_roundtrip_string_integer); 516 + ("roundtrip string 'yes'", `Quick, test_roundtrip_string_yes); 517 + ("roundtrip string 'no'", `Quick, test_roundtrip_string_no); 518 + ("emit integer float without decimal", `Quick, test_emit_integer_float); 519 + ("emit negative integer float", `Quick, test_emit_negative_integer_float); 520 + ( "parse special floats (.inf, -.inf, .nan)", 521 + `Quick, 522 + test_parse_special_floats ); 523 + ("bare inf/nan/infinity are strings", `Quick, test_bare_inf_nan_are_strings); 524 + ("quoted scalars preserved as strings", `Quick, test_quoted_scalar_preserved); 525 + ("complex roundtrip preserves types", `Quick, test_complex_roundtrip); 526 + ] 518 527 519 528 (** Run all tests *) 520 529
+1 -1
yamlrw-eio.opam
··· 9 9 homepage: "https://tangled.org/@anil.recoil.org/ocaml-yamlrw" 10 10 bug-reports: "https://tangled.org/@anil.recoil.org/ocaml-yamlrw/issues" 11 11 depends: [ 12 - "dune" {>= "3.18"} 12 + "dune" {>= "3.21"} 13 13 "ocaml" {>= "5.0.0"} 14 14 "yamlrw" 15 15 "bytesrw-eio"
+1 -1
yamlrw-unix.opam
··· 9 9 homepage: "https://tangled.org/@anil.recoil.org/ocaml-yamlrw" 10 10 bug-reports: "https://tangled.org/@anil.recoil.org/ocaml-yamlrw/issues" 11 11 depends: [ 12 - "dune" {>= "3.18"} 12 + "dune" {>= "3.21"} 13 13 "ocaml" {>= "4.14.0"} 14 14 "yamlrw" 15 15 "bytesrw"
+1 -1
yamlrw.opam
··· 9 9 homepage: "https://tangled.org/@anil.recoil.org/ocaml-yamlrw" 10 10 bug-reports: "https://tangled.org/@anil.recoil.org/ocaml-yamlrw/issues" 11 11 depends: [ 12 - "dune" {>= "3.18"} 12 + "dune" {>= "3.21"} 13 13 "ocaml" {>= "4.14.0"} 14 14 "bytesrw" 15 15 "cmdliner"