Skip to content

Commit 36292e6

Browse files
endorhvlsi
authored andcommitted
Add required property delegates
Fixes #66 Fixes #65
1 parent f9555bb commit 36292e6

File tree

2 files changed

+142
-22
lines changed

2 files changed

+142
-22
lines changed

plugins/gradle-extensions-plugin/src/main/kotlin/com/github/vlsi/gradle/properties/dsl/ProjectExtensions.kt

Lines changed: 58 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,12 @@ package com.github.vlsi.gradle.properties.dsl
1919
import org.gradle.api.GradleException
2020
import org.gradle.api.Project
2121
import kotlin.properties.ReadOnlyProperty
22-
import kotlin.reflect.KProperty
2322

2423
fun Project.stringProperty(property: String, required: Boolean = false): String? {
2524
val value = project.findProperty(property)
2625
if (value == null) {
2726
if (required) {
28-
throw GradleException("Property $property is not specified")
27+
throw GradleException("Project property '$property' is not specified")
2928
}
3029
logger.debug("Using null value for $property")
3130
return null
@@ -48,41 +47,78 @@ fun String?.toBool(nullAs: Boolean = false, blankAs: Boolean = true, default: Bo
4847
else -> equals("true", ignoreCase = true)
4948
}
5049

50+
fun String?.toBoolOrNull(blankAs: Boolean? = null) = when {
51+
this == null -> null
52+
isBlank() -> blankAs
53+
this == "true" -> true
54+
this == "false" -> false
55+
else -> null
56+
}
57+
5158
val Project.props: PropertyMapper get() = PropertyMapper(this)
5259

5360
class PropertyMapper internal constructor(private val project: Project) {
54-
operator fun invoke(default: Boolean = false) = object : ReadOnlyProperty<Any?, Boolean> {
55-
override fun getValue(thisRef: Any?, property: KProperty<*>): Boolean =
56-
bool(property.name, default = default)
57-
}
61+
operator fun invoke(default: Boolean = false) = delegate { bool(it, default) }
5862

59-
operator fun invoke(default: String) = object : ReadOnlyProperty<Any?, String> {
60-
override fun getValue(thisRef: Any?, property: KProperty<*>): String =
61-
string(property.name, default)
62-
}
63+
operator fun invoke(default: String) = delegate { string(it, default) }
6364

64-
operator fun invoke(default: Int) = object : ReadOnlyProperty<Any?, Int> {
65-
override fun getValue(thisRef: Any?, property: KProperty<*>): Int =
66-
int(property.name, default)
67-
}
65+
operator fun invoke(default: Int) = delegate { int(it, default) }
6866

69-
operator fun invoke(default: Long) = object : ReadOnlyProperty<Any?, Long> {
70-
override fun getValue(thisRef: Any?, property: KProperty<*>): Long =
71-
long(property.name, default)
72-
}
67+
operator fun invoke(default: Long) = delegate { long(it, default) }
68+
69+
val bool get() = delegate { requiredBool(it) }
70+
71+
val string get() = delegate { requiredString(it) }
72+
73+
val int get() = delegate { requiredInt(it) }
74+
75+
val long get() = delegate { requiredLong(it) }
76+
77+
fun requiredString(name: String): String =
78+
project.stringProperty(name, true)!!
79+
80+
fun requiredBool(name: String, blankAs: Boolean? = true) =
81+
requiredString(name).let {
82+
it.toBoolOrNull(blankAs = blankAs)
83+
?: throw GradleException("Project property \"$name\" should be a Boolean (true/false): '$it'")
84+
}
85+
86+
fun requiredInt(name: String) =
87+
requiredString(name).let {
88+
it.toIntOrNull()
89+
?: throw GradleException("Project property \"$name\" should be an Int: '$it'")
90+
}
91+
92+
fun requiredLong(name: String) =
93+
requiredString(name).let {
94+
it.toLongOrNull()
95+
?: throw GradleException("Project property \"$name\" should be a Long: '$it'")
96+
}
7397

7498
fun bool(name: String, default: Boolean = false, nullAs: Boolean = default, blankAs: Boolean = true) =
75-
project.stringProperty(name, false)
99+
project.stringProperty(name)
76100
.toBool(nullAs = nullAs, blankAs = blankAs, default = default)
77101

78102
fun string(name: String, default: String = "") =
79-
project.stringProperty(name, false) ?: default
103+
project.stringProperty(name) ?: default
80104

81105
fun int(name: String, default: Int = 0) =
82-
project.stringProperty(name, false)?.toInt() ?: default
106+
project.stringProperty(name)?.let { value ->
107+
value.toIntOrNull() ?: null.also {
108+
project.logger.debug("Unable to parse project property $name=$value as Int, using default value: $default")
109+
}
110+
} ?: default
83111

84112
fun long(name: String, default: Long = 0) =
85-
project.stringProperty(name, false)?.toLong() ?: default
113+
project.stringProperty(name)?.let { value ->
114+
value.toLongOrNull() ?: null.also {
115+
project.logger.debug("Unable to parse project property $name=$value as Long, using default value: $default")
116+
}
117+
} ?: default
118+
119+
private fun <T> delegate(provider: (String) -> T) = ReadOnlyProperty { _: Any?, property ->
120+
provider(property.name)
121+
}
86122
}
87123

88124
private val yearRegexp = Regex("\\d{4}")

plugins/gradle-extensions-plugin/src/test/kotlin/com/github/vlsi/gradle/properties/PropertyExtensionsTest.kt

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,12 @@
1717
package com.github.vlsi.gradle.properties
1818

1919
import com.github.vlsi.gradle.properties.dsl.props
20+
import com.github.vlsi.gradle.properties.dsl.toBoolOrNull
21+
import org.gradle.api.GradleException
2022
import org.gradle.kotlin.dsl.extra
2123
import org.gradle.testfixtures.ProjectBuilder
2224
import org.junit.jupiter.api.Assertions
25+
import org.junit.jupiter.api.assertThrows
2326
import org.junit.jupiter.params.ParameterizedTest
2427
import org.junit.jupiter.params.provider.ValueSource
2528

@@ -74,4 +77,85 @@ class PropertyExtensionsTest {
7477
"skipJavadoc=='$actual'; val skipJavadoc by p.props('default')"
7578
)
7679
}
80+
81+
@ParameterizedTest
82+
@ValueSource(strings = ["true", "false", "abcd", "", "NOT_SET"])
83+
internal fun requiredBoolProperty(actual: String) {
84+
val p = ProjectBuilder.builder().withName("hello").build()
85+
if (actual != "NOT_SET") {
86+
p.extra["skipJavadoc"] = actual
87+
}
88+
val skipJavadoc by p.props.bool
89+
if (actual == "NOT_SET" || actual.toBoolOrNull(true) == null) {
90+
assertThrows<GradleException>("val skipJavadoc by p.props.bool") {
91+
println("skipJavadoc=='$actual'; Did not throw: $skipJavadoc!")
92+
}
93+
} else {
94+
Assertions.assertEquals(
95+
actual.ifBlank { "true" }.toBoolean(),
96+
skipJavadoc,
97+
"skipJavadoc=='$actual'; val skipJavadoc by p.props.bool"
98+
)
99+
}
100+
}
101+
102+
@ParameterizedTest
103+
@ValueSource(strings = ["true", "false", "abcd", "", "NOT_SET"])
104+
internal fun requiredStringProperty(actual: String) {
105+
val p = ProjectBuilder.builder().withName("hello").build()
106+
if (actual != "NOT_SET") {
107+
p.extra["skipJavadoc"] = actual
108+
}
109+
val skipJavadoc by p.props.string
110+
if (actual == "NOT_SET") {
111+
assertThrows<GradleException>("val skipJavadoc by p.props.string") {
112+
println("skipJavadoc=='$actual'; Did not throw: $skipJavadoc!")
113+
}
114+
} else {
115+
Assertions.assertEquals(
116+
actual,
117+
skipJavadoc,
118+
"skipJavadoc=='$actual'; val skipJavadoc by p.props.string"
119+
)
120+
}
121+
}
122+
123+
@ParameterizedTest
124+
@ValueSource(strings = ["0", "42", "MAX_VALUE", "0x42", "abcd", "", "NOT_SET"])
125+
internal fun requiredNumberProperties(actual: String) {
126+
val p = ProjectBuilder.builder().withName("hello").build()
127+
val value = if (actual == "MAX_VALUE") {
128+
(Int.MAX_VALUE.toLong() + 1L).toString()
129+
} else actual
130+
if (value != "NOT_SET") {
131+
p.extra["intProperty"] = value
132+
p.extra["longProperty"] = value
133+
}
134+
val intProperty by p.props.int
135+
val longProperty by p.props.long
136+
137+
if (actual == "NOT_SET" || actual == "MAX_VALUE" || value.toIntOrNull() == null) {
138+
assertThrows<GradleException>("val intProperty by p.props.int") {
139+
println("skipJavadoc=='$actual'; Did not throw: $intProperty!")
140+
}
141+
} else {
142+
Assertions.assertEquals(
143+
value.toInt(),
144+
intProperty,
145+
"intProperty=='$value'; val intProperty by p.props.int"
146+
)
147+
}
148+
149+
if (actual == "NOT_SET" || value.toLongOrNull() == null) {
150+
assertThrows<GradleException>("val longProperty by p.props.long") {
151+
println("skipJavadoc=='$actual'; Did not throw: $longProperty!")
152+
}
153+
} else {
154+
Assertions.assertEquals(
155+
value.toLong(),
156+
longProperty,
157+
"longProperty=='$value'; val longProperty by p.props.long"
158+
)
159+
}
160+
}
77161
}

0 commit comments

Comments
 (0)