Skip to content

Commit 10c8238

Browse files
Merge pull request #89 from MercuryTechnologies/joseph/add-deprecated-fields
Add deprecated fields support
2 parents 1b4217b + f5abee8 commit 10c8238

File tree

8 files changed

+129
-27
lines changed

8 files changed

+129
-27
lines changed
+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
data class Data(
2+
val field0: Int,
3+
// Deprecated since build 500
4+
// val field1: Int? = null,
5+
)
+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
struct Data {
2+
var field0: Int
3+
// Deprecated since build 500
4+
// var field1: Int?
5+
}

moat.cabal

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
cabal-version: 1.12
22

3-
-- This file has been generated from package.yaml by hpack version 0.35.2.
3+
-- This file has been generated from package.yaml by hpack version 0.37.0.
44
--
55
-- see: https://github.com/sol/hpack
66

@@ -80,6 +80,7 @@ test-suite spec
8080
BasicNewtypeWithEitherFieldSpec
8181
BasicRecordSpec
8282
Common
83+
DeprecatedFieldSpec
8384
DuplicateRecordFieldSpec
8485
EnumValueClassDocSpec
8586
EnumValueClassSpec

src/Moat.hs

+35-10
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ module Moat
7474
omitFields,
7575
omitCases,
7676
fieldsRequiredByClients,
77+
deprecatedFields,
7778
strictCases,
7879
makeBase,
7980
sumOfProductEncodingOptions,
@@ -509,6 +510,10 @@ data MoatError
509510
}
510511
| MissingRequiredFields
511512
{ _missingFields :: [String]
513+
, _missingDeprecatedFields :: [String]
514+
}
515+
| MissingDeprecatedRequiredFields
516+
{ _missingDeprecatedRequiredFields :: [String]
512517
}
513518
| MissingStrictCases
514519
{ _missingCases :: [String]
@@ -577,8 +582,10 @@ prettyMoatError = \case
577582
ImproperNewtypeConstructorInfo conInfo ->
578583
"Expected `ConstructorInfo` with single field, but got "
579584
++ show conInfo
580-
MissingRequiredFields missingFields ->
581-
"These fields are required by clients: " ++ L.unwords missingFields
585+
MissingRequiredFields missingFields missingDeprecatedFields ->
586+
"These fields are required by clients: " ++ L.unwords missingFields ++ " " ++ L.unwords missingDeprecatedFields
587+
MissingDeprecatedRequiredFields missingDeprecatedFields ->
588+
"These fields need to be added to the required field list due to being necessary on older clients: " ++ L.unwords missingDeprecatedFields
582589
MissingStrictCases missingCases ->
583590
"Removing these cases will break clients: " ++ L.unwords missingCases
584591

@@ -975,7 +982,8 @@ mkNewtype o@Options {..} typName doc instTys ts = \case
975982
} -> do
976983
fieldDocs <- lift $ mapM (getDocWith o) fieldNames
977984
fields <- zipFields o fieldNames constructorFields fieldDocs
978-
matchProxy =<< lift (structExp typName doc instTys dataInterfaces dataProtocols dataAnnotations fields ts makeBase)
985+
deprecatedFieldExps <- lift [e|deprecatedFields|]
986+
matchProxy =<< lift (structExp typName doc instTys dataInterfaces dataProtocols dataAnnotations fields deprecatedFieldExps ts makeBase)
979987
ConstructorInfo
980988
{ constructorFields = [field]
981989
} -> do
@@ -1004,7 +1012,8 @@ mkProd o@Options {..} typName parentDoc instTys ts = \case
10041012
{ constructorVariant = NormalConstructor
10051013
, constructorFields = []
10061014
} -> do
1007-
matchProxy =<< lift (structExp typName parentDoc instTys dataInterfaces dataProtocols dataAnnotations [] ts makeBase)
1015+
emptyDeprecatedFieldsExp <- lift [e|[]|]
1016+
matchProxy =<< lift (structExp typName parentDoc instTys dataInterfaces dataProtocols dataAnnotations [] emptyDeprecatedFieldsExp ts makeBase)
10081017
-- single constructor, non-record (Normal)
10091018
ConstructorInfo
10101019
{ constructorVariant = NormalConstructor
@@ -1026,17 +1035,30 @@ mkProd o@Options {..} typName parentDoc instTys ts = \case
10261035
} -> do
10271036
fieldDocs <- lift $ mapM (getDocWith o) fieldNames
10281037
fields <- zipFields o fieldNames constructorFields fieldDocs
1029-
matchProxy =<< lift (structExp typName parentDoc instTys dataInterfaces dataProtocols dataAnnotations fields ts makeBase)
1038+
deprecatedFieldExp <- lift [e|deprecatedFields|]
1039+
matchProxy =<< lift (structExp typName parentDoc instTys dataInterfaces dataProtocols dataAnnotations fields deprecatedFieldExp ts makeBase)
10301040

