Skip to content

Commit

Permalink
feat: add default to deriving
Browse files Browse the repository at this point in the history
  • Loading branch information
tjdevries authored and leostera committed Feb 19, 2024
1 parent e9c9386 commit 425ec2a
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 15 deletions.
16 changes: 13 additions & 3 deletions derive/attributes.ml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
open Ppxlib

type type_attributes = {
rename : string;
mode :
Expand Down Expand Up @@ -26,7 +28,7 @@ type variant_attributes = {

type field_attributes = {
name : string;
default : string option;
presence : [ `required | `optional | `with_default of Parsetree.expression ];
should_skip :
[ `skip_serializing_if of string
| `skip_deserializing_if of string
Expand All @@ -37,8 +39,13 @@ type field_attributes = {
let of_field_attributes lbl =
let open Ppxlib in
let name = ref lbl.pld_name.txt in
let default = ref None in
let should_skip = ref `never in
let presence =
ref
(match lbl.pld_type.ptyp_desc with
| Ptyp_constr ({ txt = Lident "option"; _ }, _) -> `optional
| _ -> `required)
in
let () =
match lbl.pld_attributes with
| [
Expand All @@ -63,9 +70,12 @@ let of_field_attributes lbl =
{ pexp_desc = Pexp_constant (Pconst_string (s, _, _)); _ } ) ->
name := s;
()
| { txt = Lident "default"; _ }, expr ->
presence := `with_default expr;
()
| _ -> ())
fields
| _ -> ()
in

(lbl, { name = !name; default = !default; should_skip = !should_skip })
(lbl, { name = !name; presence = !presence; should_skip = !should_skip })
27 changes: 21 additions & 6 deletions derive/de.ml
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,6 @@ module Record_deserializer = struct
let deserialize_with_unordered_fields ~ctxt labels final_expr =
let loc = loc ~ctxt in
let labels = List.rev labels in
(* [@serde { rename = "type" }] *)
let labels = List.map Attributes.of_field_attributes labels in

(* NOTE(@leostera): Generate the final assembling of the record value
Expand Down Expand Up @@ -198,11 +197,27 @@ module Record_deserializer = struct
Attributes.(attr.name)
field.pld_name.txt)
in
[%expr
let* [%p field_pat] =
Option.to_result ~none:(`Msg [%e missing_msg]) ![%e field_var]
in
[%e last]])
match Attributes.(attr.presence) with
| `required ->
[%expr
let* [%p field_pat] =
Option.to_result ~none:(`Msg [%e missing_msg]) ![%e field_var]
in
[%e last]]
| `optional ->
[%expr
let [%p field_pat] =
match ![%e field_var] with Some opt -> opt | None -> None
in
[%e last]]
| `with_default str ->
[%expr
let [%p field_pat] =
match ![%e field_var] with
| Some opt -> opt
| None -> [%e str]
in
[%e last]])
body labels
in

Expand Down
7 changes: 2 additions & 5 deletions derive/ppx.t/run.t
Original file line number Diff line number Diff line change
Expand Up @@ -289,11 +289,8 @@
"missing field \"updated_at\" (\"updated_at\")")
(!updated_at)
in
let* credits =
Option.to_result
~none:(`Msg "missing field \"credits\" (\"credits\")")
(!credits)
in
let credits =
match !credits with | Some opt -> opt | None -> None in
let* keywords =
Option.to_result
~none:(`Msg "missing field \"keywords\" (\"keywords\")")
Expand Down
32 changes: 31 additions & 1 deletion serde_json/serde_json_test.ml
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
open Serde
open Serde_json

let keyword fmt = Spices.(default |> fg (color "#00FF00") |> build) fmt
let error fmt = Spices.(default |> fg (color "#FF0000") |> build) fmt
Expand Down Expand Up @@ -488,4 +487,35 @@ let _serde_json_roundtrip_tests =

test "record_with_key_rename" pp_with_type_field serialize_with_type_field
deserialize_with_type_field { type_ = "hello" } {|{type="hello"}|};

()

type hello = { hello : string; count : int option }
[@@deriving serialize, deserialize]

let _serde_json_parse_test_no_key =
let str = {| {"hello":"world"} |} in
let parsed = Serde_json.of_string deserialize_hello str in
match parsed with
| Ok _ ->
Format.printf "serde_json.ser/de test %S %s\r\n%!" "parsed with no key"
(keyword "OK")
| Error _ ->
Format.printf "serde_json.ser/de test %S %s\r\n%!" "parsed with no key"
(error "Failed!");
assert false

type with_default = {
greeting : string;
count_with_default : int; [@serde { default = 5 }]
}
[@@deriving serialize, deserialize]

let _serde_json_parse_test_with_default =
let str = {| {"greeting":"yoyo"} |} in
let parsed = Serde_json.of_string deserialize_with_default str in
let parsed = Result.get_ok parsed in
assert (parsed.count_with_default = 5);
Format.printf "serde_json.ser/de test %S %s\r\n%!" "parsed with default"
(keyword "OK");
()

0 comments on commit 425ec2a

Please sign in to comment.