1 /*
<lambda>null2  * Copyright 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.datastore.core
18 
19 /** Returns an initializer function created from a list of DataMigrations. */
20 internal class DataMigrationInitializer<T>() {
21     companion object {
22         /**
23          * Creates an initializer from DataMigrations for use with DataStore.
24          *
25          * @param migrations A list of migrations that will be included in the initializer.
26          * @return The initializer which includes the data migrations returned from the factory
27          *   functions.
28          */
29         fun <T> getInitializer(
30             migrations: List<DataMigration<T>>
31         ): suspend (api: InitializerApi<T>) -> Unit = { api -> runMigrations(migrations, api) }
32 
33         private suspend fun <T> runMigrations(
34             migrations: List<DataMigration<T>>,
35             api: InitializerApi<T>
36         ) {
37             val cleanUps = mutableListOf<suspend () -> Unit>()
38 
39             api.updateData { startingData ->
40                 migrations.fold(startingData) { data, migration ->
41                     if (migration.shouldMigrate(data)) {
42                         cleanUps.add { migration.cleanUp() }
43                         migration.migrate(data)
44                     } else {
45                         data
46                     }
47                 }
48             }
49 
50             var cleanUpFailure: Throwable? = null
51 
52             cleanUps.forEach { cleanUp ->
53                 try {
54                     cleanUp()
55                 } catch (exception: Throwable) {
56                     if (cleanUpFailure == null) {
57                         cleanUpFailure = exception
58                     } else {
59                         cleanUpFailure!!.addSuppressed(exception)
60                     }
61                 }
62             }
63 
64             // If we encountered a failure on cleanup, throw it.
65             cleanUpFailure?.let { throw it }
66         }
67     }
68 }
69