diff --git a/.golden/kotlinBasicRecordWithExpandOptionalSpec/golden b/.golden/kotlinBasicRecordWithExpandOptionalSpec/golden new file mode 100644 index 0000000..9520d4f --- /dev/null +++ b/.golden/kotlinBasicRecordWithExpandOptionalSpec/golden @@ -0,0 +1,4 @@ +data class Data( + val field0: Int, + val field1: Int? = null, +) \ No newline at end of file diff --git a/.golden/swiftBasicRecordWithExpandOptionalSpec/golden b/.golden/swiftBasicRecordWithExpandOptionalSpec/golden new file mode 100644 index 0000000..71f9b62 --- /dev/null +++ b/.golden/swiftBasicRecordWithExpandOptionalSpec/golden @@ -0,0 +1,4 @@ +struct Data { + let field0: Int + let field1: Optional +} \ No newline at end of file diff --git a/moat.cabal b/moat.cabal index 7c2ac00..9401bf4 100644 --- a/moat.cabal +++ b/moat.cabal @@ -67,6 +67,7 @@ test-suite spec BasicNewtypeWithConcreteFieldSpec BasicNewtypeWithEitherFieldSpec BasicRecordSpec + BasicRecordWithExpandOptionalSpec Common SumOfProductSpec SumOfProductWithLinkEnumInterfaceSpec diff --git a/src/Moat.hs b/src/Moat.hs index ba128ea..a01c119 100644 --- a/src/Moat.hs +++ b/src/Moat.hs @@ -662,7 +662,7 @@ consToMoatType :: consToMoatType o@Options {..} parentName instTys variant ts bs = \case [] -> do value <- lift $ newName "value" - matches <- liftCons (mkVoid parentName instTys ts) + matches <- liftCons (mkVoid o parentName instTys ts) lift $ lamE [varP value] (caseE (varE value) matches) cons -> do -- TODO: use '_' instead of matching @@ -677,14 +677,14 @@ consToMoatType o@Options {..} parentName instTys variant ts bs = \case case variant of NewtypeInstance -> do if typeAlias - then mkNewtypeInstanceAlias instTys con + then mkNewtypeInstanceAlias o instTys con else mkNewtypeInstance o instTys con Newtype -> do if | newtypeTag -> do mkTypeTag o parentName instTys con | typeAlias -> do - mkTypeAlias parentName instTys con + mkTypeAlias o parentName instTys con | otherwise -> do mkNewtype o parentName instTys con _ -> do @@ -695,7 +695,7 @@ consToMoatType o@Options {..} parentName instTys variant ts bs = \case cases <- forM cons' (liftEither . mkCase o) ourMatch <- matchProxy - =<< lift (enumExp parentName instTys dataInterfaces dataProtocols dataAnnotations cases dataRawValue ts bs) + =<< lift (enumExp o parentName instTys dataInterfaces dataProtocols dataAnnotations cases dataRawValue ts bs) pure [pure ourMatch] liftCons :: (Functor f, Applicative g) => f a -> f [g a] @@ -770,13 +770,14 @@ mkLabel Options {..} = . show mkNewtypeInstanceAlias :: - () => + -- | Options + Options -> -- | type variables [Type] -> -- | constructor info ConstructorInfo -> MoatM Match -mkNewtypeInstanceAlias (stripConT -> instTys) = \case +mkNewtypeInstanceAlias o (stripConT -> instTys) = \case ConstructorInfo { constructorName = conName, constructorFields = [field] @@ -785,8 +786,7 @@ mkNewtypeInstanceAlias (stripConT -> instTys) = \case match (conP 'Proxy []) ( normalB - ( pure - (aliasExp conName instTys field) + ( aliasExp o conName instTys field ) ) [] @@ -807,7 +807,7 @@ mkNewtypeInstance o@Options {..} (stripConT -> instTys) = \case { constructorFields = [field], .. } -> do - matchProxy =<< lift (newtypeExp constructorName instTys dataInterfaces dataProtocols dataAnnotations (prettyField o (mkName "value") field)) + matchProxy =<< lift (newtypeExp o constructorName instTys dataInterfaces dataProtocols dataAnnotations (prettyField o (mkName "value") field)) _ -> throwError ExpectedNewtypeInstance -- make a newtype into an empty enum @@ -823,7 +823,7 @@ mkTypeTag :: -- | constructor info ConstructorInfo -> MoatM Match -mkTypeTag Options {..} typName instTys = \case +mkTypeTag o@Options {..} typName instTys = \case ConstructorInfo { constructorFields = [field] } -> do @@ -831,12 +831,13 @@ mkTypeTag Options {..} typName instTys = \case mkName (nameStr typName ++ "Tag") let tag = tagExp typName parentName field False - matchProxy =<< lift (enumExp parentName instTys dataInterfaces dataProtocols dataAnnotations [] dataRawValue [tag] (False, Nothing, [])) + matchProxy =<< lift (enumExp o parentName instTys dataInterfaces dataProtocols dataAnnotations [] dataRawValue [tag] (False, Nothing, [])) _ -> throwError $ NotANewtype typName -- make a newtype into a type alias mkTypeAlias :: - () => + -- | Options + Options -> -- | type name Name -> -- | type variables @@ -844,7 +845,7 @@ mkTypeAlias :: -- | constructor info ConstructorInfo -> MoatM Match -mkTypeAlias typName instTys = \case +mkTypeAlias o typName instTys = \case ConstructorInfo { constructorFields = [field] } -> do @@ -852,14 +853,15 @@ mkTypeAlias typName instTys = \case match (conP 'Proxy []) ( normalB - (pure (aliasExp typName instTys field)) + (aliasExp o typName instTys field) ) [] _ -> throwError $ NotANewtype typName -- | Make a void type (empty enum) mkVoid :: - () => + -- | Options + Options -> -- | type name Name -> -- | type variables @@ -867,9 +869,9 @@ mkVoid :: -- | tags [Exp] -> MoatM Match -mkVoid typName instTys ts = +mkVoid o typName instTys ts = matchProxy - =<< lift (enumExp typName instTys [] [] [] [] Nothing ts (False, Nothing, [])) + =<< lift (enumExp o typName instTys [] [] [] [] Nothing ts (False, Nothing, [])) mkNewtype :: () => @@ -883,11 +885,11 @@ mkNewtype o@Options {..} typName instTys = \case { constructorFields = [field], constructorVariant = RecordConstructor [name] } -> do - matchProxy =<< lift (newtypeExp typName instTys dataInterfaces dataProtocols dataAnnotations (prettyField o name field)) + matchProxy =<< lift (newtypeExp o typName instTys dataInterfaces dataProtocols dataAnnotations (prettyField o name field)) ConstructorInfo { constructorFields = [field] } -> do - matchProxy =<< lift (newtypeExp typName instTys dataInterfaces dataProtocols dataAnnotations (prettyField o (mkName "value") field)) + matchProxy =<< lift (newtypeExp o typName instTys dataInterfaces dataProtocols dataAnnotations (prettyField o (mkName "value") field)) ci -> throwError $ ImproperNewtypeConstructorInfo ci -- | Make a single-constructor product (struct) @@ -910,7 +912,7 @@ mkProd o@Options {..} typName instTys ts = \case { constructorVariant = NormalConstructor, constructorFields = [] } -> do - matchProxy =<< lift (structExp typName instTys dataInterfaces dataProtocols dataAnnotations [] ts makeBase) + matchProxy =<< lift (structExp o typName instTys dataInterfaces dataProtocols dataAnnotations [] ts makeBase) -- single constructor, non-record (Normal) ConstructorInfo { constructorVariant = NormalConstructor, @@ -931,7 +933,7 @@ mkProd o@Options {..} typName instTys ts = \case .. } -> do let fields = zipFields o fieldNames constructorFields - matchProxy =<< lift (structExp typName instTys dataInterfaces dataProtocols dataAnnotations fields ts makeBase) + matchProxy =<< lift (structExp o typName instTys dataInterfaces dataProtocols dataAnnotations fields ts makeBase) zipFields :: Options -> [Name] -> [Type] -> [Exp] zipFields o = zipWithPred p (prettyField o) @@ -1402,21 +1404,25 @@ stripConT = mapMaybe noConT -- | Construct a Type Alias. aliasExp :: - () => + -- | Options + Options -> -- | alias name Name -> -- | type variables [Type] -> -- | type (RHS) Type -> - Exp -aliasExp name tyVars field = - RecConE - 'MoatAlias - [ ('aliasName, unqualName name), - ('aliasTyVars, prettyTyVars tyVars), - ('aliasTyp, toMoatTypeECxt field) - ] + Q Exp +aliasExp Options {..} name tyVars field = do + optionalExpand_ <- Syntax.lift optionalExpand + pure $ + RecConE + 'MoatAlias + [ ('aliasName, unqualName name), + ('aliasTyVars, prettyTyVars tyVars), + ('aliasTyp, toMoatTypeECxt field), + ('aliasOptionalExpand, optionalExpand_) + ] -- | Construct a Tag. tagExp :: @@ -1443,7 +1449,8 @@ tagExp tyconName parentName typ dis = -- | Construct an Enum. enumExp :: - () => + -- | Options + Options -> -- | parent name Name -> -- | type variables @@ -1463,11 +1470,12 @@ enumExp :: -- | Make base? (Bool, Maybe MoatType, [Protocol]) -> Q Exp -enumExp parentName tyVars ifaces protos anns cases raw tags bs = +enumExp Options {..} parentName tyVars ifaces protos anns cases raw tags bs = do enumInterfaces_ <- Syntax.lift ifaces enumAnnotations_ <- Syntax.lift anns enumProtocols_ <- Syntax.lift protos + optionalExpand_ <- Syntax.lift optionalExpand applyBase bs $ RecConE 'MoatEnum @@ -1479,11 +1487,12 @@ enumExp parentName tyVars ifaces protos anns cases raw tags bs = ('enumCases, ListE cases), ('enumRawValue, rawValueE raw), ('enumPrivateTypes, ListE []), - ('enumTags, ListE tags) + ('enumTags, ListE tags), + ('enumOptionalExpand, optionalExpand_) ] newtypeExp :: - () => + Options -> Name -> [Type] -> [Interface] -> @@ -1491,7 +1500,7 @@ newtypeExp :: [Annotation] -> Exp -> Q Exp -newtypeExp name tyVars ifaces protos anns field = +newtypeExp Options {..} name tyVars ifaces protos anns field = [| MoatNewtype { newtypeName = $(pure $ unqualName name), @@ -1499,13 +1508,15 @@ newtypeExp name tyVars ifaces protos anns field = newtypeField = $(pure field), newtypeProtocols = $(Syntax.lift protos), newtypeAnnotations = $(Syntax.lift anns), - newtypeInterfaces = $(Syntax.lift ifaces) + newtypeInterfaces = $(Syntax.lift ifaces), + newtypeOptionalExpand = $(Syntax.lift optionalExpand) } |] -- | Construct a Struct. structExp :: - () => + -- | Options + Options -> -- | struct name Name -> -- | type variables @@ -1523,10 +1534,11 @@ structExp :: -- | Make base? (Bool, Maybe MoatType, [Protocol]) -> Q Exp -structExp name tyVars ifaces protos anns fields tags bs = do +structExp Options {..} name tyVars ifaces protos anns fields tags bs = do structInterfaces_ <- Syntax.lift ifaces structAnnotations_ <- Syntax.lift anns structProtocols_ <- Syntax.lift protos + optionalExpand_ <- Syntax.lift optionalExpand applyBase bs $ RecConE 'MoatStruct @@ -1537,7 +1549,8 @@ structExp name tyVars ifaces protos anns fields tags bs = do ('structAnnotations, structAnnotations_), ('structFields, ListE fields), ('structPrivateTypes, ListE []), - ('structTags, ListE tags) + ('structTags, ListE tags), + ('structOptionalExpand, optionalExpand_) ] matchProxy :: Exp -> MoatM Match @@ -1608,7 +1621,8 @@ aliasToNewtype MoatAlias {..} = newtypeField = ("value", aliasTyp), newtypeInterfaces = [], newtypeProtocols = [], - newtypeAnnotations = [] + newtypeAnnotations = [], + newtypeOptionalExpand = aliasOptionalExpand } aliasToNewtype m = m @@ -1618,6 +1632,7 @@ newtypeToAlias MoatNewtype {..} = MoatAlias { aliasName = newtypeName, aliasTyVars = newtypeTyVars, - aliasTyp = snd newtypeField + aliasTyp = snd newtypeField, + aliasOptionalExpand = newtypeOptionalExpand } newtypeToAlias m = m diff --git a/src/Moat/Pretty/Swift.hs b/src/Moat/Pretty/Swift.hs index 8ada2ee..668db02 100644 --- a/src/Moat/Pretty/Swift.hs +++ b/src/Moat/Pretty/Swift.hs @@ -26,36 +26,36 @@ prettySwiftDataWith indent = \case MoatEnum {..} -> "enum " ++ prettyMoatTypeHeader enumName enumTyVars - ++ prettyRawValueAndProtocols enumRawValue enumProtocols + ++ prettyRawValueAndProtocols (OptionalExpand enumOptionalExpand) enumRawValue enumProtocols ++ " {" ++ newlineNonEmpty enumCases - ++ prettyEnumCases indents enumCases + ++ prettyEnumCases (OptionalExpand enumOptionalExpand) indents enumCases ++ newlineNonEmpty enumPrivateTypes ++ prettyPrivateTypes indents enumPrivateTypes - ++ prettyTags indents enumTags + ++ prettyTags (OptionalExpand enumOptionalExpand) indents enumTags ++ newlineNonEmpty enumTags ++ "}" MoatStruct {..} -> "struct " ++ prettyMoatTypeHeader structName structTyVars - ++ prettyRawValueAndProtocols Nothing structProtocols + ++ prettyRawValueAndProtocols (OptionalExpand structOptionalExpand) Nothing structProtocols ++ " {" ++ newlineNonEmpty structFields - ++ prettyStructFields indents structFields + ++ prettyStructFields (OptionalExpand structOptionalExpand) indents structFields ++ newlineNonEmpty structPrivateTypes ++ prettyPrivateTypes indents structPrivateTypes - ++ prettyTags indents structTags + ++ prettyTags (OptionalExpand structOptionalExpand) indents structTags ++ newlineNonEmpty structTags ++ "}" MoatAlias {..} -> "typealias " ++ prettyMoatTypeHeader aliasName aliasTyVars ++ " = " - ++ prettyMoatType aliasTyp + ++ prettyMoatType (OptionalExpand aliasOptionalExpand) aliasTyp MoatNewtype {..} -> "struct " ++ prettyMoatTypeHeader newtypeName newtypeTyVars - ++ prettyRawValueAndProtocols Nothing newtypeProtocols + ++ prettyRawValueAndProtocols (OptionalExpand newtypeOptionalExpand) Nothing newtypeProtocols ++ " {\n" ++ indents ++ if isConcrete newtypeField @@ -63,7 +63,7 @@ prettySwiftDataWith indent = \case "let " ++ fst newtypeField ++ ": " - ++ prettyMoatType (snd newtypeField) + ++ prettyMoatType (OptionalExpand newtypeOptionalExpand) (snd newtypeField) ++ "\n}" else "typealias " @@ -72,7 +72,7 @@ prettySwiftDataWith indent = \case ++ " = Tagged<" ++ newtypeName ++ ", " - ++ prettyMoatType (snd newtypeField) + ++ prettyMoatType (OptionalExpand newtypeOptionalExpand) (snd newtypeField) ++ ">\n" ++ prettyNewtypeField indents newtypeField newtypeName ++ "}" @@ -91,11 +91,11 @@ prettyMoatTypeHeader :: String -> [String] -> String prettyMoatTypeHeader name [] = name prettyMoatTypeHeader name tyVars = name ++ "<" ++ intercalate ", " tyVars ++ ">" -prettyRawValueAndProtocols :: Maybe MoatType -> [Protocol] -> String -prettyRawValueAndProtocols Nothing [] = "" -prettyRawValueAndProtocols Nothing ps = ": " ++ prettyProtocols ps -prettyRawValueAndProtocols (Just ty) [] = ": " ++ prettyMoatType ty -prettyRawValueAndProtocols (Just ty) ps = ": " ++ prettyMoatType ty ++ ", " ++ prettyProtocols ps +prettyRawValueAndProtocols :: OptionalExpand -> Maybe MoatType -> [Protocol] -> String +prettyRawValueAndProtocols _ Nothing [] = "" +prettyRawValueAndProtocols _ Nothing ps = ": " ++ prettyProtocols ps +prettyRawValueAndProtocols o (Just ty) [] = ": " ++ prettyMoatType o ty +prettyRawValueAndProtocols o (Just ty) ps = ": " ++ prettyMoatType o ty ++ ", " ++ prettyProtocols ps prettyProtocols :: [Protocol] -> String prettyProtocols = \case @@ -111,8 +111,8 @@ prettyProtocols = \case -- TODO: Need a plan to avoid @error@ in these pure functions {-# ANN prettyTags "HLint: ignore" #-} -prettyTags :: String -> [MoatType] -> String -prettyTags indents = go +prettyTags :: OptionalExpand -> String -> [MoatType] -> String +prettyTags o indents = go where go [] = "" go (Tag {..} : ts) = @@ -124,7 +124,7 @@ prettyTags indents = go ++ " = Tagged<" ++ (if tagDisambiguate then tagName ++ "Tag" else tagParent) ++ ", " - ++ prettyMoatType tagTyp + ++ prettyMoatType o tagTyp ++ ">" ++ go ts go _ = error "non-tag supplied to prettyTags" @@ -147,28 +147,33 @@ prettyTagDisambiguator disambiguate indents parent = ++ "Tag { }\n" else "" -labelCase :: Maybe String -> MoatType -> String -labelCase Nothing ty = prettyMoatType ty -labelCase (Just label) ty = "_ " ++ label ++ ": " ++ prettyMoatType ty +labelCase :: OptionalExpand -> Maybe String -> MoatType -> String +labelCase o Nothing ty = prettyMoatType o ty +labelCase o (Just label) ty = "_ " ++ label ++ ": " ++ prettyMoatType o ty + +newtype OptionalExpand = OptionalExpand Bool -- | Pretty-print a 'Ty'. -prettyMoatType :: MoatType -> String -prettyMoatType = \case +prettyMoatType :: OptionalExpand -> MoatType -> String +prettyMoatType o@(OptionalExpand oe) = \case Str -> "String" Unit -> "()" Bool -> "Bool" Character -> "Character" - Tuple2 e1 e2 -> "(" ++ prettyMoatType e1 ++ ", " ++ prettyMoatType e2 ++ ")" - Tuple3 e1 e2 e3 -> "(" ++ prettyMoatType e1 ++ ", " ++ prettyMoatType e2 ++ ", " ++ prettyMoatType e3 ++ ")" - Optional e -> prettyMoatType e ++ "?" + Tuple2 e1 e2 -> "(" ++ prettyMoatType o e1 ++ ", " ++ prettyMoatType o e2 ++ ")" + Tuple3 e1 e2 e3 -> "(" ++ prettyMoatType o e1 ++ ", " ++ prettyMoatType o e2 ++ ", " ++ prettyMoatType o e3 ++ ")" + Optional e -> + if oe + then "Optional<" <> prettyMoatType o e <> ">" + else prettyMoatType o e <> "?" -- Swift flips the parameters for Result, see https://developer.apple.com/documentation/swift/result - Result e1 e2 -> "Result<" ++ prettyMoatType e2 ++ ", " ++ prettyMoatType e1 ++ ">" - Set e -> "Set<" ++ prettyMoatType e ++ ">" - Dictionary e1 e2 -> "Dictionary<" ++ prettyMoatType e1 ++ ", " ++ prettyMoatType e2 ++ ">" - Array e -> "[" ++ prettyMoatType e ++ "]" + Result e1 e2 -> "Result<" ++ prettyMoatType o e2 ++ ", " ++ prettyMoatType o e1 ++ ">" + Set e -> "Set<" ++ prettyMoatType o e ++ ">" + Dictionary e1 e2 -> "Dictionary<" ++ prettyMoatType o e1 ++ ", " ++ prettyMoatType o e2 ++ ">" + Array e -> "[" ++ prettyMoatType o e ++ "]" -- App is special, we recurse until we no longer -- any applications. - App e1 e2 -> prettyApp e1 e2 + App e1 e2 -> prettyApp o e1 e2 I -> "Int" I8 -> "Int8" I16 -> "Int16" @@ -188,16 +193,16 @@ prettyMoatType = \case Concrete ty tys -> ty ++ "<" - ++ intercalate ", " (map prettyMoatType tys) + ++ intercalate ", " (map (prettyMoatType o) tys) ++ ">" Tag {..} -> tagParent ++ "." ++ tagName -prettyApp :: MoatType -> MoatType -> String -prettyApp t1 t2 = +prettyApp :: OptionalExpand -> MoatType -> MoatType -> String +prettyApp o t1 t2 = "((" - ++ intercalate ", " (map prettyMoatType as) + ++ intercalate ", " (map (prettyMoatType o) as) ++ ") -> " - ++ prettyMoatType r + ++ prettyMoatType o r ++ ")" where (as, r) = go t1 t2 @@ -205,8 +210,8 @@ prettyApp t1 t2 = (args, ret) -> (e1 : args, ret) go e1 e2 = ([e1], e2) -prettyEnumCases :: String -> [(String, [(Maybe String, MoatType)])] -> String -prettyEnumCases indents = go +prettyEnumCases :: OptionalExpand -> String -> [(String, [(Maybe String, MoatType)])] -> String +prettyEnumCases o indents = go where go = \case [] -> "" @@ -221,15 +226,15 @@ prettyEnumCases indents = go ++ "case " ++ caseNm ++ "(" - ++ intercalate ", " (map (uncurry labelCase) cs) + ++ intercalate ", " (map (uncurry $ labelCase o) cs) ++ ")\n" ++ go xs -prettyStructFields :: String -> [(String, MoatType)] -> String -prettyStructFields indents = go +prettyStructFields :: OptionalExpand -> String -> [(String, MoatType)] -> String +prettyStructFields o indents = go where go [] = "" - go ((fieldName, ty) : fs) = indents ++ "let " ++ fieldName ++ ": " ++ prettyMoatType ty ++ "\n" ++ go fs + go ((fieldName, ty) : fs) = indents ++ "let " ++ fieldName ++ ": " ++ prettyMoatType o ty ++ "\n" ++ go fs prettyNewtypeField :: String -> (String, MoatType) -> String -> String prettyNewtypeField indents (alias, _) fieldName = indents ++ "let " ++ alias ++ ": " ++ fieldName ++ "Tag" ++ "\n" diff --git a/src/Moat/Types.hs b/src/Moat/Types.hs index 7e2f68d..fe224ce 100644 --- a/src/Moat/Types.hs +++ b/src/Moat/Types.hs @@ -140,7 +140,9 @@ data MoatData -- | The tags of the struct. See 'Tag'. -- -- Only used by the Swift backend. - structTags :: [MoatType] + structTags :: [MoatType], + -- | See 'optionalExpand' for understanding this parameter + structOptionalExpand :: Bool } | -- | An enum, sum, or coproduct type MoatEnum @@ -177,7 +179,9 @@ data MoatData -- | The tags of the struct. See 'Tag'. -- -- Only used by the Swift backend. - enumTags :: [MoatType] + enumTags :: [MoatType], + -- | See 'optionalExpand' for understanding this parameter + enumOptionalExpand :: Bool } | -- | A newtype. -- Kotlin backend: becomes a value class. @@ -188,7 +192,9 @@ data MoatData newtypeField :: (String, MoatType), newtypeInterfaces :: [Interface], newtypeProtocols :: [Protocol], -- TODO: remove this? - newtypeAnnotations :: [Annotation] + newtypeAnnotations :: [Annotation], + -- | See 'optionalExpand' for understanding this parameter + newtypeOptionalExpand :: Bool } | -- | A /top-level/ type alias MoatAlias @@ -197,7 +203,9 @@ data MoatData -- | the type variables of the type alias aliasTyVars :: [String], -- | the type this represents (RHS) - aliasTyp :: MoatType + aliasTyp :: MoatType, + -- | See 'optionalExpand' for understanding this parameter + aliasOptionalExpand :: Bool } deriving stock (Eq, Read, Show, Generic) diff --git a/test/BasicRecordWithExpandOptionalSpec.hs b/test/BasicRecordWithExpandOptionalSpec.hs new file mode 100644 index 0000000..c014894 --- /dev/null +++ b/test/BasicRecordWithExpandOptionalSpec.hs @@ -0,0 +1,25 @@ +module BasicRecordWithExpandOptionalSpec where + +import Common +import Moat +import Moat.Types (Options (..)) +import Test.Hspec +import Test.Hspec.Golden + +data Data = Data + { field0 :: Int, + field1 :: Maybe Int + } + +mobileGenWith + defaultOptions {optionalExpand = True} + ''Data + +spec :: Spec +spec = + describe "stays golden" $ do + let moduleName = "BasicRecordWithExpandOptionalSpec" + it "swift" $ + defaultGolden ("swift" <> moduleName) (showSwift @Data) + it "kotlin" $ + defaultGolden ("kotlin" <> moduleName) (showKotlin @Data)