test.smk

    # =============================================================================
    # Snakefile — comprehensive feature test for Snakemake highlighting
    # Minimal comments; lines marked `# ERROR:` are intentionally invalid.
    # =============================================================================

    # ---------- Top-level Python ----------

    if True: forbidden # ERROR: no text allowed after block opener
        a = 1
    else: forbidden # ERROR: same
        a = 0

    config = {
        "method": "B",
        "samples": ["S1", "S2"],
        "ref": "ref/genome.fa",
        "gtf": "ref/genes.gtf",
        "threads_default": 4,
        "work": "work",
        "adapters": {"fwd": "AGATCGGAAGAGC", "rev": "AGATCGGAAGAGC"}
    }
    SAMPLES = config["samples"]

    def fq(sample, mate):
        return f"raw/{sample}_R{mate}.fastq.gz"

    # ---------- Global Snakemake directives ----------
    workdir: config["work"]
    localrules: all, qc_fastqc
    ruleorder: sort_index > align
    report: "report/report.html"
    envvars: "LD_LIBRARY_PATH", "OMP_NUM_THREADS"
    wildcard_constraints:
        sample = r"[A-Za-z0-9_]+"

    include: "extras.smk"
    container: "docker://ubuntu:22.04"
    conda: "envs/global.yml"
    containerized:

    # Legacy/compat directives still recognized in old workflows
    moduleinclude: "legacy/tools.smk"   # legacy include for modules
    # deprecated (kept for legacy)
    subworkflow oldwf:  something #ERROR: nothing allowed after colon
        workdir: "oldwf"
        snakefile: "workflow/Snakefile"
        input: # ERROR: `input` directive not allowed in subworkflows

    # ---------- Modules and use rule ----------
    module asm: something #ERROR: nothing allowed after colon
        snakefile: "modules/assembly.smk"
        config: "modules/assembly.yaml"
        input: "test" # ERROR: `input` directive not allowed in modules

    # Reuse a rule from the module; inside `with:` we use regular rule directives
    use rule assemble as assemble_mod with: something #ERROR: nothing allowed after colon
        snakefile: # ERROR `snakefile` directive not allowed in rules
        threads: 8
        params:
            mode = "quick"
        message:
            "Assembling {wildcards.sample} (module override)"
        # wrong directive (kept to test error handling inside a use-body)
        outpt: "SHOULD-NOT-BE-HERE"   # ERROR: typo

    # ---------- INTENTIONAL TOP-LEVEL ERRORS ----------
    workdirr: "typo/dir"              # ERROR: unknown top-level keyword
    snakefile: "top/level.smk"        # ERROR: only valid inside module/subworkflow

    # ---------- Pipeline ----------
    rule all:
        input:
            expand("results/{sample}/summary.txt", sample=SAMPLES)

    rule qc_fastqc:
        input:
            r1 = lambda wc: fq(wc.sample, 1),
            r2 = lambda wc: fq(wc.sample, 2),
        output:
            html = "qc/{sample}_fastqc.html",
            zip  = "qc/{sample}_fastqc.zip",
        threads: 2
        resources:
            mem_mb = 1024
        log:
            "log/fastqc_{sample}.log"
        params:
            extra = "--nogroup"
        shell:
            """
            fastqc -t {threads} {params.extra} -o qc {input.r1} {input.r2} > {log} 2>&1
            """

    rule trim_cutadapt:
        input:
            r1 = lambda wc: fq(wc.sample, 1),
            r2 = lambda wc: fq(wc.sample, 2),
        output:
            r1 = "trim/{sample}_R1.fastq.gz", # `sample`: wildcard
            r2 = "trim/{sample}_R2.fastq.gz",
            report = f"{report_dir}/{{sample}}" # `report_dir`: f-string interpolation, `sample`: wildcard
        params:
            a = config["adapters"]["fwd"],
            A = config["adapters"]["rev"],
        threads: 8
        conda:
            "envs/cutadapt.yml"
        log:
            "log/cutadapt_{sample}.log"
        shell:
            """
            cutadapt -j {threads} -a {params.a} -A {params.A} \
                -o {output.r1} -p {output.r2} {input.r1} {input.r2} > {log} 2>&1
            """

    # Example of wrapper usage (version string illustrative)
    rule align:
        input:
            r1 = "trim/{sample}_R1.fastq.gz",
            r2 = "trim/{sample}_R2.fastq.gz",
            ref = config["ref"],
        output:
            bam = "map/{sample}.unsorted.bam",
        threads: 12
        resources:
            mem_mb = 8000
        params:
            # BWA-MEM2 example options
            extra = "-M"
        log:
            "log/align_{sample}.log"
        wrapper:
            "0.90.0/bio/bwa/mem2"
        shell:
            "bwa-mem2 mem -t {threads} {params.extra} {input.ref} {input.r1} {input.r2} | samtools view -bS - > {output.bam} 2> {log}"

    rule sort_index:
        input:
            "map/{sample}.unsorted.bam"
        output:
            bam = "map/{sample}.bam",
            bai = "map/{sample}.bam.bai",
        threads: 6
        resources:
            mem_mb = 4000
        envmodules:
            "samtools/1.16"
        shadow:
            "minimal"
        shell:
            """
            samtools sort -@ {threads} -o {output.bam} {input}
            samtools index -@ {threads} {output.bam}
            """

    rule quantify:
        input:
            bam = "map/{sample}.bam",
            bai = "map/{sample}.bam.bai",
            gtf = config["gtf"],
        output:
            counts = "counts/{sample}.txt",
        threads: 4
        group:
            "counting"
        priority:
            50
        params:
            feature_type = "exon",
            id_attr = "gene_id",
        shell:
            """
            featureCounts -T {threads} -a {input.gtf} -t {params.feature_type} -g {params.id_attr} \
                -o {output.counts} {input.bam}
            """

    # Example of script & notebook directives
    rule plot_qc:
        input:
            "qc/{sample}_fastqc.zip"
        output:
            "plots/{sample}_qc.png"
        script:
            "scripts/plot_qc.py"    # not executed; present to test directive

    rule explore_notebook:
        input:
            "counts/{sample}.txt"
        output:
            "notebooks/{sample}_eda.ipynb"
        notebook:
            "notebooks/template.ipynb"

    # Example of per-rule container / cache / benchmark / message / name / version (legacy)
    rule summarize:
        input:
            bam = "map/{sample}.bam",
            counts = "counts/{sample}.txt"
        output:
            txt = "results/{sample}/summary.txt"
        params:
            tag = "{sample}"        # wildcard should highlight distinctly
        message:
            "Summarizing {wildcards.sample}"
        name:
            "summarize_{sample}"
        benchmark:
            "benchmark/summarize_{sample}.tsv"
        cache:
            "permissive"
        container:
            "docker://python:3.11"
        version: "1.0"            # legacy directive
        threads: 2
        resources:
            mem_mb = 512
        log:
            "log/summarize_{sample}.log"
        run:
            # simple Python run block
            import json
            meta = {
                "sample": wildcards.sample,
                "bam": input.bam,
                "counts": input.counts,
                "tag": params.tag,
            }
            # write a tiny summary
            import os
            os.makedirs(os.path.dirname(output.txt), exist_ok=True)
            with open(output.txt, "w") as fh:
                fh.write(json.dumps(meta, indent=2) + "\n")

    # ---------- More intentional errors inside a rule body ----------

    if config["method"] == "A":

        rule bad_header_examples_A:
            input:
                "map/{sample}.bam"
            outpt:      # ERROR: unknown directive
                "nowhere.txt"
            foo:        # ERROR: unknown directive
                "bar"
            shell:
                "true"
    else:
        rule bad_header_examples_B:
            input:
                "map/{sample}.bam"
            output:      # ERROR: unknown directive
                "nowhere.txt"
            foo:        # ERROR: unknown directive
                "bar"
            shell:
                "true"

    # ---------- Using the module rule  ----------
    rule assemble_via_module:
        input:
            "trim/{sample}_R1.fastq.gz",
            "trim/{sample}_R2.fastq.gz",
        output:
            "assembly/{sample}/contigs.fa"
        threads: 8
        shell:
            "echo assembly > {output}"

    # ---------- Default target redirection ----------
    rule final_default:
        output:
            "FINAL.marker"
        default_target:
            True
        shell:
            "touch {output}"

    rule: no text allowed here  # ERROR: no text allowed after block opener
        input: "back_to_normal.txt"