<commit_before>package com.squareup.sqldelight.gradle

import com.alecstrong.sql.psi.core.DialectPreset
import com.android.builder.model.AndroidProject.FD_GENERATED
import com.squareup.sqldelight.core.SqlDelightCompilationUnit
import com.squareup.sqldelight.core.SqlDelightDatabaseName
import com.squareup.sqldelight.core.SqlDelightDatabaseProperties
import com.squareup.sqldelight.core.SqlDelightSourceFolder
import com.squareup.sqldelight.core.lang.MigrationFileType
import com.squareup.sqldelight.core.lang.SqlDelightFileType
import com.squareup.sqldelight.gradle.kotlin.Source
import com.squareup.sqldelight.gradle.kotlin.sources
import groovy.lang.GroovyObject
import java.io.File
import org.gradle.api.GradleException
import org.gradle.api.Project

class SqlDelightDatabase(
  val project: Project,
  var name: String,
  var packageName: String? = null,
  var schemaOutputDirectory: File? = null,
  var sourceFolders: Collection<String>? = null,
  var dialect: String = "sqlite:3.18",
  var deriveSchemaFromMigrations: Boolean = false,
  var migrationOutputDirectory: File? = null,
  var migrationOutputFileFormat: String = ".sql"
) {
  private val generatedSourcesDirectory
    get() = File(project.buildDir, "$FD_GENERATED/sqldelight/code/$name")

  private val sources by lazy { sources() }
  private val dependencies = mutableListOf<SqlDelightDatabase>()

  private var recursionGuard = false

  fun methodMissing(name: String, args: Any): Any {
    return (project as GroovyObject).invokeMethod(name, args)
  }

  fun dependency(dependencyProject: Project) {
    project.evaluationDependsOn(dependencyProject.path)

    val dependency = dependencyProject.extensions.findByType(SqlDelightExtension::class.java)
        ?: throw IllegalStateException("Cannot depend on a module with no sqldelight plugin.")
    val database = dependency.databases.singleOrNull { it.name == name }
        ?: throw IllegalStateException("No database named $name in $dependencyProject")
    check(database.packageName != packageName) { "Detected a schema that already has the package name $packageName in project $dependencyProject" }
    dependencies.add(database)
  }

  internal fun getProperties(): SqlDelightDatabaseProperties {
    val packageName = requireNotNull(packageName) { "property packageName must be provided" }

    check(!recursionGuard) { "Found a circular dependency in $project with database $name" }
    recursionGuard = true

    val dialectMapping = mapOf(
        "sqlite:3.18" to DialectPreset.SQLITE_3_18,
        "sqlite:3.24" to DialectPreset.SQLITE_3_24,
        "sqlite:3.25" to DialectPreset.SQLITE_3_25,
        "mysql" to DialectPreset.MYSQL,
        "postgresql" to DialectPreset.POSTGRESQL,
        "hsql" to DialectPreset.HSQL
    )

    val dialect = dialectMapping[dialect]
        ?: throw GradleException("The dialect $dialect is not supported. Supported dialects: ${dialectMapping.keys.joinToString()}.")

    try {
      return SqlDelightDatabaseProperties(
          packageName = packageName,
          compilationUnits = sources.map { source ->
            SqlDelightCompilationUnit(
                name = source.name,
                sourceFolders = sourceFolders(source).sortedBy { it.path }
            )
          },
          outputDirectory = generatedSourcesDirectory.toRelativeString(project.projectDir),
          className = name,
          dependencies = dependencies.map { SqlDelightDatabaseName(it.packageName!!, it.name) },
          dialectPreset = dialect,
          deriveSchemaFromMigrations = deriveSchemaFromMigrations
      )
    } finally {
      recursionGuard = false
    }
  }

  private fun sourceFolders(source: Source): List<SqlDelightSourceFolder> {
    val sourceFolders = sourceFolders ?: listOf("sqldelight")

    val relativeSourceFolders = sourceFolders.flatMap { folder ->
      source.sourceSets.map { SqlDelightSourceFolder("src/$it/$folder", false) }
    }

    return relativeSourceFolders + dependencies.flatMap { dependency ->
      val dependencySource = source.closestMatch(dependency.sources)
          ?: return@flatMap emptyList<SqlDelightSourceFolder>()
      val compilationUnit = dependency.getProperties().compilationUnits
          .single { it.name == dependencySource.name }

      return@flatMap compilationUnit.sourceFolders.map {
        val folder = File(dependency.project.projectDir, it.path)
        SqlDelightSourceFolder(project.relativePath(folder), true)
      }
    }
  }

  internal fun registerTasks() {
    // Ideally each sourceSet has its proper source set up, but with sqldelight they all go into one
    // place right now. Can revisit later but prioritise common for now:

    val common = sources.singleOrNull { it.name == "commonMain" }
    common?.sourceDirectorySet?.srcDir(generatedSourcesDirectory.toRelativeString(project.projectDir))

    sources.forEach { source ->
      // Add the source dependency on the generated code.<commit_msg>Properly retrieve the commonMain source set (#1907)<commit_after>package com.squareup.sqldelight.gradle

import com.alecstrong.sql.psi.core.DialectPreset
import com.android.builder.model.AndroidProject.FD_GENERATED
import com.squareup.sqldelight.core.SqlDelightCompilationUnit
import com.squareup.sqldelight.core.SqlDelightDatabaseName
import com.squareup.sqldelight.core.SqlDelightDatabaseProperties
import com.squareup.sqldelight.core.SqlDelightSourceFolder
import com.squareup.sqldelight.core.lang.MigrationFileType
import com.squareup.sqldelight.core.lang.SqlDelightFileType
import com.squareup.sqldelight.gradle.kotlin.Source
import com.squareup.sqldelight.gradle.kotlin.sources
import groovy.lang.GroovyObject
import java.io.File
import org.gradle.api.GradleException
import org.gradle.api.Project

class SqlDelightDatabase(
  val project: Project,
  var name: String,
  var packageName: String? = null,
  var schemaOutputDirectory: File? = null,
  var sourceFolders: Collection<String>? = null,
  var dialect: String = "sqlite:3.18",
  var deriveSchemaFromMigrations: Boolean = false,
  var migrationOutputDirectory: File? = null,
  var migrationOutputFileFormat: String = ".sql"
) {
  private val generatedSourcesDirectory
    get() = File(project.buildDir, "$FD_GENERATED/sqldelight/code/$name")

  private val sources by lazy { sources() }
  private val dependencies = mutableListOf<SqlDelightDatabase>()

  private var recursionGuard = false

  fun methodMissing(name: String, args: Any): Any {
    return (project as GroovyObject).invokeMethod(name, args)
  }

  fun dependency(dependencyProject: Project) {
    project.evaluationDependsOn(dependencyProject.path)

    val dependency = dependencyProject.extensions.findByType(SqlDelightExtension::class.java)
        ?: throw IllegalStateException("Cannot depend on a module with no sqldelight plugin.")
    val database = dependency.databases.singleOrNull { it.name == name }
        ?: throw IllegalStateException("No database named $name in $dependencyProject")
    check(database.packageName != packageName) { "Detected a schema that already has the package name $packageName in project $dependencyProject" }
    dependencies.add(database)
  }

  internal fun getProperties(): SqlDelightDatabaseProperties {
    val packageName = requireNotNull(packageName) { "property packageName must be provided" }

    check(!recursionGuard) { "Found a circular dependency in $project with database $name" }
    recursionGuard = true

    val dialectMapping = mapOf(
        "sqlite:3.18" to DialectPreset.SQLITE_3_18,
        "sqlite:3.24" to DialectPreset.SQLITE_3_24,
        "sqlite:3.25" to DialectPreset.SQLITE_3_25,
        "mysql" to DialectPreset.MYSQL,
        "postgresql" to DialectPreset.POSTGRESQL,
        "hsql" to DialectPreset.HSQL
    )

    val dialect = dialectMapping[dialect]
        ?: throw GradleException("The dialect $dialect is not supported. Supported dialects: ${dialectMapping.keys.joinToString()}.")

    try {
      return SqlDelightDatabaseProperties(
          packageName = packageName,
          compilationUnits = sources.map { source ->
            SqlDelightCompilationUnit(
                name = source.name,
                sourceFolders = sourceFolders(source).sortedBy { it.path }
            )
          },
          outputDirectory = generatedSourcesDirectory.toRelativeString(project.projectDir),
          className = name,
          dependencies = dependencies.map { SqlDelightDatabaseName(it.packageName!!, it.name) },
          dialectPreset = dialect,
          deriveSchemaFromMigrations = deriveSchemaFromMigrations
      )
    } finally {
      recursionGuard = false
    }
  }

  private fun sourceFolders(source: Source): List<SqlDelightSourceFolder> {
    val sourceFolders = sourceFolders ?: listOf("sqldelight")

    val relativeSourceFolders = sourceFolders.flatMap { folder ->
      source.sourceSets.map { SqlDelightSourceFolder("src/$it/$folder", false) }
    }

    return relativeSourceFolders + dependencies.flatMap { dependency ->
      val dependencySource = source.closestMatch(dependency.sources)
          ?: return@flatMap emptyList<SqlDelightSourceFolder>()
      val compilationUnit = dependency.getProperties().compilationUnits
          .single { it.name == dependencySource.name }

      return@flatMap compilationUnit.sourceFolders.map {
        val folder = File(dependency.project.projectDir, it.path)
        SqlDelightSourceFolder(project.relativePath(folder), true)
      }
    }
  }

  internal fun registerTasks() {
    // Ideally each sourceSet has its proper source set up, but with sqldelight they all go into one
    // place right now. Can revisit later but prioritise common for now:

    val common = sources.singleOrNull { it.sourceSets.singleOrNull() == "commonMain" }
    common?.sourceDirectorySet?.srcDir(generatedSourcesDirectory.toRelativeString(project.projectDir))

    sources.forEach { source ->
      // Add the source dependency on the generated code.
{'fraction_non_alphanumeric': 0.08829136149292666, 'fraction_numerical': 0.00461523025985753, 'mean_word_length': 3.905511811023622, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2282123', 'n_tokens_mistral': 2881, 'n_tokens_neox': 2778, 'n_words': 635}