Persistent store with Git semantics: lazy reads, delayed writes, content-addressing
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

irmin/toml: scalars are bare value literals

Instead of wrapping scalars in a sentinel "_" -keyed table, encode them
as their bare TOML lexical form (the RHS of a k = v assignment). A
top-level TOML document remains a table or array for composite
Named / Indexed children; scalar leaves are distinct bytes like "42",
"true", or a quoted string. Stepping into a scalar yields no further
children and there is no sentinel leaking into the structure.

+89 -32
+32 -14
lib/toml/irmin_toml.ml
··· 1 - (** TOML codec for string blocks. *) 1 + (** TOML codec for string blocks. 2 + 3 + A top-level TOML document must be a table or array. We keep that invariant 4 + for composite {!S.Named} / {!S.Indexed} children, and represent scalar 5 + children as their bare TOML lexical form (["42"], [{|"hi"|}], ["true"]) — 6 + the RHS of a [k = v] assignment, not a standalone document. 7 + 8 + This means scalars are genuine leaves: stepping into one yields no further 9 + children, there is no sentinel key leaking into the structure, and [Inline] 10 + bytes are either a valid TOML document (tables/arrays) or a bare value 11 + literal (scalars). *) 2 12 3 13 module S = Irmin.SHA256 4 14 5 15 let meta = Toml.Meta.none 6 16 7 - (* Round-trip a single TOML value through its string form, keyed "_" at the 8 - top so we can re-parse it as a stand-alone document. *) 17 + (* Encode a scalar value to its bare lexical TOML form by round-tripping 18 + it through a throwaway wrapper and slicing out the RHS. [Toml.to_string] 19 + requires a Table at top level, so we can't feed a scalar directly; the 20 + wrapper is internal and never escapes to callers. *) 21 + let lex_of_scalar (v : Toml.Value.t) : string = 22 + let wrapped = Toml.Value.Table ([ (("__v", meta), v) ], meta) in 23 + let s = Toml.to_string Toml.Value.codec wrapped in 24 + (* [__v = 42\n] -> [42]. Be tolerant of trailing newline/whitespace. *) 25 + match String.index_opt s '=' with 26 + | None -> String.trim s 27 + | Some i -> String.trim (String.sub s (i + 1) (String.length s - i - 1)) 28 + 9 29 let enc (v : Toml.Value.t) : string = 10 30 match v with 11 - | Toml.Value.Table _ -> Toml.to_string Toml.Value.codec v 12 - | _ -> 13 - (* Scalars and arrays need to be wrapped; use a dummy key. *) 14 - let wrapped = Toml.Value.Table ([ (("_", meta), v) ], meta) in 15 - Toml.to_string Toml.Value.codec wrapped 31 + | Toml.Value.Table _ | Toml.Value.Array _ -> Toml.to_string Toml.Value.codec v 32 + | _ -> lex_of_scalar v 16 33 17 - (* Scalars round-trip through an [enc]-wrapped TOML document whose single 18 - member is keyed with the sentinel "_" (see [enc]). Only that exact shape 19 - is unwrapped here; tables with a real single key must stay as tables so 20 - [parse] can expose them as [Named] children. *) 34 + (* Decode bytes that are either a full TOML document (table or array) or 35 + a bare scalar lexical form. Tries the document parse first; on failure 36 + retries with a [__v = BYTES] wrapper to cover the scalar case. *) 21 37 let dec (s : string) : Toml.Value.t = 22 38 match Toml.of_string Toml.Value.codec s with 23 - | Ok (Toml.Value.Table ([ (("_", _), v) ], _)) -> v 24 39 | Ok v -> v 25 - | Error _ -> Toml.Value.Table ([], meta) 40 + | Error _ -> ( 41 + match Toml.of_string Toml.Value.codec ("__v = " ^ s) with 42 + | Ok (Toml.Value.Table ([ ((_, _), v) ], _)) -> v 43 + | _ -> Toml.Value.Table ([], meta)) 26 44 27 45 let parse : S.dec = 28 46 fun block ->
+3 -7
test/text/test_irmin_text.ml
··· 17 17 (Some expected_name) (Irmin.SHA256.mime codec) 18 18 19 19 let plain_named_mime () = name_and_mime Irmin_text.plain "text/plain" 20 - 21 - let markdown_named_mime () = 22 - name_and_mime Irmin_text.markdown "text/markdown" 20 + let markdown_named_mime () = name_and_mime Irmin_text.markdown "text/markdown" 23 21 24 22 let suite = 25 23 ( "irmin_text", 26 24 [ 27 - Alcotest.test_case "plain name and MIME" `Quick 28 - plain_named_mime; 29 - Alcotest.test_case "markdown name and MIME" `Quick 30 - markdown_named_mime; 25 + Alcotest.test_case "plain name and MIME" `Quick plain_named_mime; 26 + Alcotest.test_case "markdown name and MIME" `Quick markdown_named_mime; 31 27 ] )
+53 -9
test/toml/test_irmin_toml.ml
··· 3 3 Spec: 4 4 - A top-level TOML table yields [Named] children, one per key. 5 5 - A top-level TOML array yields [Indexed] children. 6 - - Empty / invalid / scalar input yields [Named []]. 7 - - [parse (serialize c) = c] on the keys of [c] (value bytes may reformat, 8 - but the logical member set is preserved). *) 6 + - Empty / invalid / scalar-only input yields [Named []]. 7 + - Scalar children are stored as their bare lexical form (the RHS of 8 + [k = v]); no sentinel key leaks into the structure. Stepping back into a 9 + scalar child yields [Named []] — scalars are leaves. 10 + - Table / array children are stored as full TOML documents. 11 + - [parse (serialize c) = c] on member names (tables) and values (scalars). 12 + *) 9 13 10 14 let parse_table_exact_names () = 11 15 match Irmin_toml.parse {| ··· 17 21 Alcotest.(check (list string)) "names" [ "age"; "name" ] names 18 22 | _ -> Alcotest.fail "parse_table_exact_names" 19 23 20 - let parse_scalars_stored_inline () = 21 - match Irmin_toml.parse {|k = 1|} with 22 - | Irmin.SHA256.Named [ ("k", `Inline _) ] -> () 23 - | _ -> Alcotest.fail "parse_scalars_stored_inline" 24 + let scalar_int_is_bare_lexical_form () = 25 + (* [age = 30] → Named [("age", Inline "30")]. No sentinel key. *) 26 + match Irmin_toml.parse "age = 30\n" with 27 + | Irmin.SHA256.Named [ ("age", `Inline bytes) ] -> 28 + Alcotest.(check string) "bare scalar" "30" bytes 29 + | _ -> Alcotest.fail "scalar_int_is_bare_lexical_form" 30 + 31 + let scalar_string_is_quoted_lexical () = 32 + match Irmin_toml.parse {|name = "alice"|} with 33 + | Irmin.SHA256.Named [ ("name", `Inline bytes) ] -> 34 + Alcotest.(check string) "quoted" {|"alice"|} bytes 35 + | _ -> Alcotest.fail "scalar_string_is_quoted_lexical" 36 + 37 + let scalar_steps_to_leaf () = 38 + (* Re-parsing a scalar child's Inline bytes as a TOML document yields 39 + no children — scalars are leaves. *) 40 + match Irmin_toml.parse "n = 42\n" with 41 + | Irmin.SHA256.Named [ (_, `Inline bytes) ] -> ( 42 + match Irmin_toml.parse bytes with 43 + | Irmin.SHA256.Named [] -> () 44 + | _ -> Alcotest.fail "scalar bytes re-parsed as non-empty structure") 45 + | _ -> Alcotest.fail "scalar_steps_to_leaf" 24 46 25 47 let parse_empty_is_empty_named () = 26 48 Alcotest.(check bool) ··· 50 72 (List.map fst b |> List.sort String.compare) 51 73 | _ -> Alcotest.fail "roundtrip_preserves_names" 52 74 75 + let roundtrip_scalar_values () = 76 + (* Scalar Inline bytes survive serialize . parse. *) 77 + let original = 78 + Irmin.SHA256.Named [ ("k", `Inline "42"); ("s", `Inline {|"hi"|}) ] 79 + in 80 + match Irmin_toml.parse (Irmin_toml.serialize original) with 81 + | Irmin.SHA256.Named kids -> 82 + let find name = 83 + match List.assoc_opt name kids with 84 + | Some (`Inline s) -> s 85 + | _ -> Alcotest.failf "%s missing" name 86 + in 87 + Alcotest.(check string) "k preserved" "42" (find "k"); 88 + Alcotest.(check string) "s preserved" {|"hi"|} (find "s") 89 + | _ -> Alcotest.fail "roundtrip_scalar_values" 90 + 53 91 let schema_name_is_application_toml () = 54 92 Alcotest.(check string) 55 93 "schema name" "application/toml" ··· 60 98 [ 61 99 Alcotest.test_case "parse table -> exact names" `Quick 62 100 parse_table_exact_names; 63 - Alcotest.test_case "parse stores scalar values inline" `Quick 64 - parse_scalars_stored_inline; 101 + Alcotest.test_case "int scalar is bare lexical form" `Quick 102 + scalar_int_is_bare_lexical_form; 103 + Alcotest.test_case "string scalar is quoted lexical form" `Quick 104 + scalar_string_is_quoted_lexical; 105 + Alcotest.test_case "stepping into scalar yields leaf" `Quick 106 + scalar_steps_to_leaf; 65 107 Alcotest.test_case "parse empty -> Named []" `Quick 66 108 parse_empty_is_empty_named; 67 109 Alcotest.test_case "parse invalid -> Named []" `Quick 68 110 parse_invalid_is_empty_named; 69 111 Alcotest.test_case "roundtrip preserves member names" `Quick 70 112 roundtrip_preserves_names; 113 + Alcotest.test_case "roundtrip preserves scalar values" `Quick 114 + roundtrip_scalar_values; 71 115 Alcotest.test_case "schema name" `Quick schema_name_is_application_toml; 72 116 ] )
+1 -2
test/ui/test_drop_zone.ml
··· 54 54 form_post_action_enctype; 55 55 Alcotest.test_case "hidden dir input carries target_dir" `Quick 56 56 hidden_dir_carries_target; 57 - Alcotest.test_case "file input auto-submits" `Quick 58 - file_input_auto_submit; 57 + Alcotest.test_case "file input auto-submits" `Quick file_input_auto_submit; 59 58 ] )