@@ -94,11 +94,11 @@ import Safe
9494-- custom migration tags in the changelog, as generated by
9595-- 'generateMigrationKind'.
9696
97- migrateDataDump :: (Read db , Read rec , Read fld )
97+ migrateDataDump :: (Read db , Read rec , Read alt , Read fld )
9898 => (API , Version ) -- ^ Starting schema and version
9999 -> (API , VersionExtra ) -- ^ Ending schema and version
100100 -> APIChangelog -- ^ Log of changes, containing both versions
101- -> CustomMigrations JS. Object JS. Value db rec fld -- ^ Custom migration functions
101+ -> CustomMigrations JS. Object JS. Value db rec alt fld -- ^ Custom migration functions
102102 -> TypeName -- ^ Name of the dataset's type
103103 -> DataChecks -- ^ How thoroughly to validate changes
104104 -> JS. Value -- ^ Dataset to be migrated
@@ -110,11 +110,11 @@ migrateDataDump startApi endApi changelog custom root chks db = do
110110 db' <- applyChangesToDatabase root custom' db changes ?!? uncurry ValueError
111111 return (db', warnings)
112112
113- migrateDataDump' :: (Read db , Read rec , Read fld )
113+ migrateDataDump' :: (Read db , Read rec , Read alt , Read fld )
114114 => (API , Version ) -- ^ Starting schema and version
115115 -> (API , VersionExtra ) -- ^ Ending schema and version
116116 -> APIChangelog -- ^ Log of changes, containing both versions
117- -> CustomMigrations Record Value db rec fld -- ^ Custom migration functions
117+ -> CustomMigrations Record Value db rec alt fld -- ^ Custom migration functions
118118 -> TypeName -- ^ Name of the dataset's type
119119 -> DataChecks -- ^ How thoroughly to validate changes
120120 -> Value. Value -- ^ Dataset to be migrated
@@ -129,36 +129,41 @@ migrateDataDump' startApi endApi changelog custom root chks db = do
129129
130130
131131-- | Custom migrations used in the changelog must be implemented in
132- -- Haskell, and supplied in this record. There are three kinds:
132+ -- Haskell, and supplied in this record. There are four kinds:
133133--
134134-- * Whole-database migrations, which may arbitrarily change the API
135135-- schema and the data to match;
136136--
137- -- * Type migrations, which may change the schema of a single type; and
137+ -- * Type migrations, which may change the schema of a single type;
138+ --
139+ -- * Union alternative migrations, which may change the type of a
140+ -- single alternative within a union (with the new type specified in
141+ -- the changelog); and
138142--
139143-- * Single field migrations, which may change only the type of the
140144-- field (with the new type specified in the changelog).
141145--
142146-- For database and type migrations, if the schema is unchanged, the
143147-- corresponding function should return 'Nothing'.
144148--
145- -- The @db@, @ty@ and @fld@ parameters should be instantiated with
146- -- the enumeration types generated by 'generateMigrationKinds', which
147- -- correspond to the exact set of custom migration tags used in the
148- -- changelog.
149- data CustomMigrations o v db ty fld = CustomMigrations
149+ -- The @db@, @ty@, @alt@ and @fld@ parameters should be instantiated
150+ -- with the enumeration types generated by 'generateMigrationKinds',
151+ -- which correspond to the exact set of custom migration tags used in
152+ -- the changelog.
153+ data CustomMigrations o v db ty alt fld = CustomMigrations
150154 { databaseMigration :: db -> o -> Either ValueError o
151155 , databaseMigrationSchema :: db -> NormAPI -> Either ApplyFailure (Maybe NormAPI )
152156 , typeMigration :: ty -> v -> Either ValueError v
153157 , typeMigrationSchema :: ty -> NormTypeDecl -> Either ApplyFailure (Maybe NormTypeDecl )
158+ , unionAltMigration :: alt -> v -> Either ValueError v
154159 , fieldMigration :: fld -> v -> Either ValueError v }
155160
156- type CustomMigrationsTagged o v = CustomMigrations o v MigrationTag MigrationTag MigrationTag
161+ type CustomMigrationsTagged o v = CustomMigrations o v MigrationTag MigrationTag MigrationTag MigrationTag
157162
158- readCustomMigrations :: (Read db , Read ty , Read fld )
159- => CustomMigrations o v db ty fld -> CustomMigrationsTagged o v
160- readCustomMigrations (CustomMigrations db dbs r rs f) =
161- CustomMigrations (db . read ) (dbs . read ) (r . read ) (rs . read ) (f . read )
163+ readCustomMigrations :: (Read db , Read ty , Read alt , Read fld )
164+ => CustomMigrations o v db ty alt fld -> CustomMigrationsTagged o v
165+ readCustomMigrations (CustomMigrations db dbs r rs a f) =
166+ CustomMigrations (db . read ) (dbs . read ) (r . read ) (rs . read ) (a . read ) ( f . read )
162167
163168-- | Lift a custom record migration to work on arbitrary values
164169mkRecordMigration :: (JS. Object -> Either ValueError JS. Object )
@@ -200,10 +205,11 @@ data DataChecks = NoChecks -- ^ Not at all
200205
201206-- | Whether to validate the dataset after this change
202207validateAfter :: DataChecks -> APIChange -> Bool
203- validateAfter chks (ChChangeField {}) = chks >= CheckCustom
204- validateAfter chks (ChCustomType {}) = chks >= CheckCustom
205- validateAfter chks (ChCustomAll {}) = chks >= CheckCustom
206- validateAfter chks _ = chks >= CheckAll
208+ validateAfter chks (ChChangeField {}) = chks >= CheckCustom
209+ validateAfter chks (ChChangeUnionAlt {}) = chks >= CheckCustom
210+ validateAfter chks (ChCustomType {}) = chks >= CheckCustom
211+ validateAfter chks (ChCustomAll {}) = chks >= CheckCustom
212+ validateAfter chks _ = chks >= CheckAll
207213
208214
209215--------------------
@@ -243,22 +249,23 @@ isChangelogOrdered changelog =
243249
244250
245251-- | Sets of custom migration tags in the changelog for
246- -- whole-database, single-record and single-field migrations
247- changelogTags :: APIChangelog -> (Set MigrationTag , Set MigrationTag , Set MigrationTag )
248- changelogTags (ChangesStart _) = (Set. empty, Set. empty, Set. empty)
252+ -- whole-database, single-type, union-alternative and single-field migrations
253+ changelogTags :: APIChangelog -> (Set MigrationTag , Set MigrationTag , Set MigrationTag , Set MigrationTag )
254+ changelogTags (ChangesStart _) = (Set. empty, Set. empty, Set. empty, Set. empty )
249255changelogTags (ChangesUpTo _ cs older) =
250- unions3 (map changeTags cs) `union3 ` changelogTags older
256+ unions4 (map changeTags cs) `union4 ` changelogTags older
251257 where
252- union3 (a, b, c) (x, y, z) = (a `Set.union` x, b `Set.union` y, c `Set.union` z)
253- unions3 xyzs = (Set. unions xs, Set. unions ys, Set. unions zs)
254- where (xs, ys, zs) = unzip3 xyzs
258+ union4 (a, b, c, d ) (x, y, z, w ) = (a `Set.union` x, b `Set.union` y, c `Set.union` z, d `Set.union` w )
259+ unions4 xyzws = (Set. unions xs, Set. unions ys, Set. unions zs, Set. unions ws )
260+ where (xs, ys, zs, ws ) = unzip4 xyzws
255261
256262-- | Sets of custom migration tags in a single change
257- changeTags :: APIChange -> (Set MigrationTag , Set MigrationTag , Set MigrationTag )
258- changeTags (ChChangeField _ _ _ t) = (Set. empty, Set. empty, Set. singleton t)
259- changeTags (ChCustomType _ t) = (Set. empty, Set. singleton t, Set. empty)
260- changeTags (ChCustomAll t) = (Set. singleton t, Set. empty, Set. empty)
261- changeTags _ = (Set. empty, Set. empty, Set. empty)
263+ changeTags :: APIChange -> (Set MigrationTag , Set MigrationTag , Set MigrationTag , Set MigrationTag )
264+ changeTags (ChChangeField _ _ _ t) = (Set. empty, Set. empty, Set. empty, Set. singleton t)
265+ changeTags (ChChangeUnionAlt _ _ _ t) = (Set. empty, Set. empty, Set. singleton t, Set. empty)
266+ changeTags (ChCustomType _ t) = (Set. empty, Set. singleton t, Set. empty, Set. empty)
267+ changeTags (ChCustomAll t) = (Set. singleton t, Set. empty, Set. empty, Set. empty)
268+ changeTags _ = (Set. empty, Set. empty, Set. empty, Set. empty)
262269
263270
264271--------------------------------
@@ -304,11 +311,11 @@ findUpdatePos tname api = Map.alter (Just . UpdateHere) tname $
304311
305312-- | Check that a changelog adequately describes how to migrate from
306313-- one version to another.
307- validateChanges :: (Read db , Read rec , Read fld )
314+ validateChanges :: (Read db , Read rec , Read alt , Read fld )
308315 => (API , Version ) -- ^ Starting schema and version
309316 -> (API , VersionExtra ) -- ^ Ending schema and version
310317 -> APIChangelog -- ^ Changelog to be validated
311- -> CustomMigrations o v db rec fld -- ^ Custom migration functions
318+ -> CustomMigrations o v db rec alt fld -- ^ Custom migration functions
312319 -> TypeName -- ^ Name of the dataset's type
313320 -> DataChecks -- ^ How thoroughly to validate changes
314321 -> Either ValidateFailure [ValidateWarning ]
@@ -484,6 +491,14 @@ applyAPIChangeToAPI _ _ (ChRenameUnionAlt tname fname fname') api = do
484491 . Map. delete fname) unioninfo
485492 return (Map. insert tname tinfo' api, findUpdatePos tname api)
486493
494+ applyAPIChangeToAPI _ _custom (ChChangeUnionAlt tname fname ftype _tag) api = do
495+ tinfo <- lookupType tname api
496+ unioninfo <- expectUnionType tinfo ?! TypeWrongKind tname TKUnion
497+ guard (Map. member fname unioninfo) ?! FieldDoesNotExist tname TKUnion fname
498+ typeIsValid ftype api ?!? TypeMalformed ftype
499+ let tinfo' = (NUnionType . Map. insert fname ftype) unioninfo
500+ return (Map. insert tname tinfo' api, findUpdatePos tname api)
501+
487502applyAPIChangeToAPI _ _ (ChAddEnumVal tname fname) api = do
488503 tinfo <- lookupType tname api
489504 enuminfo <- expectEnumType tinfo ?! TypeWrongKind tname TKEnum
@@ -607,6 +622,13 @@ applyChangeToData (ChRenameUnionAlt _ fname fname') _ = withObject $ \un p ->
607622 | otherwise -> return un
608623 Nothing -> Left (JSONError $ SyntaxError " Not singleton" , p)
609624
625+ applyChangeToData (ChChangeUnionAlt _ fname _ftype tag) custom = withObject $ \ un p ->
626+ case matchSingletonObject un of
627+ Just (k, r) | k == _FieldName fname -> do
628+ r' <- liftMigration (unionAltMigration custom tag) r p
629+ return $ singletonObject (_FieldName fname) r'
630+ _ -> return un
631+
610632applyChangeToData (ChRenameEnumVal _ fname fname') _ = withString $ \ s _ ->
611633 if s == _FieldName fname then return (_FieldName fname')
612634 else return s
@@ -718,6 +740,12 @@ applyChangeToData' _ (ChRenameUnionAlt _ fname fname') _ v p = do
718740 (fn, v') <- expectUnion v p
719741 pure $! if fn == fname then Union fname' v' else v
720742
743+ applyChangeToData' _ (ChChangeUnionAlt _ fname _ftype tag) custom v p = do
744+ (fn, v') <- expectUnion v p
745+ if fn == fname
746+ then Union fn <$!> liftMigration (unionAltMigration custom tag) v' (inField fn: p)
747+ else pure v
748+
721749applyChangeToData' _ (ChRenameEnumVal _ fname fname') _ v p = do
722750 fn <- expectEnum v p
723751 pure $! if fn == fname then Enum fname' else v
@@ -864,17 +892,21 @@ type Decode t = JS.Value -> Either [(JSONError, Position)] t
864892
865893-- | Generate enumeration datatypes corresponding to the custom
866894-- migrations used in an API migration changelog.
867- generateMigrationKinds :: APIChangelog -> String -> String -> String -> Q [Dec ]
868- generateMigrationKinds changes all_nm rec_nm fld_nm = do
895+ generateMigrationKinds :: APIChangelog -> String -> String -> String -> String -> Q [Dec ]
896+ generateMigrationKinds changes all_nm rec_nm alt_nm fld_nm = do
869897 guardNoDups (all_tags `Set.intersection` rec_tags)
898+ guardNoDups (all_tags `Set.intersection` alt_tags)
870899 guardNoDups (all_tags `Set.intersection` fld_tags)
900+ guardNoDups (rec_tags `Set.intersection` alt_tags)
871901 guardNoDups (rec_tags `Set.intersection` fld_tags)
902+ guardNoDups (alt_tags `Set.intersection` fld_tags)
872903
873904 return [ mkDataD [] (mkName all_nm) [] (cons all_nm all_tags) derivs
874905 , mkDataD [] (mkName rec_nm) [] (cons rec_nm rec_tags) derivs
906+ , mkDataD [] (mkName alt_nm) [] (cons alt_nm alt_tags) derivs
875907 , mkDataD [] (mkName fld_nm) [] (cons fld_nm fld_tags) derivs ]
876908 where
877- (all_tags, rec_tags, fld_tags) = changelogTags changes
909+ (all_tags, rec_tags, alt_tags, fld_tags) = changelogTags changes
878910
879911 guardNoDups xs
880912 | Set. null xs = return ()
0 commit comments