Created
February 3, 2025 15:07
-
-
Save davesnx/78d8130b6f9cc01cd99a1afb20ba7719 to your computer and use it in GitHub Desktop.
a few functions to replace of_yojson dependency
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(* | |
TODO: If we want to remove the dependency on of_json, we need to implement the json decoder manually. | |
TODO: Maybe use a custom deriving called "rsc" or similar where it handles the JSON/Promise/React.element. *) | |
let rec make_of_json ~loc (type_ : core_type) value = | |
match type_.ptyp_desc with | |
| Ptyp_constr ({ txt = Lident "int"; _ }, _) -> value | |
| Ptyp_constr ({ txt = Lident "string"; _ }, _) -> value | |
| Ptyp_constr ({ txt = Lident "bool"; _ }, _) -> value | |
| Ptyp_constr ({ txt = Lident "float"; _ }, _) -> value | |
| Ptyp_constr ({ txt = Lident "unit"; _ }, _) -> value | |
| Ptyp_constr ({ txt = Lident "list"; _ }, list) -> | |
let inner = List.hd list in | |
let mapped = [%expr Stdlib.List.map (fun x -> [%e make_of_json ~loc inner [%expr x]]) [%e value]] in | |
pexp_variant ~loc:value.pexp_loc "List" (Some mapped) | |
| Ptyp_constr ({ txt = Lident "array"; _ }, array) -> | |
let inner = List.hd array in | |
let mapped = [%expr Stdlib.Array.map (fun x -> [%e make_of_json ~loc inner [%expr x]]) [%e value]] in | |
let as_list = [%expr Stdlib.Array.to_list [%e mapped]] in | |
pexp_variant ~loc:value.pexp_loc "List" (Some as_list) | |
| Ptyp_constr ({ txt = Lident "option"; _ }, option) -> | |
let inner = List.hd option in | |
let matched = [%expr match [%e value] with None -> `Null | Some x -> [%e make_of_json ~loc inner [%expr x]]] in | |
matched | |
(* TODO: Add json/yojson *) | |
(* | [%type: Yojson.Basic.t] -> pexp_variant ~loc:value.pexp_loc "Yojson" (Some value) *) | |
| Ptyp_constr ({ txt = lident; _ }, _) -> | |
let rec make_of_json_fn lident = | |
match lident with | |
| Lident name when name = "t" -> Lident "of_json" | |
| Lident name -> Lident (Printf.sprintf "%s_of_json" name) | |
| Ldot (modulePath, name) when name = "t" -> Ldot (modulePath, "of_json") | |
| Ldot (modulePath, name) -> Ldot (modulePath, Printf.sprintf "%s_of_json" name) | |
| Lapply (apply, longident) -> Lapply (apply, make_of_json_fn longident) | |
in | |
pexp_apply ~loc:value.pexp_loc (pexp_ident ~loc { txt = make_of_json_fn lident; loc }) [ (Nolabel, value) ] | |
| Ptyp_tuple tuple -> | |
let item_name index = "x" ^ Int.to_string index in | |
let loc = value.pexp_loc in | |
let descructuring = | |
ppat_tuple ~loc (List.mapi ~f:(fun index _ -> ppat_var ~loc { txt = item_name index; loc }) tuple) | |
in | |
let list = | |
List.mapi | |
~f:(fun index t -> | |
let identifier = pexp_ident ~loc { txt = Lident (item_name index); loc } in | |
make_of_json ~loc [%type: [%t t]] identifier) | |
tuple | |
in | |
pexp_let ~loc Nonrecursive | |
[ value_binding ~loc ~pat:descructuring ~expr:value ] | |
[%expr `List [%e pexp_list ~loc list]] | |
| Ptyp_var name -> | |
let msg = Printf.sprintf "server-reason-react: unsupported type: '%s" name in | |
[%expr [%ocaml.error [%e estring ~loc msg]]] | |
| Ptyp_arrow _ -> | |
[%expr | |
[%ocaml.error | |
"server-reason-react: callbacks are not supported in client components. Functions can't be serialized to the \ | |
client."]] | |
(* | Ptyp_object _ -> error_cannot_create_json_encoder ~loc ~type_name:"objects" | |
| Ptyp_class _ -> error_cannot_create_json_encoder ~loc ~type_name:"classes" | |
| Ptyp_variant _ -> error_cannot_create_json_encoder ~loc ~type_name:"polyvariants" | |
| Ptyp_alias _ -> error_not_supported ~loc ~type_name:"aliases" | |
| Ptyp_extension _ -> error_not_supported ~loc ~type_name:"extensions" | |
| Ptyp_package _ -> error_not_supported ~loc ~type_name:"modules" | |
| Ptyp_poly _ -> error_not_supported ~loc ~type_name:"polymorphic types" | |
| Ptyp_any -> error_not_supported ~loc ~type_name:"'_' annotations" *) | |
| _ -> [%expr [%ocaml.error "server-reason-react: unsupported type"]] | |
(* let error_cannot_create_json_encoder ~loc ~type_name = | |
let msg = | |
Printf.sprintf | |
"server-reason-react: inline types such as %s, need to be a type definition with a json encoder. If the type is \ | |
named 't' the encoder should be named 't_to_json', if the type is named 'foo' the encoder should be named \ | |
'foo_to_json'." | |
type_name | |
in | |
[%expr [%ocaml.error [%e estring ~loc msg]]] | |
let error_not_supported ~loc ~type_name = | |
let msg = | |
Printf.sprintf | |
"server-reason-react: %s aren't supported in client components. Try using a type definition with a json encoder \ | |
but there's no guarantee that it will work. Open an issue if you need it." | |
type_name | |
in | |
[%expr [%ocaml.error [%e estring ~loc msg]]] *) | |
let rec make_to_yojson ~loc (type_ : core_type) value = | |
match type_.ptyp_desc with | |
| Ptyp_constr ({ txt = Lident "int"; _ }, _) -> pexp_variant ~loc:value.pexp_loc "Int" (Some value) | |
| Ptyp_constr ({ txt = Lident "string"; _ }, _) -> pexp_variant ~loc:value.pexp_loc "String" (Some value) | |
| Ptyp_constr ({ txt = Lident "bool"; _ }, _) -> pexp_variant ~loc:value.pexp_loc "Bool" (Some value) | |
| Ptyp_constr ({ txt = Lident "float"; _ }, _) -> pexp_variant ~loc:value.pexp_loc "Float" (Some value) | |
| Ptyp_constr ({ txt = Lident "list"; _ }, list) -> | |
let inner = List.hd list in | |
let mapped = [%expr Stdlib.List.map (fun x -> [%e make_to_yojson ~loc inner [%expr x]]) [%e value]] in | |
pexp_variant ~loc:value.pexp_loc "List" (Some mapped) | |
| Ptyp_constr ({ txt = Lident "array"; _ }, array) -> | |
let inner = List.hd array in | |
let mapped = [%expr Stdlib.Array.map (fun x -> [%e make_to_yojson ~loc inner [%expr x]]) [%e value]] in | |
let as_list = [%expr Stdlib.Array.to_list [%e mapped]] in | |
pexp_variant ~loc:value.pexp_loc "List" (Some as_list) | |
| Ptyp_constr ({ txt = Lident "option"; _ }, option) -> | |
let inner = List.hd option in | |
let matched = | |
[%expr match [%e value] with None -> `Null | Some x -> [%e make_to_yojson ~loc inner [%expr x]]] | |
in | |
matched | |
| Ptyp_constr ({ txt = Lident "unit"; _ }, _) -> pexp_variant ~loc:value.pexp_loc "Null" None | |
(* TODO: Add json/yojson *) | |
(* | [%type: Yojson.Basic.t] -> pexp_variant ~loc:value.pexp_loc "Yojson" (Some value) *) | |
| Ptyp_constr ({ txt = lident; _ }, _) -> | |
let rec make_to_json_fn lident = | |
match lident with | |
| Lident name when name = "t" -> Lident "to_json" | |
| Lident name -> Lident (Printf.sprintf "%s_to_json" name) | |
| Ldot (modulePath, name) when name = "t" -> Ldot (modulePath, "to_json") | |
| Ldot (modulePath, name) -> Ldot (modulePath, Printf.sprintf "%s_to_json" name) | |
| Lapply (apply, longident) -> Lapply (apply, make_to_json_fn longident) | |
in | |
pexp_apply ~loc:value.pexp_loc (pexp_ident ~loc { txt = make_to_json_fn lident; loc }) [ (Nolabel, value) ] | |
| Ptyp_tuple tuple -> | |
let item_name index = "x" ^ Int.to_string index in | |
let loc = value.pexp_loc in | |
let descructuring = | |
ppat_tuple ~loc (List.mapi ~f:(fun index _ -> ppat_var ~loc { txt = item_name index; loc }) tuple) | |
in | |
let list = | |
List.mapi | |
~f:(fun index t -> | |
let identifier = pexp_ident ~loc { txt = Lident (item_name index); loc } in | |
make_to_yojson ~loc [%type: [%t t]] identifier) | |
tuple | |
in | |
pexp_let ~loc Nonrecursive | |
[ value_binding ~loc ~pat:descructuring ~expr:value ] | |
[%expr `List [%e pexp_list ~loc list]] | |
| Ptyp_var name -> | |
let msg = Printf.sprintf "server-reason-react: unsupported type: '%s" name in | |
[%expr [%ocaml.error [%e estring ~loc msg]]] | |
| Ptyp_arrow _ -> | |
[%expr | |
[%ocaml.error | |
"server-reason-react: callbacks are not supported in client components. Functions can't be serialized to the \ | |
client."]] | |
| Ptyp_object _ -> error_cannot_create_json_encoder ~loc ~type_name:"objects" | |
| Ptyp_class _ -> error_cannot_create_json_encoder ~loc ~type_name:"classes" | |
| Ptyp_variant _ -> error_cannot_create_json_encoder ~loc ~type_name:"polyvariants" | |
| Ptyp_alias _ -> error_not_supported ~loc ~type_name:"aliases" | |
| Ptyp_extension _ -> error_not_supported ~loc ~type_name:"extensions" | |
| Ptyp_package _ -> error_not_supported ~loc ~type_name:"modules" | |
| Ptyp_poly _ -> error_not_supported ~loc ~type_name:"polymorphic types" | |
| Ptyp_any -> error_not_supported ~loc ~type_name:"'_' annotations" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment