diff --git a/modules/nextflow/src/main/groovy/nextflow/config/scope/nextflow/DefaultOpts.groovy b/modules/nextflow/src/main/groovy/nextflow/config/scope/nextflow/DefaultOpts.groovy new file mode 100644 index 0000000000..f1853ea2d8 --- /dev/null +++ b/modules/nextflow/src/main/groovy/nextflow/config/scope/nextflow/DefaultOpts.groovy @@ -0,0 +1,39 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * 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 nextflow.config.scope.nextflow + +import groovy.transform.CompileStatic +import groovy.transform.EqualsAndHashCode +import groovy.transform.ToString + +/** + * Model nextflow default options + * + * @author Paolo Di Tommaso + */ +@ToString(includePackage = false, includeNames = true) +@EqualsAndHashCode +@CompileStatic +class DefaultOpts { + + final PublishDirOpts publishDir + + DefaultOpts(Map opts) { + publishDir = new PublishDirOpts( opts.publishDir as Map ?: Map.of()) + } +} diff --git a/modules/nextflow/src/main/groovy/nextflow/config/scope/nextflow/NextflowOpts.groovy b/modules/nextflow/src/main/groovy/nextflow/config/scope/nextflow/NextflowOpts.groovy new file mode 100644 index 0000000000..bd267f8ffc --- /dev/null +++ b/modules/nextflow/src/main/groovy/nextflow/config/scope/nextflow/NextflowOpts.groovy @@ -0,0 +1,40 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * 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 nextflow.config.scope.nextflow + +import groovy.transform.CompileStatic +import groovy.transform.EqualsAndHashCode +import groovy.transform.ToString + +/** + * Model nextflow options + * + * @author Paolo Di Tommaso + */ +@ToString(includePackage = false, includeNames = true) +@EqualsAndHashCode +@CompileStatic +class NextflowOpts { + + final DefaultOpts defaults + + NextflowOpts(Map opts) { + defaults = new DefaultOpts(opts.defaults as Map ?: Map.of()) + } + +} diff --git a/modules/nextflow/src/main/groovy/nextflow/config/scope/nextflow/PublishDirOpts.groovy b/modules/nextflow/src/main/groovy/nextflow/config/scope/nextflow/PublishDirOpts.groovy new file mode 100644 index 0000000000..a423520aaa --- /dev/null +++ b/modules/nextflow/src/main/groovy/nextflow/config/scope/nextflow/PublishDirOpts.groovy @@ -0,0 +1,62 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * 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 nextflow.config.scope.nextflow + +import groovy.transform.CompileStatic +import groovy.transform.EqualsAndHashCode +import groovy.transform.ToString + +/** + * Model publishDir options + * + * @author Paolo Di Tommaso + */ +@ToString(includePackage = false, includeNames = true) +@EqualsAndHashCode +@CompileStatic +class PublishDirOpts { + + static final public PublishDirOpts EMPTY = new PublishDirOpts(Map.of()) + + + final String mode + final Boolean enabled + final Boolean failOnError + final String pattern + final Object contentType + final Boolean overwrite + final String storageClass + final Map tags + + PublishDirOpts(Map opts) { + mode = opts.mode + enabled = asBool(opts.enabled) + failOnError = asBool(opts.failOnError) + overwrite = asBool(opts.overwrite) + pattern = opts.pattern + contentType = opts.contentType + storageClass = opts.storageClass + tags = opts.tags as Map + } + + private Boolean asBool(Object value) { + if( value==null ) + return null + return Boolean.valueOf(value as String) + } +} diff --git a/modules/nextflow/src/main/groovy/nextflow/processor/PublishDir.groovy b/modules/nextflow/src/main/groovy/nextflow/processor/PublishDir.groovy index 6fe0bdd7a3..14dc38205b 100644 --- a/modules/nextflow/src/main/groovy/nextflow/processor/PublishDir.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/processor/PublishDir.groovy @@ -37,6 +37,7 @@ import groovy.util.logging.Slf4j import nextflow.Global import nextflow.NF import nextflow.Session +import nextflow.config.scope.nextflow.PublishDirOpts import nextflow.extension.FilesEx import nextflow.file.FileHelper import nextflow.file.TagAwareFile @@ -178,41 +179,55 @@ class PublishDir { * @return An instance of {@link PublishDir} class */ @CompileDynamic - static PublishDir create( Map params ) { + static PublishDir create( Map params, PublishDirOpts defaults=PublishDirOpts.EMPTY ) { assert params def result = new PublishDir() if( params.path ) result.path = params.path - if( params.mode ) - result.mode = params.mode + final mode = params.mode as String ?: defaults.mode + if( mode ) + result.setMode(mode as String) - if( params.pattern ) - result.pattern = params.pattern + final pattern = params.pattern ?: defaults.pattern + if( pattern ) + result.pattern = pattern - if( params.overwrite != null ) - result.overwrite = Boolean.parseBoolean(params.overwrite.toString()) + final overwrite = params.overwrite!=null + ? Boolean.parseBoolean(params.overwrite.toString()) + : defaults.overwrite + if( overwrite != null ) + result.overwrite = overwrite if( params.saveAs ) result.saveAs = (Closure) params.saveAs - if( params.enabled != null ) - result.enabled = Boolean.parseBoolean(params.enabled.toString()) - - if( params.failOnError != null ) - result.failOnError = Boolean.parseBoolean(params.failOnError.toString()) - - if( params.tags != null ) - result.tags = params.tags - - if( params.contentType instanceof Boolean ) - result.contentType = params.contentType - else if( params.contentType ) - result.contentType = params.contentType as String - - if( params.storageClass ) - result.storageClass = params.storageClass as String + final enabled = params.enabled!=null + ? Boolean.parseBoolean(params.enabled.toString()) + : defaults.enabled + if( enabled != null ) + result.enabled = enabled + + final failOnError = params.failOnError!=null + ? Boolean.parseBoolean(params.failOnError.toString()) + : defaults.failOnError + if( failOnError != null ) + result.failOnError = failOnError + + final tags = params.tags ?: defaults.tags + if( tags != null ) + result.tags = tags + + final contentType = params.contentType!=null ? params.contentType : defaults.contentType + if( contentType instanceof Boolean ) + result.contentType = contentType + else if( contentType ) + result.contentType = contentType as String + + final storageClass = params.storageClass as String ?: defaults.storageClass + if( storageClass ) + result.storageClass = storageClass return result } diff --git a/modules/nextflow/src/test/groovy/nextflow/config/scope/nextflow/DefaultOptsTest.groovy b/modules/nextflow/src/test/groovy/nextflow/config/scope/nextflow/DefaultOptsTest.groovy new file mode 100644 index 0000000000..201def1769 --- /dev/null +++ b/modules/nextflow/src/test/groovy/nextflow/config/scope/nextflow/DefaultOptsTest.groovy @@ -0,0 +1,41 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * 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 nextflow.config.scope.nextflow + +import spock.lang.Specification + +/** + * + * @author Paolo Di Tommaso + */ +class DefaultOptsTest extends Specification { + + def 'should validate equals and hashcode' () { + given: + def o1 = new DefaultOpts([publishDir: [mode:'foo']]) + def o2 = new DefaultOpts([publishDir: [mode:'foo']]) + def o3 = new DefaultOpts([publishDir: [mode:'bar']]) + + expect: + o1 == o2 + o1 != o3 + and: + o1.hashCode() == o2.hashCode() + o1.hashCode() != o3.hashCode() + } +} diff --git a/modules/nextflow/src/test/groovy/nextflow/config/scope/nextflow/NextflowOptsTest.groovy b/modules/nextflow/src/test/groovy/nextflow/config/scope/nextflow/NextflowOptsTest.groovy new file mode 100644 index 0000000000..5d3c291b12 --- /dev/null +++ b/modules/nextflow/src/test/groovy/nextflow/config/scope/nextflow/NextflowOptsTest.groovy @@ -0,0 +1,80 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * 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 nextflow.config.scope.nextflow + +import spock.lang.Specification + +/** + * + * @author Paolo Di Tommaso + */ +class NextflowOptsTest extends Specification { + + def 'should validate equals and hashcode' () { + given: + def o1 = new NextflowOpts([defaults: [publishDir: [mode:'foo']]]) + def o2 = new NextflowOpts([defaults: [publishDir: [mode:'foo']]]) + def o3 = new NextflowOpts([defaults: [publishDir: [mode:'bar']]]) + + expect: + o1 == o2 + o1 != o3 + and: + o1.hashCode() == o2.hashCode() + o1.hashCode() != o3.hashCode() + } + + def 'should create empty nextflow opts' () { + when: + def nextflow = new NextflowOpts([:]) + then: + !nextflow.defaults.publishDir.enabled + !nextflow.defaults.publishDir.mode + !nextflow.defaults.publishDir.failOnError + !nextflow.defaults.publishDir.contentType + !nextflow.defaults.publishDir.overwrite + !nextflow.defaults.publishDir.contentType + !nextflow.defaults.publishDir.storageClass + !nextflow.defaults.publishDir.tags + } + + def 'should create nextflow publishdir opts' () { + when: + def nextflow = new NextflowOpts([ + defaults: [ + publishDir: [ + mode:'foo', + enabled: 'true', + failOnError: 'true', + contentType: 'some-content', + overwrite: 'true', + storageClass: 'some-storage', + tags: ['this':'one', 'that': 'two'] + ] + ]]) + then: + nextflow.defaults.publishDir.mode == 'foo' + nextflow.defaults.publishDir.enabled + nextflow.defaults.publishDir.failOnError + nextflow.defaults.publishDir.contentType == 'some-content' + nextflow.defaults.publishDir.overwrite + nextflow.defaults.publishDir.storageClass == 'some-storage' + nextflow.defaults.publishDir.tags == ['this':'one', 'that': 'two'] + } + +} diff --git a/modules/nextflow/src/test/groovy/nextflow/config/scope/nextflow/PublishDirOptsTest.groovy b/modules/nextflow/src/test/groovy/nextflow/config/scope/nextflow/PublishDirOptsTest.groovy new file mode 100644 index 0000000000..f31c0a0b95 --- /dev/null +++ b/modules/nextflow/src/test/groovy/nextflow/config/scope/nextflow/PublishDirOptsTest.groovy @@ -0,0 +1,89 @@ +/* + * Copyright 2013-2024, Seqera Labs + * + * 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 nextflow.config.scope.nextflow + + +import spock.lang.Specification +import spock.lang.Unroll + +/** + * + * @author Paolo Di Tommaso + */ +class PublishDirOptsTest extends Specification { + + def 'should validate equals and hash code' () { + when: + def p1 = new PublishDirOpts(enabled: true, mode:'foo') + def p2 = new PublishDirOpts(enabled: true, mode:'foo') + def p3 = new PublishDirOpts(enabled: false, mode:'bar') + + then: + p1 == p2 + p1 != p3 + and: + p1.hashCode() == p2.hashCode() + p1.hashCode() != p3.hashCode() + } + + def 'should create empty opts' () { + given: + def opts = new PublishDirOpts(Map.of()) + expect: + opts.mode == null + opts.enabled == null + opts.failOnError == null + opts.pattern == null + opts.contentType == null + opts.overwrite == null + opts.storageClass == null + opts.tags == null + } + + @Unroll + def 'should populate publishdir obj' () { + expect: + new PublishDirOpts(OPTS)."$PROPERTY" == EXPECTED + where: + OPTS | PROPERTY | EXPECTED + [mode:'foo'] | 'mode' | 'foo' + [mode:'bar'] | 'mode' | 'bar' + and: + [:] | 'enabled' | null + [enabled:false] | 'enabled' | false + [enabled:'false'] | 'enabled' | false + [enabled:true] | 'enabled' | true + [enabled:'true'] | 'enabled' | true + and: + [:] | 'overwrite' | null + [overwrite:false] | 'overwrite' | false + [overwrite:'false'] | 'overwrite' | false + [overwrite:true] | 'overwrite' | true + [overwrite:'true'] | 'overwrite' | true + and: + [:] | 'failOnError' | null + [failOnError:false] | 'failOnError' | false + [failOnError:'false'] | 'failOnError' | false + [failOnError:true] | 'failOnError' | true + [failOnError:'true'] | 'failOnError' | true + and: + [pattern: '*.txt'] | 'pattern' | '*.txt' + and: + [tags: [FOO:'one',BAR:'two']] | 'tags' | [FOO:'one',BAR:'two'] + } +}