feat: support optional RPC fields

This commit is contained in:
Wojciech Nawrocki 2022-05-12 14:19:12 -04:00 committed by Leonardo de Moura
parent dd6528bceb
commit bea68819c9
2 changed files with 39 additions and 3 deletions

View file

@ -85,6 +85,9 @@ def withFieldsFlattened (indVal : InductiveVal) (params : Array Expr)
end
def isOptField (n : Name) : Bool :=
n.toString.endsWith "?"
private def deriveStructureInstance (indVal : InductiveVal) (params : Array Expr) : TermElabM Syntax :=
withFields indVal params fun fields => do
trace[Elab.Deriving.RpcEncoding] "for structure {indVal.name} with params {params}"
@ -97,20 +100,30 @@ private def deriveStructureInstance (indVal : InductiveVal) (params : Array Expr
let mut uniqFieldEncIds : Array Syntax := #[]
let mut fieldEncIds' : DiscrTree Syntax := {}
for (fieldName, fieldTp) in fields do
let mut fieldTp := fieldTp
if isOptField fieldName then
if !fieldTp.isAppOf ``Option then
throwError "optional field '{fieldName}' has type{indentD m!"{fieldTp}"}\nbut is expected to have type{indentD "Option _"}"
fieldTp := fieldTp.getArg! 0
-- postulate that the field has an encoding and remember the encoding's binder name
fieldIds := fieldIds.push <| mkIdent fieldName
let mut fieldEncId := Syntax.missing
match (← fieldEncIds'.getMatch fieldTp).back? with
| none =>
let fieldEncId ← mkIdent <$> mkFreshUserName fieldName
fieldEncId ← mkIdent <$> mkFreshUserName fieldName
binders := binders.push <| ← `(Deriving.explicitBinderF| ( $fieldEncId:ident ))
let stx ← PrettyPrinter.delab fieldTp
binders := binders.push <|
← `(Deriving.instBinderF| [ $(mkIdent ``Lean.Server.RpcEncoding) $stx $fieldEncId:ident ])
fieldEncIds' ← fieldEncIds'.insert fieldTp fieldEncId
uniqFieldEncIds := uniqFieldEncIds.push fieldEncId
| some fid => fieldEncId := fid
if isOptField fieldName then
fieldEncIds := fieldEncIds.push <| ← ``(Option $fieldEncId:ident)
else
fieldEncIds := fieldEncIds.push fieldEncId
| some fid =>
fieldEncIds := fieldEncIds.push fid
-- helpers for field initialization syntax
let fieldInits (func : Name) := fieldIds.mapM fun fid =>
@ -222,6 +235,7 @@ private def deriveInductiveInstance (indVal : InductiveVal) (params : Array Expr
inductive $(mkIdent packetNm) where
$[$(st.ctors):ctor]*
deriving $(mkIdent ``FromJson), $(mkIdent ``ToJson)
instance : $(mkIdent ``RpcEncoding) $typeId:ident $packetAppliedId:ident where
rpcEncode := fun x => match x with

View file

@ -2,10 +2,16 @@ import Lean.Server.Rpc.Basic
open Lean Server
@[reducible]
def rpcPacketFor (α : Type) {β : outParam Type} [RpcEncoding α β] := β
structure FooRef where
a : Array Nat
deriving RpcEncoding with { withRef := true }
#synth FromJson (rpcPacketFor (WithRpcRef FooRef))
#synth ToJson (rpcPacketFor (WithRpcRef FooRef))
structure FooJson where
s : String
deriving FromJson, ToJson
@ -15,19 +21,35 @@ structure Bar where
fooJson : FooJson
deriving RpcEncoding
#synth FromJson (rpcPacketFor Bar)
#synth ToJson (rpcPacketFor Bar)
structure BarTrans where
bar : Bar
deriving RpcEncoding
#synth FromJson (rpcPacketFor BarTrans)
#synth ToJson (rpcPacketFor BarTrans)
structure Baz where
arr : Array String -- non-constant field
deriving RpcEncoding
#synth FromJson (rpcPacketFor Baz)
#synth ToJson (rpcPacketFor Baz)
structure FooGeneric (α : Type) where
a : α
b? : Option α
deriving RpcEncoding
#synth FromJson (rpcPacketFor <| FooGeneric Nat)
#synth ToJson (rpcPacketFor <| FooGeneric Nat)
inductive FooInductive (α : Type) where
| a : α → Bar → FooInductive α
| b : (n : Nat) → (a : α) → Nat → FooInductive α
deriving RpcEncoding
#synth FromJson (rpcPacketFor <| FooInductive Nat)
#synth ToJson (rpcPacketFor <| FooInductive Nat)