From ebe97257a6893940883361d2cf84cdc15e85b273 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=82=B5=E3=83=9D=E3=83=9F=E3=82=AF?= Date: Sat, 24 Feb 2024 22:49:54 +0900 Subject: [PATCH] feat(napi-derive): add use_nullable attribute (#1971) * feat(napi-derive): add use_nullable attribute Co-authored-by: naskya * chore(napi-derive): update tests Co-authored-by: naskya --------- Co-authored-by: naskya --- crates/backend/src/ast.rs | 1 + crates/backend/src/codegen/struct.rs | 38 ++++++++++---- crates/backend/src/typegen/struct.rs | 9 +++- crates/macro/src/parser/attrs.rs | 27 +++++----- crates/macro/src/parser/mod.rs | 2 + .../__snapshots__/typegen.spec.ts.md | 45 ++++++++++++++++ .../__snapshots__/typegen.spec.ts.snap | Bin 4444 -> 4577 bytes examples/napi/index.d.ts | 45 ++++++++++++++++ examples/napi/src/nullable.rs | 48 ++++++++++++++++++ 9 files changed, 190 insertions(+), 25 deletions(-) diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index 58a9a238..3954c634 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -88,6 +88,7 @@ pub struct NapiStruct { pub implement_iterator: bool, pub use_custom_finalize: bool, pub register_name: Ident, + pub use_nullable: bool, } #[derive(Debug, Clone, PartialEq, Eq)] diff --git a/crates/backend/src/codegen/struct.rs b/crates/backend/src/codegen/struct.rs index 93348776..a5bcb209 100644 --- a/crates/backend/src/codegen/struct.rs +++ b/crates/backend/src/codegen/struct.rs @@ -493,15 +493,24 @@ impl NapiStruct { let alias_ident = format_ident!("{}_", ident); field_destructions.push(quote! { #ident: #alias_ident }); if is_optional_field { - obj_field_setters.push(quote! { - if #alias_ident.is_some() { - obj.set(#field_js_name, #alias_ident)?; - } + obj_field_setters.push(match self.use_nullable { + false => quote! { + if #alias_ident.is_some() { + obj.set(#field_js_name, #alias_ident)?; + } + }, + true => quote! { + if let Some(#alias_ident) = #alias_ident { + obj.set(#field_js_name, #alias_ident)?; + } else { + obj.set(#field_js_name, napi::bindgen_prelude::Null)?; + } + }, }); } else { obj_field_setters.push(quote! { obj.set(#field_js_name, #alias_ident)?; }); } - if is_optional_field { + if is_optional_field && !self.use_nullable { obj_field_getters.push(quote! { let #alias_ident: #ty = obj.get(#field_js_name)?; }); } else { obj_field_getters.push(quote! { @@ -515,15 +524,24 @@ impl NapiStruct { syn::Member::Unnamed(i) => { field_destructions.push(quote! { arg #i }); if is_optional_field { - obj_field_setters.push(quote! { - if arg #1.is_some() { - obj.set(#field_js_name, arg #i)?; - } + obj_field_setters.push(match self.use_nullable { + false => quote! { + if arg #1.is_some() { + obj.set(#field_js_name, arg #i)?; + } + }, + true => quote! { + if let Some(arg #i) = arg #i { + obj.set(#field_js_name, arg #i)?; + } else { + obj.set(#field_js_name, napi::bindgen_prelude::Null)?; + } + }, }); } else { obj_field_setters.push(quote! { obj.set(#field_js_name, arg #1)?; }); } - if is_optional_field { + if is_optional_field && !self.use_nullable { obj_field_getters.push(quote! { let arg #i: #ty = obj.get(#field_js_name)?; }); } else { obj_field_getters.push(quote! { diff --git a/crates/backend/src/typegen/struct.rs b/crates/backend/src/typegen/struct.rs index b18b6ab2..b32f1e86 100644 --- a/crates/backend/src/typegen/struct.rs +++ b/crates/backend/src/typegen/struct.rs @@ -128,8 +128,13 @@ impl NapiStruct { let (arg, is_optional) = ty_to_ts_type(&f.ty, false, true, false); let arg = f.ts_type.as_ref().map(|ty| ty.to_string()).unwrap_or(arg); - let sep = if is_optional { "?" } else { "" }; - let arg = format!("{}{}: {}", &f.js_name, sep, arg); + let arg = match is_optional { + false => format!("{}: {}", &f.js_name, arg), + true => match self.use_nullable { + false => format!("{}?: {}", &f.js_name, arg), + true => format!("{}: {} | null", &f.js_name, arg), + }, + }; if self.kind == NapiStructKind::Constructor { ctor_args.push(arg.clone()); } diff --git a/crates/macro/src/parser/attrs.rs b/crates/macro/src/parser/attrs.rs index f1e3e11d..f150597f 100644 --- a/crates/macro/src/parser/attrs.rs +++ b/crates/macro/src/parser/attrs.rs @@ -55,15 +55,15 @@ macro_rules! attrgen { (getter, Getter(Span, Option)), (setter, Setter(Span, Option)), (readonly, Readonly(Span)), - (enumerable, Enumerable(Span, Option)), - (writable, Writable(Span, Option)), - (configurable, Configurable(Span, Option)), + (enumerable, Enumerable(Span, Option), true), + (writable, Writable(Span, Option), true), + (configurable, Configurable(Span, Option), true), (skip, Skip(Span)), (strict, Strict(Span)), (return_if_invalid, ReturnIfInvalid(Span)), (object, Object(Span)), - (object_from_js, ObjectFromJs(Span, Option)), - (object_to_js, ObjectToJs(Span, Option)), + (object_from_js, ObjectFromJs(Span, Option), true), + (object_to_js, ObjectToJs(Span, Option), true), (custom_finalize, CustomFinalize(Span)), (namespace, Namespace(Span, String, Span)), (iterator, Iterator(Span)), @@ -72,6 +72,7 @@ macro_rules! attrgen { (ts_type, TsType(Span, String, Span)), (ts_generic_types, TsGenericTypes(Span, String, Span)), (string_enum, StringEnum(Span)), + (use_nullable, UseNullable(Span, Option), false), // impl later // (inspectable, Inspectable(Span)), @@ -86,8 +87,8 @@ macro_rules! attrgen { } macro_rules! methods { - ($(($name:ident, $variant:ident($($contents:tt)*)),)*) => { - $(methods!(@method $name, $variant($($contents)*));)* + ($(($name:ident, $variant:ident($($contents:tt)*) $($extra_tokens:tt)*),)*) => { + $(methods!(@method $name, $variant($($contents)*) $($extra_tokens)*);)* #[cfg(feature = "strict")] #[allow(unused)] @@ -131,7 +132,7 @@ macro_rules! methods { } }; - (@method $name:ident, $variant:ident(Span, Option)) => { + (@method $name:ident, $variant:ident(Span, Option), $default_value:literal) => { pub fn $name(&self) -> bool { self.attrs .iter() @@ -143,7 +144,7 @@ macro_rules! methods { _ => None, }) .next() - .unwrap_or(true) + .unwrap_or($default_value) } }; @@ -265,11 +266,11 @@ impl Default for BindgenAttrs { } macro_rules! gen_bindgen_attr { - ($( ($method:ident, $($variants:tt)*) ,)*) => { + ($( ($method:ident, $variant:ident($($associated_data:tt)*) $($extra_tokens:tt)*) ,)*) => { /// The possible attributes in the `#[napi]`. #[derive(Debug)] pub enum BindgenAttr { - $($($variants)*,)* + $($variant($($associated_data)*)),* } } } @@ -395,7 +396,7 @@ impl Parse for BindgenAttr { return Ok(BindgenAttr::$variant(attr_span, val, span)) }); - (@parser $variant:ident(Span, Option)) => ({ + (@parser $variant:ident(Span, Option), $default_value:literal) => ({ if let Ok(_) = input.parse::() { let (val, _) = match input.parse::() { Ok(str) => (str.value(), str.span()), @@ -406,7 +407,7 @@ impl Parse for BindgenAttr { }; return Ok::(BindgenAttr::$variant(attr_span, Some(val))) } else { - return Ok(BindgenAttr::$variant(attr_span, Some(true))) + return Ok(BindgenAttr::$variant(attr_span, Some($default_value))) } }); diff --git a/crates/macro/src/parser/mod.rs b/crates/macro/src/parser/mod.rs index 5de28f29..ff01a070 100644 --- a/crates/macro/src/parser/mod.rs +++ b/crates/macro/src/parser/mod.rs @@ -862,6 +862,7 @@ impl ConvertToAST for syn::ItemStruct { } else { NapiStructKind::None }; + let use_nullable = opts.use_nullable(); for (i, field) in self.fields.iter_mut().enumerate() { match field.vis { @@ -943,6 +944,7 @@ impl ConvertToAST for syn::ItemStruct { implement_iterator, use_custom_finalize: opts.custom_finalize().is_some(), register_name: get_register_ident(format!("{struct_name}_struct").as_str()), + use_nullable, }), }) } diff --git a/examples/napi/__tests__/__snapshots__/typegen.spec.ts.md b/examples/napi/__tests__/__snapshots__/typegen.spec.ts.md index e1a4e7ac..93e4c626 100644 --- a/examples/napi/__tests__/__snapshots__/typegen.spec.ts.md +++ b/examples/napi/__tests__/__snapshots__/typegen.spec.ts.md @@ -143,6 +143,14 @@ Generated by [AVA](https://avajs.dev). constructor(width: number, height: number)␊ }␊ ␊ + export class DefaultUseNullableClass {␊ + requiredNumberField: number␊ + requiredStringField: string␊ + optionalNumberField?: number␊ + optionalStringField?: string␊ + constructor(requiredNumberField: number, requiredStringField: string, optionalNumberField?: number, optionalStringField?: string)␊ + }␊ + ␊ export class Dog {␊ name: string␊ constructor(name: string)␊ @@ -193,6 +201,14 @@ Generated by [AVA](https://avajs.dev). returnThis(this: this): this␊ }␊ ␊ + export class NotUseNullableClass {␊ + requiredNumberField: number␊ + requiredStringField: string␊ + optionalNumberField?: number␊ + optionalStringField?: string␊ + constructor(requiredNumberField: number, requiredStringField: string, optionalNumberField?: number, optionalStringField?: string)␊ + }␊ + ␊ export class NotWritableClass {␊ name: string␊ constructor(name: string)␊ @@ -214,6 +230,14 @@ Generated by [AVA](https://avajs.dev). constructor(orderBy: Array, select: Array, struct: string, where?: string)␊ }␊ ␊ + export class UseNullableClass {␊ + requiredNumberField: number␊ + requiredStringField: string␊ + nullableNumberField: number | null␊ + nullableStringField: string | null␊ + constructor(requiredNumberField: number, requiredStringField: string, nullableNumberField: number | null, nullableStringField: string | null)␊ + }␊ + ␊ export class Width {␊ value: number␊ constructor(value: number)␊ @@ -372,6 +396,13 @@ Generated by [AVA](https://avajs.dev). /** This is a const */␊ export const DEFAULT_COST: number␊ ␊ + export interface DefaultUseNullableStruct {␊ + requiredNumberField: number␊ + requiredStringField: string␊ + optionalNumberField?: number␊ + optionalStringField?: string␊ + }␊ + ␊ export function derefUint8Array(a: Uint8Array, b: Uint8ClampedArray): number␊ ␊ export function either3(input: string | number | boolean): number␊ @@ -465,6 +496,13 @@ Generated by [AVA](https://avajs.dev). ␊ export function mutateTypedArray(input: Float32Array): void␊ ␊ + export interface NotUseNullableStruct {␊ + requiredNumberField: number␊ + requiredStringField: string␊ + optionalNumberField?: number␊ + optionalStringField?: string␊ + }␊ + ␊ export interface Obj {␊ v: string | number␊ }␊ @@ -619,6 +657,13 @@ Generated by [AVA](https://avajs.dev). ␊ export function u8ArrayToArray(input: any): Array␊ ␊ + export interface UseNullableStruct {␊ + requiredNumberField: number␊ + requiredStringField: string␊ + nullableNumberField: number | null␊ + nullableStringField: string | null␊ + }␊ + ␊ export function validateArray(arr: Array): number␊ ␊ export function validateBigint(input: bigint): bigint␊ diff --git a/examples/napi/__tests__/__snapshots__/typegen.spec.ts.snap b/examples/napi/__tests__/__snapshots__/typegen.spec.ts.snap index 65024907062463c5f71d9e0c4dd5b2405ad5800b..d8e2ce56a278216354cef86cd67eef84282c30dd 100644 GIT binary patch literal 4577 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 LLLca6AWQ%Nba%)d literal 4444 zcmV-i5u@%wRzVz+wzW@hr_&jK>l>iC)A4R*tyr?hn0u7({M@bE-Ws=5TK=vtC{u<`ng{^?GyG9?m75Ff+R?;WH*VEC3wyc2f)Gk2QdFy z%wry2iogAvN+x4^{X4PZc^r`?O=uQKNpNe)Yv~{^#iU>#s!|N@TDkY@GsWLIOb?c%=K^w=i>T;It0k z-NT_*20lsHI*8TaKuq!-OCrJ-B+$)3?F{)6UXB%sp`NXnAn*@J0 ziiXr?M8Y;X_*y<(T1QCzxXxqA9)Moq*w<^CNZpQN!u%1s=IGm+C@beq_A(y4vaTrP4=g^?>^pq{ipGlUw+gLH^opgD)THkM_oKbMMBv? zOwXI|VLlFXA^G~y(FhOdDf1p{$B6^ZU>;sGPAf9B)^W;=1z#!onEbnY%r)*^D zBbs3A$Hcz5v5+_Sd@7D&yr7E!R?U$^DZZ;4@TiwR$=7q5)%4h<990O1o^E+D2gf`+ zV)&%`a}nG5gqX@rOs6Xfdil|)Y?jAV%{9EffZwl}kbqzSl;yPi)2YM%%nMuZSqRfr zRYKi0TI1J&Tw(8Ka;hD_6dK@}2J(T6HuM22XL4L1{RNuh*x!w?{fJser~|%bZCghp z!6DBR>5NS^o&nuNY?2UPyw3O<%X?MIE7EfHE14DyK;IQYF;-HUNAVyh5$LyyFlX z;+JO5+Q18I0u`LI8_`Im;0hC2S~z7YT}$W<6#Rw;_=>)tRSa%J#+s#WcNxll*F+z7 z*>*589>cAM?=$F)5mbCYm+{vw`j|lw%zmbgVO+6DuB=HLkridjm27u@5DMJR2G;?O z*<1m?|`Wg(!MZXQ8GnTv$W_bn)X@_TE;siPgAbvhy z3_b*LPBHhpO!!qYbWJI``9q}^WW)1}B#2I=g8wa~0)PK^C9+TG)kTo1fXb0WTZ7X; zTpsdRN6LjfCM2n}>3AD+hyi*4?FZW2!5Np|XG~%itQPOKfl`a>F%d{uxUC@p{P9VN(&tEu3(qb=Pjz@hadUTM>X(K z4ZCSNzDEb7#$h&~*Jf7UxgZdK&}zEv68LWoYm>^pgvXW=}rUSK^wuV23NG!H}d=9`_ zp2ieXaM3OJ!_qIo({5n~)#F~Vj=fSu(I6cu|t>AlqQiI4Huc1 z{^4bbHxCD)ytW0d5eb7ho(JJ2tjNyJ4otF88u|8J{~ROQ?k&-3g}PR3+|_y*uVI^l zI`MqD@&x@hp(z{pJ!qI~YqS|xL>g&Q_EH4?Hlp!^oMLrucEu}OqLiYW`E{I&-G>hc zX@g;FhzlC!A@wf$b^tR70Ic#|C>K9y@S6SD1@Q-1wI(~R<-SMZ50rw>gRD!RwE>*y zjjJEj#??PNETnEf1{qFv_nr>6qkQtXpAdrBH%}f3JesTd`X)5%n!WK3p?7gZPpsX}Zi&?JebX>gSc-izbHaXzg0tXi?pQ~vXI zRXF`{ZU?#5oLN3h!NB)wcAuAZoV)X)jqFyP!mJ$1>zbRG+o-~s8_Fg70aTCT}^C(2IG?O7myNy!Q4G%Ne&q) z)0+TWhx$KVj5B`9;uzAqDqHKyD#kNNBtB5Z&eo~|Oz*^5`VMTdg|a3qPr^XH0@h@A zAexy~3j-MJTTs7|i`^&N(#wD)Vjv(551slOWOR^2I|w#;_d$H1nSAqTPsbr<@-cid zP5qIGE)gBD<%vF8X!Bw&U5~@lr1rRaAlT2;+3mGs>>j}^wQ-v`X{z2-Oim51-SSo7 zW}B2odeVlK32S2nc% zGe@cGcUJiLOvpumJDtmlcx2N)C4Jwjd0Q?(s^p`erQ80%eBgzj!IY*>c+ zBhNvF)%h+kU8*B`A!(}4h4J($NU;8-b_y6Jo(nw*JMJ-5nQ`gr@L9e-!Y7P-&J&6$ z7v^3NX2Z`t^%Kl8wm-s$W2}KaBaiX-lwHHmC;0OSOULl=6d%r*!udbNOcR8B=#8V4 z08<&rTpaR9mB1<%w~WisPzhUf%8bE;CrHAcn+Hz^k+QUBYMEOsZS|&to}Kz^y*N6a zym>YI^Wph))^2Gd(3M4*31J{zKB-_@J%U27?Oy7z+fIEPF<}H zK^w~Rtg0oo$?B|J9IaDnj!m0=0Ktr(JlY$8DXMI^j`>~~+iKHgBa@DHdfd|ja;7`; zvN>d?VaNu_7SZM+xu7A1up%NI|sEZ()Y-;NmpT$Psy&|EFdL-CDiW^18IXV6_f-5yo%<`!%Gatd=3V3ND2D%9o>ilgrYVc?N)u*~G;(HHab@^*s2VOhpy=zChJoD-cS zn^)@TSY_OERg)}dNAfF2hRT6h+u*(OIpW#exRPWg&c?x{)i~Xq_o8xNvLtdGvF~bw z2B!oB5NhlTl^7dXZ9&aDhby9ZfCtGvqvClZI)dG|uZTXp=%@2+(= zo4XzA6ucuINOh6=B+*HYwSt9FZa>a4$=SYM0sM3T+{njbrZB%J3mQmuIp`UA`=y{+ zL?7TveEE*t_~I+9&|+lR+*j6~6`JxnOp)zQU)W6pysy@{Pgy_Csc<{mqfX1pM<2aP zXc|Yv=vkkSJx9l0Z`n2yg{^!`k!|V})LF}V2HT+)2Ji)2s4A7zSN1KUCl&TyUhnVV zG?$-U=(@wyq07gr&+I!pst^~dL~i<)z*}|HrmDU4I5h6R4HU}=p)~3uoQE-SU-ijm z%C_6o!!ui*oQU#tqrNHsI|ohrdYaE14V%GMC%0DGIz+2xwSl1)O;-@EM~C>e7;bd^ z*v9Zu6i#^9dbZTY+?~N{nk_Q88sW*}B!N(zMYYme*MUYGgt3eksqPQFE~0@A+be># zOt(GI$?{|x0zK%z`j5k6E><>q1fWQN;mUYX9*XOdS&{{8i76 znt1nIfpB3J(tN3)ivfO~#!0fd1AVAZh3z%CTb?O5Vbx8&Q#`v+2Sc9^Pr16!1d5ko zu09EnU#S30W3=8ms&W*>9jMbyB747&*`HSi$o0w9cQmD z^bHok5Jv?R))C)PzXTT5ol7mZzGvEd53SJ!UI^g1{O|OnG|z;1`23kWxS;Brf&fED z=kl)#0kz^dHR#e@&ji+#4p%|4G%f_o?k0n!h1yh`&&x~O=Do+R=bun%%|D^in)g>) z_2mN}@7*h{D%QYjF#5m~S9V*!U|A28SVhRwcipgH(_zbiZ51XJT;}Sgb0x4(z8C6S z%ie1?F1fy(vo%XuH@(B*kp=PGPw0)}EiZl$3bBD*gIfm%7Dt7}Y;3J_?`qf+*DVqb z%VIa7mbJ6CFd~X(@40(M?QtT#Vp>ytw~NCTLML-*!Vf?*9>=hvzPtY;BG^(L3MlHSde~+Hu>3*gb;7J9(;@LqY(*A8CBM?dsRa^sifQE|HT13u_aX;hihCO)d= z2TR%f@>g@~56LxW@mYCY6cwo8`-m+e5O=Pz5%qtLsN`CcS0~?|ogSUdoS|b+AoT9u i({lK(uX0=6 diff --git a/examples/napi/index.d.ts b/examples/napi/index.d.ts index 7874fe39..814003eb 100644 --- a/examples/napi/index.d.ts +++ b/examples/napi/index.d.ts @@ -133,6 +133,14 @@ export class CustomFinalize { constructor(width: number, height: number) } +export class DefaultUseNullableClass { + requiredNumberField: number + requiredStringField: string + optionalNumberField?: number + optionalStringField?: string + constructor(requiredNumberField: number, requiredStringField: string, optionalNumberField?: number, optionalStringField?: string) +} + export class Dog { name: string constructor(name: string) @@ -183,6 +191,14 @@ export class NinjaTurtle { returnThis(this: this): this } +export class NotUseNullableClass { + requiredNumberField: number + requiredStringField: string + optionalNumberField?: number + optionalStringField?: string + constructor(requiredNumberField: number, requiredStringField: string, optionalNumberField?: number, optionalStringField?: string) +} + export class NotWritableClass { name: string constructor(name: string) @@ -204,6 +220,14 @@ export class Selector { constructor(orderBy: Array, select: Array, struct: string, where?: string) } +export class UseNullableClass { + requiredNumberField: number + requiredStringField: string + nullableNumberField: number | null + nullableStringField: string | null + constructor(requiredNumberField: number, requiredStringField: string, nullableNumberField: number | null, nullableStringField: string | null) +} + export class Width { value: number constructor(value: number) @@ -362,6 +386,13 @@ export function dateToNumber(input: Date): number /** This is a const */ export const DEFAULT_COST: number +export interface DefaultUseNullableStruct { + requiredNumberField: number + requiredStringField: string + optionalNumberField?: number + optionalStringField?: string +} + export function derefUint8Array(a: Uint8Array, b: Uint8ClampedArray): number export function either3(input: string | number | boolean): number @@ -455,6 +486,13 @@ export function mutateExternal(external: ExternalObject, newVal: number) export function mutateTypedArray(input: Float32Array): void +export interface NotUseNullableStruct { + requiredNumberField: number + requiredStringField: string + optionalNumberField?: number + optionalStringField?: string +} + export interface Obj { v: string | number } @@ -609,6 +647,13 @@ export function u64ArrayToArray(input: any): Array export function u8ArrayToArray(input: any): Array +export interface UseNullableStruct { + requiredNumberField: number + requiredStringField: string + nullableNumberField: number | null + nullableStringField: string | null +} + export function validateArray(arr: Array): number export function validateBigint(input: bigint): bigint diff --git a/examples/napi/src/nullable.rs b/examples/napi/src/nullable.rs index 18a532c8..b6f1fd38 100644 --- a/examples/napi/src/nullable.rs +++ b/examples/napi/src/nullable.rs @@ -12,3 +12,51 @@ fn return_null() -> Null { #[napi] fn return_undefined() -> Undefined {} + +#[napi(object, use_nullable = true)] +struct UseNullableStruct { + pub required_number_field: u32, + pub required_string_field: String, + pub nullable_number_field: Option, + pub nullable_string_field: Option, +} + +#[napi(object, use_nullable = false)] +struct NotUseNullableStruct { + pub required_number_field: u32, + pub required_string_field: String, + pub optional_number_field: Option, + pub optional_string_field: Option, +} + +#[napi(object)] +struct DefaultUseNullableStruct { + pub required_number_field: u32, + pub required_string_field: String, + pub optional_number_field: Option, + pub optional_string_field: Option, +} + +#[napi(constructor, use_nullable = true)] +struct UseNullableClass { + pub required_number_field: u32, + pub required_string_field: String, + pub nullable_number_field: Option, + pub nullable_string_field: Option, +} + +#[napi(constructor, use_nullable = false)] +struct NotUseNullableClass { + pub required_number_field: u32, + pub required_string_field: String, + pub optional_number_field: Option, + pub optional_string_field: Option, +} + +#[napi(constructor)] +struct DefaultUseNullableClass { + pub required_number_field: u32, + pub required_string_field: String, + pub optional_number_field: Option, + pub optional_string_field: Option, +}