a database layer insipred by caqti and ecto
at main 5.2 kB view raw
1open Repodb 2 3module YojsonJson : Embedded.JSON with type t = Yojson.Safe.t = struct 4 type t = Yojson.Safe.t 5 6 let null = `Null 7 let to_string json = Yojson.Safe.to_string json 8 9 let of_string s = 10 try Ok (Yojson.Safe.from_string s) with Yojson.Json_error msg -> Error msg 11 12 let get_field json key = 13 match json with `Assoc fields -> List.assoc_opt key fields | _ -> None 14 15 let get_string = function `String s -> Some s | _ -> None 16 let get_int = function `Int i -> Some i | _ -> None 17 18 let get_float = function 19 | `Float f -> Some f 20 | `Int i -> Some (float_of_int i) 21 | _ -> None 22 23 let get_bool = function `Bool b -> Some b | _ -> None 24 let get_list = function `List l -> Some l | _ -> None 25 let is_null = function `Null -> true | _ -> false 26 let make_object fields = `Assoc fields 27 let make_string s = `String s 28 let make_int i = `Int i 29 let make_float f = `Float f 30 let make_bool b = `Bool b 31 let make_list l = `List l 32end 33 34module E = Embedded.Make (YojsonJson) 35 36type address = { street : string; city : string; zip : string } 37 38let address_schema = 39 E.schema ~name:"address" 40 ~decode:(fun json -> 41 match json with 42 | `Assoc fields -> 43 let get_string key = 44 match List.assoc_opt key fields with 45 | Some (`String s) -> s 46 | _ -> "" 47 in 48 Ok 49 { 50 street = get_string "street"; 51 city = get_string "city"; 52 zip = get_string "zip"; 53 } 54 | _ -> Error "Expected object") 55 ~encode:(fun addr -> 56 `Assoc 57 [ 58 ("street", `String addr.street); 59 ("city", `String addr.city); 60 ("zip", `String addr.zip); 61 ]) 62 ~fields:[ "street"; "city"; "zip" ] 63 () 64 65let test_from_json () = 66 let json = 67 `Assoc 68 [ 69 ("street", `String "123 Main St"); 70 ("city", `String "Springfield"); 71 ("zip", `String "12345"); 72 ] 73 in 74 match E.from_json address_schema json with 75 | Ok embedded -> 76 let data = E.data embedded in 77 Alcotest.(check string) "street" "123 Main St" data.street; 78 Alcotest.(check string) "city" "Springfield" data.city 79 | Error msg -> Alcotest.fail msg 80 81let test_to_json () = 82 let addr = { street = "456 Oak Ave"; city = "Shelbyville"; zip = "67890" } in 83 let embedded = 84 { E.data = addr; fields = [ "street"; "city"; "zip" ]; source = E.Virtual } 85 in 86 let json = E.to_json address_schema embedded in 87 match json with 88 | `Assoc fields -> ( 89 match List.assoc_opt "street" fields with 90 | Some (`String s) -> 91 Alcotest.(check string) "street in json" "456 Oak Ave" s 92 | _ -> Alcotest.fail "missing street") 93 | _ -> Alcotest.fail "expected object" 94 95let test_from_json_string () = 96 let json_str = {|{"street":"789 Elm Blvd","city":"Capital","zip":"11111"}|} in 97 match E.from_json_string address_schema json_str with 98 | Ok embedded -> 99 let data = E.data embedded in 100 Alcotest.(check string) "parsed street" "789 Elm Blvd" data.street 101 | Error msg -> Alcotest.fail msg 102 103let test_to_json_string () = 104 let addr = { street = "Test St"; city = "Test City"; zip = "00000" } in 105 let embedded = 106 { E.data = addr; fields = [ "street"; "city"; "zip" ]; source = E.Virtual } 107 in 108 let json_str = E.to_json_string address_schema embedded in 109 Alcotest.(check bool) "contains street" true (String.length json_str > 0) 110 111let test_embeds_one_from_json () = 112 let json = 113 `Assoc 114 [ 115 ("street", `String "Single"); 116 ("city", `String "Town"); 117 ("zip", `String "1"); 118 ] 119 in 120 match E.embeds_one_from_json address_schema json with 121 | Ok (Some embedded) -> 122 Alcotest.(check string) 123 "embeds_one street" "Single" (E.data embedded).street 124 | Ok None -> Alcotest.fail "expected Some" 125 | Error msg -> Alcotest.fail msg 126 127let test_embeds_one_from_json_null () = 128 match E.embeds_one_from_json address_schema `Null with 129 | Ok None -> Alcotest.(check pass) "null is None" () () 130 | Ok (Some _) -> Alcotest.fail "expected None" 131 | Error msg -> Alcotest.fail msg 132 133let test_embeds_many_from_json () = 134 let json = 135 `List 136 [ 137 `Assoc 138 [ 139 ("street", `String "First"); 140 ("city", `String "A"); 141 ("zip", `String "1"); 142 ]; 143 `Assoc 144 [ 145 ("street", `String "Second"); 146 ("city", `String "B"); 147 ("zip", `String "2"); 148 ]; 149 ] 150 in 151 match E.embeds_many_from_json address_schema json with 152 | Ok embeddeds -> 153 Alcotest.(check int) "two items" 2 (List.length embeddeds); 154 Alcotest.(check string) 155 "first street" "First" (E.data (List.hd embeddeds)).street 156 | Error msg -> Alcotest.fail msg 157 158let tests = 159 [ 160 ("from_json", `Quick, test_from_json); 161 ("to_json", `Quick, test_to_json); 162 ("from_json_string", `Quick, test_from_json_string); 163 ("to_json_string", `Quick, test_to_json_string); 164 ("embeds_one from_json", `Quick, test_embeds_one_from_json); 165 ("embeds_one from_json null", `Quick, test_embeds_one_from_json_null); 166 ("embeds_many from_json", `Quick, test_embeds_many_from_json); 167 ]