1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
|
/*
* Copyright 2022 Google, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.gradle_recipe.converter.recipe
import com.github.rising3.semver.SemVer
import com.google.android.gradle_recipe.converter.converters.AgpVersion
import com.google.android.gradle_recipe.converter.converters.FullAgpVersion
import com.google.android.gradle_recipe.converter.converters.RecipeConverter
import com.google.android.gradle_recipe.converter.converters.ShortAgpVersion
import com.google.android.gradle_recipe.converter.converters.ShortAgpVersion.Companion.isShortVersion
import com.google.android.gradle_recipe.converter.converters.getVersionsFromAgp
import com.google.android.gradle_recipe.converter.printErrorAndTerminate
import org.tomlj.Toml
import org.tomlj.TomlParseResult
import java.io.File
import java.nio.file.Path
import kotlin.io.path.name
const val RECIPE_METADATA_FILE = "recipe_metadata.toml"
/**
* Recipe Data representing the content of `recipe_metadata.toml`
*/
class RecipeData private constructor(
/** The name of the recipe to show in the index */
val indexName: String,
/** the name of the folder that should contain the recipe */
val destinationFolder: String,
val minAgpVersion: FullAgpVersion,
val maxAgpVersion: ShortAgpVersion?,
val tasks: List<String>,
val validationTasks: List<String>?,
val keywords: List<String>,
) {
fun isCompliantWithAgp(agpVersion: FullAgpVersion): Boolean {
val min = minAgpVersion
val max = maxAgpVersion
return if (max != null) {
agpVersion >= min && agpVersion.toShort() <= max
} else {
// when maxAgpVersion is not specified
agpVersion >= min
}
}
companion object {
fun loadFrom(recipeFolder: Path, mode: RecipeConverter.Mode): RecipeData {
val toml = recipeFolder.resolve(RECIPE_METADATA_FILE)
val parseResult: TomlParseResult = Toml.parse(toml)
if (parseResult.hasErrors()) {
System.err.println("TOML Parsing error(s) for $toml:")
parseResult.errors().forEach { error -> System.err.println(error.toString()) }
printErrorAndTerminate("Unable to read $toml")
}
val indexName = if (mode == RecipeConverter.Mode.RELEASE) {
val entry = parseResult.getString("indexName")
if (entry.isNullOrBlank()) {
recipeFolder.name
} else {
entry
}
} else {
recipeFolder.name
}
val destinationFolder = if (mode == RecipeConverter.Mode.RELEASE) {
val entry = parseResult.getString("destinationFolder")
if (entry.isNullOrBlank()) {
recipeFolder.name
} else {
// check there's no path separator in there
if (entry.contains('/')) {
printErrorAndTerminate("destinationFolder value ('$entry') cannot contain / character ($recipeFolder)")
}
entry
}
} else {
recipeFolder.name
}
val minAgpString = parseResult.getString("agpVersion.min")
?: printErrorAndTerminate("Did not find mandatory 'agpVersion.min' in $toml")
val minAgpVersion = if (minAgpString.isShortVersion()) {
getVersionsFromAgp(ShortAgpVersion.of(minAgpString))?.agp
?: printErrorAndTerminate("Unable to get published AGP version from '$minAgpString'")
} else {
FullAgpVersion.of(minAgpString)
}
return RecipeData(
indexName = indexName,
destinationFolder = destinationFolder,
minAgpVersion = minAgpVersion,
maxAgpVersion = parseResult.getString("agpVersion.max")?.let { ShortAgpVersion.of(it) },
tasks = parseResult.getArray("gradleTasks.tasks")?.toList()?.map { it as String } ?: emptyList(),
validationTasks = parseResult.getArray("gradleTasks.validationTasks")?.toList()?.map { it as String },
keywords = parseResult.getArray("indexMetadata.index")?.toList()?.map { it as String } ?: emptyList()
)
}
}
}
fun String.toMajorMinor(): String = SemVer.parse(this).run { "${this.major}.${this.minor}" }
fun isRecipeFolder(folder: Path) = File(folder.toFile(), RECIPE_METADATA_FILE).exists()
|