All Downloads are FREE. Search and download functionalities are using the official Maven repository.

foundry.gradle.properties.PropertyResolver.kt Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2024 Slack Technologies, LLC
 *
 * 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
 *
 *    https://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 foundry.gradle.properties

import java.util.concurrent.Callable
import org.gradle.api.Project
import org.gradle.api.provider.Provider
import org.gradle.api.provider.ProviderFactory

/**
 * A property resolver that handles multiple property sources in a hierarchical fashion for
 * [providerFor].
 *
 * This checks in the following order of priority
 * - project-local `local.properties`
 * - project-local `gradle.properties`
 * - root-project `local.properties`
 * - root-project/global `gradle.properties`
 *
 * @property startParameterProperty A provider of a property _only_ contained in the project's start
 *   parameters.
 * @property globalLocalProperty A provider of a property _only_ contained in the root project's
 *   `local.properties`.
 */
public class PropertyResolver(
  private val providers: SimpleProviderFactory,
  private val localPropertyProvider: (String) -> Provider,
  private val localGradlePropertyProvider: (String) -> Provider,
  private val startParameterProperty: (String) -> Provider,
  private val globalLocalProperty: (String) -> Provider,
  private val globalGradleLocalProperty: (String) -> Provider,
) {

  public interface SimpleProviderFactory {
    public fun  provider(callable: Callable): Provider
  }

  /**
   * Returns a "safe" property [Provider] mechanism that handles multiple property sources in a
   * hierarchical fashion.
   *
   * This checks in the following order of priority
   * - project-local `local.properties`
   * - project-local `gradle.properties`
   * - root-project `local.properties`
   * - root-project/global `gradle.properties`
   */
  public fun providerFor(key: String): Provider =
    startParameterProperty(key) // start parameters
      .orElse(localPropertyProvider(key)) // project-local `local.properties`
      .orElse(localGradlePropertyProvider(key)) // project-local `gradle.properties`
      .orElse(globalLocalProperty(key)) // root-project `local.properties`
      .orElse(globalGradleLocalProperty(key)) // root-project/global `gradle.properties`

  public fun booleanValue(key: String, defaultValue: Boolean = false): Boolean {
    return booleanProvider(key, defaultValue).get()
  }

  public fun booleanValue(key: String, defaultValue: Provider): Boolean {
    return booleanProvider(key, defaultValue).get()
  }

  public fun booleanProvider(key: String, defaultValue: Boolean = false): Provider {
    return booleanProvider(key, providers.provider { defaultValue })
  }

  public fun booleanProvider(key: String, defaultValue: Provider): Provider {
    return booleanProvider(key).orElse(defaultValue)
  }

  public fun booleanProvider(key: String): Provider {
    return providerFor(key).mapToBoolean()
  }

  public fun intValue(key: String, defaultValue: Int = -1): Int {
    return intProvider(key, defaultValue).get()
  }

  public fun intValue(key: String, defaultValue: Provider): Int {
    return intProvider(key, defaultValue).get()
  }

  public fun intProvider(key: String, defaultValue: Int = -1): Provider {
    return intProvider(key, providers.provider { defaultValue })
  }

  public fun intProvider(key: String, defaultValue: Provider): Provider {
    return providerFor(key).mapToInt().orElse(defaultValue)
  }

  public fun stringValue(key: String): String {
    return optionalStringValue(key)
      ?: error("No property for '$key' found and no default value was provided.")
  }

  public fun stringValue(key: String, defaultValue: String): String {
    return optionalStringValue(key, defaultValue)
      ?: error("No property for '$key' found and no default value was provided.")
  }

  public fun requiredStringProvider(key: String): Provider {
    return optionalStringProvider(key)
      .orElse(
        providers.provider {
          error("No property for '$key' found and no default value was provided.")
        }
      )
  }

  public fun optionalStringValue(key: String, defaultValue: String? = null): String? {
    return providerFor(key).orNull ?: defaultValue
  }

  public fun optionalStringProvider(
    key: String,
    blankBehavior: BlankBehavior = BlankBehavior.ERROR,
  ): Provider {
    return optionalStringProvider(key, null, blankBehavior)
  }

  public fun optionalStringProvider(
    key: String,
    defaultValue: String? = null,
    blankBehavior: BlankBehavior = BlankBehavior.ERROR,
  ): Provider {
    return providerFor(key)
      .let { defaultValue?.let { providers.provider { defaultValue } } ?: it }
      .filter {
        when (blankBehavior) {
          BlankBehavior.FILTER -> {
            it.isNotBlank()
          }
          BlankBehavior.ERROR -> {
            it.ifBlank { error("Unexpected blank value for property '$key'") }
            true
          }
          BlankBehavior.NONE -> {
            true
          }
        }
      }
  }

  public enum class BlankBehavior {
    FILTER,
    ERROR,
    NONE,
  }

  public companion object {
    /** @property project The project to resolve properties for. */
    public operator fun invoke(
      project: Project,
      startParameterProperty: (String) -> Provider =
        project.providers.emptyStringProvider(),
      globalLocalProperty: (String) -> Provider = project.providers.emptyStringProvider(),
      localPropertyProvider: (String) -> Provider = project::localProperty,
      localGradlePropertyProvider: (String) -> Provider = project::localGradleProperty,
      globalGradleLocalProperty: (String) -> Provider = project.providers::gradleProperty,
    ): PropertyResolver =
      PropertyResolver(
        providers =
          object : SimpleProviderFactory {
            override fun  provider(callable: Callable): Provider {
              return project.providers.provider(callable)
            }
          },
        startParameterProperty = startParameterProperty,
        globalLocalProperty = globalLocalProperty,
        localPropertyProvider = localPropertyProvider,
        localGradlePropertyProvider = localGradlePropertyProvider,
        globalGradleLocalProperty = globalGradleLocalProperty,
      )

    /** @property project The project to resolve properties for. */
    public operator fun invoke(
      project: Project,
      globalResolver: PropertyResolver,
      localPropertyProvider: (String) -> Provider = project::localProperty,
      localGradlePropertyProvider: (String) -> Provider = project::localGradleProperty,
    ): PropertyResolver =
      PropertyResolver(
        providers =
          object : SimpleProviderFactory {
            override fun  provider(callable: Callable): Provider {
              return project.providers.provider(callable)
            }
          },
        startParameterProperty = globalResolver.startParameterProperty,
        globalLocalProperty = globalResolver.globalLocalProperty,
        globalGradleLocalProperty = globalResolver.globalGradleLocalProperty,
        localPropertyProvider = localPropertyProvider,
        localGradlePropertyProvider = localGradlePropertyProvider,
      )

    /** @property project The project to resolve properties for. */
    public fun createForRootProject(
      project: Project,
      startParameterProperty: (String) -> Provider =
        project.providers.emptyStringProvider(),
      globalLocalProperty: (String) -> Provider = project.providers.emptyStringProvider(),
    ): PropertyResolver {
      check(project.rootProject == project) {
        "Project '${project.path}' should be the root project"
      }
      val gradlePropertyProvider: (String) -> Provider = project.providers::gradleProperty
      return PropertyResolver(
        providers =
          object : SimpleProviderFactory {
            override fun  provider(callable: Callable): Provider {
              return project.providers.provider(callable)
            }
          },
        startParameterProperty = startParameterProperty,
        globalLocalProperty = globalLocalProperty,
        localPropertyProvider = globalLocalProperty,
        localGradlePropertyProvider = gradlePropertyProvider,
        globalGradleLocalProperty = gradlePropertyProvider,
      )
    }
  }
}

public fun ProviderFactory.emptyStringProvider(): (String) -> Provider = {
  provider { null }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy