Does anyone have a good idea how to model a task i...
# plugin-development
v
Does anyone have a good idea how to model a task input property that can either be a file or directory? In pre-
Property
times I had an
@Internal
property that was set to either, and then a derived
@Optional @InputFile
read-only property and a derived
@Optional @InputDirectory
read-only property which checked whether the internal property is a file or directory and at execution time checked that exactly one of them is set. I'm thinking about cleaner ways to rewrite this, without splitting the task into multiple tasks.
e
I haven't tried but does this possibly work?
Copy code
@get:Nested var input: Either

interface Either {
    abstract class File : Either {
        @get:InputFile abstract val file: RegularFileProperty
    }
    abstract class Directory : Either {
        @get:InputDirectory abstract val dir: DirectoryProperty
    }
}
m
Probably not the answer you're looking for but your `@InputDirectory` should probably be `@InputFiles` in the first place
v
Ah, thanks to both of you. Always good to have a fresh set of eyes on something when looking at old non-sense stuff with a tired mind. 😄 Indeed a
ConfigurableFileCollection
is most probably what I want here. When I originally wrote that construct I was on Gradle 1.0 and not really knowing what I do. 😄 But besides that, @ephemients idea with some adjustments also works like intended. For completeness sake, this is what I now played with first and works like before:
Copy code
abstract class Foo : DefaultTask() {
    @get:Inject
    abstract val layout: ProjectLayout

    @get:Inject
    abstract val providers: ProviderFactory

    @get:Internal
    abstract val input: Property<FileSystemLocation>

    @get:Optional
    @get:InputFile
    val inputFile: Provider<RegularFile> = input.map {
        it.asFile.takeIf { it.isFile }?.let { layout.file(providers.provider { it }).get() }
    }

    @get:Optional
    @get:InputDirectory
    val inputDirectory: Provider<Directory> = input.map {
        it.asFile.takeIf { it.isDirectory }?.let { layout.dir(providers.provider { it }).get() }
    }

    @TaskAction
    fun execute() {
        println(
            when {
                !input.isPresent -> "Input is not configured"
                !input.get().asFile.exists() -> "Input does not exist"
                inputFile.isPresent -> "Input file: ${inputFile.get().asFile.absolutePath}"
                inputDirectory.isPresent -> "Input directory: ${inputDirectory.get().asFile.absolutePath}"
                else -> "Input has unexpected type"
            }
        )
    }
}

val foo by tasks.registering(Foo::class) {
    outputs.upToDateWhen { true }
    input = bar.flatMap { it.outputFile }
//    input = layout.buildDirectory.file("non-existing")
//    input = baz.flatMap { it.outputDirectory }
//    input = layout.buildDirectory.dir("non-existing")
}
and this with the idea of @ephemient:
Copy code
abstract class EitherFactory {
    @get:Inject
    abstract val objects: ObjectFactory

    fun file(block: Either.File.() -> Unit) = objects.newInstance<Either.File>().apply(block)
    fun dir(block: Either.Directory.() -> Unit) = objects.newInstance<Either.Directory>().apply(block)
}
extensions.create<EitherFactory>("eitherFactory")

sealed interface Either {
    abstract class File : Either {
        @get:InputFile
        abstract val file: RegularFileProperty
    }

    abstract class Directory : Either {
        @get:InputDirectory
        abstract val dir: DirectoryProperty
    }
}

abstract class Foo : DefaultTask() {
    @get:Inject
    abstract val layout: ProjectLayout

    @get:Inject
    abstract val providers: ProviderFactory

    @get:Nested
    abstract val input: Property<Either>

    @TaskAction
    fun execute() {
        println(
            when (input.get()) {
                is Either.File -> "Input file: ${(input.get() as Either.File).file.get().asFile.absolutePath}"
                is Either.Directory -> "Input directory: ${(input.get() as Either.Directory).dir.get().asFile.absolutePath}"
            }
        )
    }
}

val foo by tasks.registering(Foo::class) {
    outputs.upToDateWhen { true }
    input = eitherFactory.file { file = bar.flatMap { it.outputFile } }
//    input = eitherFactory.file { file = layout.buildDirectory.file("non-existing") }
//    input = eitherFactory.dir { dir = baz.flatMap { it.outputDirectory } }
//    input = eitherFactory.dir { dir = layout.buildDirectory.dir("non-existing") }
}
👍 1
e
for the record, you can
Copy code
when (val either = input.get()) {
    is Either.File -> "Input file: ${either.file.get().asFile.absolutePath}"
    is Either.Directory -> "Input directory: ${either.dir.get().asFile.absolutePath}"
}
instead of casting a second
get
👌 1
and yeah, Martin has a good point about
FileCollection