10311041
-- | 'strictFields' are required to exist in the record and are always included.
10321042
-- 'omitFields' will remove any remaining fields if they are 'Discard'ed.
10331043
zipFields :: Options -> [Name] -> [Type] -> [Maybe String] -> MoatM [Exp]
10341044
zipFields o ns ts ds = do
10351045
let fields = nameStr <$> ns
1036-
missingFields = fieldsRequiredByClients o L.\\ fields
1037-
if null missingFields
1038-
then pure $ catMaybes $ zipWith3 mkField ns ts ds
1039-
else throwError $ MissingRequiredFields missingFields
1046+
requiredFieldNames = fieldsRequiredByClients o
1047+
deprecatedFieldNames = fst <$> deprecatedFields o
1048+
missingFields = requiredFieldNames L.\\ fields
1049+
missingDeprecatedFields = deprecatedFieldNames L.\\ fields
1050+
deprecatedNonRequiredFields = deprecatedFieldNames L.\\ requiredFieldNames
1051+
checkMissingFields =
1052+
if null missingFields
1053+
then pure $ catMaybes $ zipWith3 mkField ns ts ds
1054+
else throwError $ MissingRequiredFields missingFields missingDeprecatedFields
1055+
case requiredFieldNames of
1056+
[] -> checkMissingFields
1057+
_requiredFields ->
1058+
-- Throw an error if required fields are available but a deprecated field is not included in them
1059+
case deprecatedNonRequiredFields of
1060+
[] -> checkMissingFields
1061+
_xs -> throwError $ MissingDeprecatedRequiredFields deprecatedNonRequiredFields
10401062
where
10411063
mkField :: Name -> Type -> Maybe String -> Maybe Exp
10421064
mkField n t d =
@@ -1650,12 +1672,14 @@ structExp ::
16501672
[Annotation] ->
16511673
-- | fields
16521674
[Exp] ->
1675+
-- | deprecated fields
1676+
Exp ->
16531677
-- | tags
16541678
[Exp] ->
16551679
-- | Make base?
16561680
(Bool, Maybe MoatType, [Protocol]) ->
16571681
Q Exp
1658-
structExp name doc tyVars ifaces protos anns fields tags bs = do
1682+
structExp name doc tyVars ifaces protos anns fields deprecatedFields tags bs = do
16591683
structInterfaces_ <- Syntax.lift ifaces
16601684
structAnnotations_ <- Syntax.lift anns
16611685
structProtocols_ <- Syntax.lift protos
@@ -1669,6 +1693,7 @@ structExp name doc tyVars ifaces protos anns fields tags bs = do
16691693
, ('structProtocols, structProtocols_)
16701694
, ('structAnnotations, structAnnotations_)
16711695
, ('structFields, ListE fields)
1696+
, ('structDeprecatedFields, deprecatedFields)
16721697
, ('structPrivateTypes, ListE [])
16731698
, ('structTags, ListE tags)
16741699
]

src/Moat/Pretty/Kotlin.hs

+25-8
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ where
66
import qualified Data.Char as Char
77
import Data.Functor ((<&>))
88
import Data.List (intercalate)
9+
import qualified Data.Map as Map
910
import Data.Maybe (catMaybes, mapMaybe)
11+
import Debug.Trace
1012
import Moat.Pretty.Doc.KDoc
1113
import Moat.Types
1214

@@ -25,6 +27,7 @@ prettyKotlinData = \case
2527
structInterfaces
2628
structAnnotations
2729
structFields
30+
structDeprecatedFields
2831
indents
2932
MoatEnum {..} ->
3033
prettyEnum
@@ -64,11 +67,11 @@ prettyTypeDoc indents doc fields =
6467
kdoc = intercalate "\n" (catMaybes [prettyDoc wrap <$> doc, prettyFieldDoc wrap fields])
6568
in prettyDocComment wrap indents kdoc
6669

67-
prettyStructFields :: String -> [Field] -> String
68-
prettyStructFields indents = go
70+
prettyStructFields :: String -> [Field] -> [(String, Maybe String)] -> String
71+
prettyStructFields indents fields deprecatedFields = go fields
6972
where
70-
go [] = ""
71-
go (Field fieldName ty _ : fs) =
73+
deprecatedFieldsMap = Map.fromList deprecatedFields
74+
prettyField (Field fieldName ty _) =
7275
indents
7376
++ "val "
7477
++ fieldName
@@ -78,7 +81,20 @@ prettyStructFields indents = go
7881
Optional _ -> " = null"
7982
_ -> ""
8083
++ ",\n"
81-
++ go fs
84+
go [] = ""
85+
go (field@(Field fieldName _ _) : fs) =
86+
traceShow deprecatedFieldsMap $
87+
traceShow fieldName $
88+
traceShow fields $
89+
traceShow fs $
90+
case Map.lookup fieldName deprecatedFieldsMap of
91+
Just mComment ->
92+
traceShow "test" $
93+
maybe "" (\comment -> "// " ++ comment ++ "\n") mComment
94+
++ "//"
95+
++ prettyField field
96+
++ go fs
97+
Nothing -> prettyField field ++ go fs
8298

8399
prettyEnumCases :: String -> [EnumCase] -> String
84100
prettyEnumCases indents = go
@@ -295,7 +311,7 @@ prettyTaggedObject parentName tyVars anns ifaces cases indents SumOfProductEncod
295311
++ "data class "
296312
++ caseTypeHeader caseNm
297313
++ "(\n"
298-
++ prettyStructFields doubleIndents fields
314+
++ prettyStructFields doubleIndents fields []
299315
++ indents
300316
++ ") : "
301317
++ parentTypeHeader
@@ -350,9 +366,10 @@ prettyStruct ::
350366
-- | fields
351367
[Field] ->
352368
-- | indents
369+
[(String, Maybe String)] ->
353370
String ->
354371
String
355-
prettyStruct name doc tyVars ifaces anns fields indents =
372+
prettyStruct name doc tyVars ifaces anns fields deprecatedFields indents =
356373
prettyTypeDoc noIndent doc fields
357374
++ prettyAnnotations Nothing noIndent anns
358375
++ body
@@ -368,7 +385,7 @@ prettyStruct name doc tyVars ifaces anns fields indents =
368385
"data class "
369386
++ prettyMoatTypeHeader name (addTyVarBounds tyVars ifaces)
370387
++ "(\n"
371-
++ prettyStructFields indents fields
388+
++ prettyStructFields indents fields deprecatedFields
372389
++ ")"
373390

374391
prettyEnum ::

src/Moat/Pretty/Swift.hs

+19-8
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ where
1010

1111
import Data.Functor ((<&>))
1212
import Data.List (intercalate, nub)
13+
import qualified Data.Map as Map
1314
import Data.Maybe (catMaybes)
1415
import Moat.Pretty.Doc.DocC
1516
import Moat.Types
@@ -52,7 +53,7 @@ prettySwiftDataWith indent = \case
5253
++ prettyRawValueAndProtocols Nothing structProtocols
5354
++ " {"
5455
++ newlineNonEmpty structFields
55-
++ prettyStructFields indents structFields
56+
++ prettyStructFields indents structFields structDeprecatedFields
5657
++ newlineNonEmpty structPrivateTypes
5758
++ prettyPrivateTypes indents structPrivateTypes
5859
++ prettyTags indents structTags
@@ -260,19 +261,29 @@ prettyEnumCases indents unknown cases = go cases ++ unknownCase
260261
Just caseNm -> indents ++ "case " ++ caseNm ++ "\n"
261262
Nothing -> ""
262263

263-
prettyStructFields :: String -> [Field] -> String
264-
prettyStructFields indents = go
264+
prettyStructFields :: String -> [Field] -> [(String, Maybe String)] -> String
265+
prettyStructFields indents fields deprecatedFields = go fields
265266
where
266-
go [] = ""
267-
go (Field {..} : fs) =
268-
prettyTypeDoc indents fieldDoc []
269-
++ indents
267+
deprecatedFieldsMap = Map.fromList deprecatedFields
268+
prettyField (Field fieldName fieldType _fieldDoc) =
269+
indents
270270
++ "var "
271271
++ fieldName
272272
++ ": "
273273
++ prettyMoatType fieldType
274274
++ "\n"
275-
++ go fs
275+
go [] = ""
276+
go (field@(Field fieldName _ fieldDoc) : fs) =
277+
case Map.lookup fieldName deprecatedFieldsMap of
278+
Just mComment ->
279+
maybe "" (\comment -> "// " ++ comment ++ "\n") mComment
280+
++ "//"
281+
++ prettyField field
282+
++ go fs
283+
Nothing ->
284+
prettyTypeDoc indents fieldDoc []
285+
++ prettyField field
286+
++ go fs
276287

277288
prettyNewtypeField :: String -> Field -> String -> String
278289
prettyNewtypeField indents (Field alias fieldType _) fieldName =

src/Moat/Types.hs

+9
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ data MoatData
144144
-- populated by setting 'makeBase'.
145145
--
146146
-- Only used by the Swift backend.
147+
, structDeprecatedFields :: [(String, Maybe String)]
147148
, structTags :: [MoatType]
148149
-- ^ The tags of the struct. See 'Tag'.
149150
--
@@ -418,6 +419,13 @@ data Options = Options
418419
--
419420
-- This can be used with @omitFields = const Discard@ to ensure fields are
420421
-- retained for client compatibility.
422+
, deprecatedFields :: [(String, Maybe String)]
423+
-- ^ These fields are deprecated for clients and a comment with details about the deprecation
424+
-- deprecated fields are also required in Haskell
425+
--
426+
-- This field will generate a mobile type as a comment instead of actual code
427+
-- and add the specified comment to the resulting type
428+
-- The purpose of this field is to allow fields to be no longer generated
421429
, strictCases :: [String]
422430
-- ^ These enum cases are relied upon and must exist in the sum type.
423431
--
@@ -574,6 +582,7 @@ defaultOptions =
574582
, omitFields = const Keep
575583
, omitCases = const Keep
576584
, fieldsRequiredByClients = []
585+
, deprecatedFields = []
577586
, strictCases = []
578587
, makeBase = (False, Nothing, [])
579588
, optionalExpand = False

test/DeprecatedFieldSpec.hs

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
module DeprecatedFieldSpec where
2+
3+
import Common
4+
import Moat
5+
import Test.Hspec
6+
import Test.Hspec.Golden
7+
8+
data Data = Data
9+
{ field0 :: Int
10+
, field1 :: Maybe Int
11+
}
12+
13+
mobileGenWith
14+
( defaultOptions
15+
{ fieldsRequiredByClients = ["field0", "field1"]
16+
, omitFields = const Discard
17+
, deprecatedFields = [("field1", Just "Deprecated since build 500")]
18+
}
19+
)
20+
''Data
21+
22+
spec :: Spec
23+
spec =
24+
fdescribe "stays golden" $ do
25+
let moduleName = "DeprecatedFieldSpec"
26+
it "swift" $
27+
defaultGolden ("swift" <> moduleName) (showSwift @Data)
28+
it "kotlin" $
29+
defaultGolden ("kotlin" <> moduleName) (showKotlin @Data)

0 commit comments

Comments
 (0)