|
|
@@ -1,245 +1,340 @@
|
|
|
-#[derive(Debug, Clone)]
|
|
|
+use log::{info, warn};
|
|
|
+use serde::{Deserialize, Serialize};
|
|
|
+use std::fs;
|
|
|
+use std::path::PathBuf;
|
|
|
+
|
|
|
+const CONFIG_TEMPLATE: &str = include_str!("../pandora-config.example.toml");
|
|
|
+
|
|
|
+#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
+/// Global configuration for the Pandora somatic pipeline.
|
|
|
+///
|
|
|
+/// Loaded from `~/.local/share/pandora/pandora-config.toml` (see [`Config::config_path`]).
|
|
|
+/// Most fields are path templates that can contain placeholders such as:
|
|
|
+/// `{result_dir}`, `{id}`, `{time}`, `{reference_name}`, `{haplotagged_bam_tag_name}`, `{output_dir}`.
|
|
|
pub struct Config {
|
|
|
+ // === General filesystem layout / I/O ===
|
|
|
+ /// Directory where POD / run description files are located.
|
|
|
pub pod_dir: String,
|
|
|
+
|
|
|
+ /// Root directory where all results will be written.
|
|
|
pub result_dir: String,
|
|
|
+
|
|
|
+ /// Temporary directory used when unarchiving input data.
|
|
|
pub unarchive_tmp_dir: String,
|
|
|
+
|
|
|
+ /// Maximum memory available for dockerized tools, in GiB.
|
|
|
+ pub docker_max_memory_go: u16,
|
|
|
+
|
|
|
+ /// Path to the SQLite database of processed cases.
|
|
|
+ pub db_cases_path: String,
|
|
|
+
|
|
|
+ /// Path to the `conda.sh` activation script (used to activate envs before running tools).
|
|
|
+ pub conda_sh: String,
|
|
|
+
|
|
|
+ // === Alignment / BAM handling ===
|
|
|
+ /// Configuration for Dorado + samtools alignment pipeline.
|
|
|
pub align: AlignConfig,
|
|
|
+
|
|
|
+ /// Minimum MAPQ for reads to be kept during BAM filtering.
|
|
|
+ pub bam_min_mapq: u8,
|
|
|
+
|
|
|
+ /// Number of threads for BAM processing steps (view, sort, index…).
|
|
|
+ pub bam_n_threads: u8,
|
|
|
+
|
|
|
+ /// Number of reads sampled when estimating BAM composition (e.g. tumor contamination).
|
|
|
+ pub bam_composition_sample_size: u32,
|
|
|
+
|
|
|
+ // === Reference genome and annotations ===
|
|
|
+ /// Path to the reference FASTA used throughout the pipeline.
|
|
|
pub reference: String,
|
|
|
+
|
|
|
+ /// Short name for the reference (e.g. "hs1"), used in filenames.
|
|
|
pub reference_name: String,
|
|
|
+
|
|
|
+ /// Path to the sequence dictionary (`.dict`) for the reference.
|
|
|
pub dict_file: String,
|
|
|
+
|
|
|
+ /// Path to the RefSeq GFF3 annotation, sorted and indexed.
|
|
|
pub refseq_gff: String,
|
|
|
- pub docker_max_memory_go: u16,
|
|
|
- pub savana_bin: String,
|
|
|
- pub savana_threads: u8,
|
|
|
+
|
|
|
+ /// BED template used to mask low-quality or filtered regions.
|
|
|
+ ///
|
|
|
+ /// Placeholders:
|
|
|
+ /// - `{result_dir}`: global result directory
|
|
|
+ /// - `{id}`: case identifier
|
|
|
+ pub mask_bed: String,
|
|
|
+
|
|
|
+ /// BED file with CpG coordinates in the reference.
|
|
|
+ pub cpg_bed: String,
|
|
|
+
|
|
|
+ /// BED file with early-replicating regions (used for replication timing–based analyses).
|
|
|
+ pub early_bed: String,
|
|
|
+
|
|
|
+ /// BED file with late-replicating regions.
|
|
|
+ pub late_bed: String,
|
|
|
+
|
|
|
+ /// Panels of interest (name, BED path).
|
|
|
+ pub panels: Vec<(String, String)>,
|
|
|
+
|
|
|
+ // === Sample naming conventions ===
|
|
|
+ /// Label used for the tumor sample in directory and file names (e.g. "diag").
|
|
|
pub tumoral_name: String,
|
|
|
+
|
|
|
+ /// Label used for the normal sample (e.g. "mrd").
|
|
|
pub normal_name: String,
|
|
|
+
|
|
|
+ /// BAM tag name used for haplotagged reads (e.g. "HP").
|
|
|
pub haplotagged_bam_tag_name: String,
|
|
|
+
|
|
|
+ // === Coverage counting (somatic-scan) ===
|
|
|
+ /// Name of the subdirectory (under each sample dir) where count files are stored.
|
|
|
pub count_dir_name: String,
|
|
|
+
|
|
|
+ /// Bin size (bp) for count files.
|
|
|
pub count_bin_size: u32,
|
|
|
+
|
|
|
+ /// Number of chunks used to split chromosomes for counting.
|
|
|
pub count_n_chunks: u32,
|
|
|
+
|
|
|
+ /// Whether to force recomputation of coverage / counting even if outputs already exist.
|
|
|
+ pub somatic_scan_force: bool,
|
|
|
+
|
|
|
+ // === Somatic pipeline global options ===
|
|
|
+ /// Whether to force recomputation of the whole somatic pipeline.
|
|
|
+ pub somatic_pipe_force: bool,
|
|
|
+
|
|
|
+ /// Default number of threads for most heavy tools (DeepVariant, Savana, etc.).
|
|
|
+ pub somatic_pipe_threads: u8,
|
|
|
+
|
|
|
+ /// Path template to the per-case somatic pipeline statistics directory.
|
|
|
+ ///
|
|
|
+ /// Placeholders: `{result_dir}`, `{id}`.
|
|
|
+ pub somatic_pipe_stats: String,
|
|
|
+
|
|
|
+ // === Basic somatic filtering / QC thresholds ===
|
|
|
+ /// Minimum depth in the constitutional sample to consider a site evaluable.
|
|
|
+ pub somatic_min_constit_depth: u16,
|
|
|
+
|
|
|
+ /// Maximum allowed ALT count in the constitutional sample for a somatic call.
|
|
|
+ pub somatic_max_alt_constit: u16,
|
|
|
+
|
|
|
+ /// Window size (bp) used when computing sequence entropy around variants.
|
|
|
+ pub entropy_seq_len: usize,
|
|
|
+
|
|
|
+ /// Minimum Shannon entropy threshold for keeping a variant.
|
|
|
+ pub min_shannon_entropy: f64,
|
|
|
+
|
|
|
+ /// Maximum depth considered "low quality" for certain filters.
|
|
|
+ pub max_depth_low_quality: u32,
|
|
|
+
|
|
|
+ /// Minimum depth considered "high quality" for certain filters.
|
|
|
+ pub min_high_quality_depth: u32,
|
|
|
+
|
|
|
+ /// Minimum number of callers supporting a variant for it to be kept.
|
|
|
+ pub min_n_callers: u8,
|
|
|
+
|
|
|
+ // === DeepVariant configuration ===
|
|
|
+ /// Template for the DeepVariant output directory (solo and normal/tumor runs).
|
|
|
+ ///
|
|
|
+ /// Placeholders: `{result_dir}`, `{id}`, `{time}`.
|
|
|
+ pub deepvariant_output_dir: String,
|
|
|
+
|
|
|
+ /// Number of threads to use for DeepVariant.
|
|
|
+ pub deepvariant_threads: u8,
|
|
|
+
|
|
|
+ /// DeepVariant docker / binary version.
|
|
|
+ pub deepvariant_bin_version: String,
|
|
|
+
|
|
|
+ /// DeepVariant model type (e.g. "ONT_R104").
|
|
|
+ pub deepvariant_model_type: String,
|
|
|
+
|
|
|
+ /// Force DeepVariant recomputation even if outputs already exist.
|
|
|
+ pub deepvariant_force: bool,
|
|
|
+
|
|
|
+ // === DeepSomatic configuration ===
|
|
|
+ /// Template for the DeepSomatic output directory.
|
|
|
+ ///
|
|
|
+ /// Placeholders: `{result_dir}`, `{id}`, `{time}`.
|
|
|
+ pub deepsomatic_output_dir: String,
|
|
|
+
|
|
|
+ /// Number of threads for DeepSomatic.
|
|
|
+ pub deepsomatic_threads: u8,
|
|
|
+
|
|
|
+ /// DeepSomatic docker / binary version.
|
|
|
+ pub deepsomatic_bin_version: String,
|
|
|
+
|
|
|
+ /// DeepSomatic model type (e.g. "ONT").
|
|
|
+ pub deepsomatic_model_type: String,
|
|
|
+
|
|
|
+ /// Force DeepSomatic recomputation.
|
|
|
+ pub deepsomatic_force: bool,
|
|
|
+
|
|
|
+ // === ClairS configuration ===
|
|
|
+ /// Number of threads for ClairS.
|
|
|
+ pub clairs_threads: u8,
|
|
|
+
|
|
|
+ /// ClairS docker tag.
|
|
|
+ pub clairs_docker_tag: String,
|
|
|
+
|
|
|
+ /// Force ClairS recomputation.
|
|
|
+ pub clairs_force: bool,
|
|
|
+
|
|
|
+ /// Platform preset for ClairS (e.g. "ont_r10_dorado_sup_5khz_ssrs").
|
|
|
+ pub clairs_platform: String,
|
|
|
+
|
|
|
+ /// Template for ClairS output directory (`{result_dir}`, `{id}`).
|
|
|
+ pub clairs_output_dir: String,
|
|
|
+
|
|
|
+ // === Savana configuration ===
|
|
|
+ /// Savana binary name or full path.
|
|
|
+ pub savana_bin: String,
|
|
|
+
|
|
|
+ /// Number of threads for Savana.
|
|
|
+ pub savana_threads: u8,
|
|
|
+
|
|
|
+ /// Template for Savana output directory (`{result_dir}`, `{id}`).
|
|
|
pub savana_output_dir: String,
|
|
|
+
|
|
|
+ /// Template for Savana copy number file.
|
|
|
+ ///
|
|
|
+ /// Placeholders: `{output_dir}`, `{id}`, `{reference_name}`, `{haplotagged_bam_tag_name}`.
|
|
|
pub savana_copy_number: String,
|
|
|
+
|
|
|
+ /// Template for Savana raw read counts file.
|
|
|
+ ///
|
|
|
+ /// Same placeholders as [`Config::savana_copy_number`].
|
|
|
pub savana_read_counts: String,
|
|
|
- pub germline_phased_vcf: String,
|
|
|
+
|
|
|
+ /// Template for Savana passed VCF output (`{output_dir}`, `{id}`).
|
|
|
pub savana_passed_vcf: String,
|
|
|
- pub conda_sh: String,
|
|
|
+
|
|
|
+ /// Force Savana recomputation.
|
|
|
pub savana_force: bool,
|
|
|
- pub deepvariant_output_dir: String,
|
|
|
+
|
|
|
+ /// Template for constitutional phased VCF (`{result_dir}`, `{id}`).
|
|
|
+ pub germline_phased_vcf: String,
|
|
|
+
|
|
|
+ // === Severus configuration ===
|
|
|
+ /// Path to Severus main script (`severus.py`).
|
|
|
pub severus_bin: String,
|
|
|
+
|
|
|
+ /// Force Severus recomputation.
|
|
|
pub severus_force: bool,
|
|
|
+
|
|
|
+ /// Number of threads for Severus.
|
|
|
pub severus_threads: u8,
|
|
|
+
|
|
|
+ /// VNTRs BED file for Severus.
|
|
|
pub vntrs_bed: String,
|
|
|
+
|
|
|
+ /// Path to Severus PoN file (TSV or VCF).
|
|
|
pub severus_pon: String,
|
|
|
+
|
|
|
+ /// Template for Severus tumor/normal (paired) output directory.
|
|
|
+ ///
|
|
|
+ /// Placeholders: `{result_dir}`, `{id}`.
|
|
|
pub severus_output_dir: String,
|
|
|
+
|
|
|
+ /// Template for Severus solo output directory.
|
|
|
+ ///
|
|
|
+ /// Placeholders: `{result_dir}`, `{id}`, `{time}`.
|
|
|
pub severus_solo_output_dir: String,
|
|
|
+
|
|
|
+ // === Longphase configuration ===
|
|
|
+ /// Path to longphase binary.
|
|
|
pub longphase_bin: String,
|
|
|
+
|
|
|
+ /// Number of threads for longphase.
|
|
|
pub longphase_threads: u8,
|
|
|
+
|
|
|
+ /// Number of threads for longphase modcall step.
|
|
|
+ pub longphase_modcall_threads: u8,
|
|
|
+
|
|
|
+ /// Template for longphase modcall VCF.
|
|
|
+ ///
|
|
|
+ /// Placeholders: `{result_dir}`, `{id}`, `{time}`.
|
|
|
pub longphase_modcall_vcf: String,
|
|
|
+
|
|
|
+ // === Modkit configuration ===
|
|
|
+ /// Path to modkit binary.
|
|
|
pub modkit_bin: String,
|
|
|
+
|
|
|
+ /// Number of threads for `modkit summary`.
|
|
|
pub modkit_summary_threads: u8,
|
|
|
+
|
|
|
+ /// Template for modkit summary output file.
|
|
|
+ ///
|
|
|
+ /// Placeholders: `{result_dir}`, `{id}`, `{time}`.
|
|
|
pub modkit_summary_file: String,
|
|
|
- pub longphase_modcall_threads: u8,
|
|
|
- pub deepvariant_threads: u8,
|
|
|
- pub deepvariant_bin_version: String,
|
|
|
- pub deepvariant_model_type: String,
|
|
|
- pub deepvariant_force: bool,
|
|
|
- pub deepsomatic_output_dir: String,
|
|
|
- pub deepsomatic_threads: u8,
|
|
|
- pub deepsomatic_bin_version: String,
|
|
|
- pub deepsomatic_model_type: String,
|
|
|
- pub deepsomatic_force: bool,
|
|
|
- pub bam_min_mapq: u8,
|
|
|
- pub bam_n_threads: u8,
|
|
|
- pub db_cases_path: String,
|
|
|
- pub somatic_pipe_stats: String,
|
|
|
|
|
|
- pub clairs_threads: u8,
|
|
|
- pub clairs_docker_tag: String,
|
|
|
- pub clairs_force: bool,
|
|
|
- pub clairs_platform: String,
|
|
|
- pub clairs_output_dir: String,
|
|
|
- pub mask_bed: String,
|
|
|
- pub somatic_min_constit_depth: u16,
|
|
|
- pub somatic_max_alt_constit: u16,
|
|
|
- pub entropy_seq_len: usize,
|
|
|
- pub min_shannon_entropy: f64,
|
|
|
+ // === Nanomonsv configuration ===
|
|
|
+ /// Path to nanomonsv binary.
|
|
|
pub nanomonsv_bin: String,
|
|
|
+
|
|
|
+ /// Template for paired nanomonsv output directory (`{result_dir}`, `{id}`, `{time}`).
|
|
|
pub nanomonsv_output_dir: String,
|
|
|
+
|
|
|
+ /// Force nanomonsv recomputation.
|
|
|
pub nanomonsv_force: bool,
|
|
|
+
|
|
|
+ /// Number of threads for nanomonsv.
|
|
|
pub nanomonsv_threads: u8,
|
|
|
+
|
|
|
+ /// Template for paired nanomonsv passed VCF (`{output_dir}`, `{id}`).
|
|
|
pub nanomonsv_passed_vcf: String,
|
|
|
+
|
|
|
+ /// Template for solo nanomonsv output directory.
|
|
|
+ ///
|
|
|
+ /// Placeholders: `{result_dir}`, `{id}`, `{time}`.
|
|
|
pub nanomonsv_solo_output_dir: String,
|
|
|
+
|
|
|
+ /// Template for solo nanomonsv passed VCF (`{output_dir}`, `{id}`, `{time}`).
|
|
|
pub nanomonsv_solo_passed_vcf: String,
|
|
|
- pub somatic_pipe_force: bool,
|
|
|
- pub somatic_pipe_threads: u8,
|
|
|
- pub min_high_quality_depth: u32,
|
|
|
- pub min_n_callers: u8,
|
|
|
- pub somatic_scan_force: bool,
|
|
|
- pub early_bed: String,
|
|
|
- pub late_bed: String,
|
|
|
- pub panels: Vec<(String, String)>,
|
|
|
- pub cpg_bed: String,
|
|
|
- pub max_depth_low_quality: u32,
|
|
|
- pub bam_composition_sample_size: u32,
|
|
|
- pub promethion_runs_metadata_dir: String,
|
|
|
- pub promethion_runs_input: String,
|
|
|
-}
|
|
|
|
|
|
-// Here comes names that can't be changed from output of tools
|
|
|
-lazy_static! {
|
|
|
- static ref DEEPVARIANT_OUTPUT_NAME: &'static str = "{id}_{time}_DeepVariant.vcf.gz";
|
|
|
- static ref CLAIRS_OUTPUT_NAME: &'static str = "output.vcf.gz";
|
|
|
- static ref CLAIRS_OUTPUT_INDELS_NAME: &'static str = "indel.vcf.gz";
|
|
|
- static ref CLAIRS_GERMLINE_NORMAL: &'static str = "clair3_normal_germline_output.vcf.gz";
|
|
|
- static ref CLAIRS_GERMLINE_TUMOR: &'static str = "clair3_tumor_germline_output.vcf.gz";
|
|
|
-}
|
|
|
+ // === PromethION runs / metadata ===
|
|
|
+ /// Directory containing metadata about PromethION runs.
|
|
|
+ pub promethion_runs_metadata_dir: String,
|
|
|
|
|
|
-impl Default for Config {
|
|
|
- fn default() -> Self {
|
|
|
- Self {
|
|
|
- pod_dir: "/data/run_data".to_string(),
|
|
|
- align: Default::default(),
|
|
|
-
|
|
|
- // Reference genome
|
|
|
- reference: "/data/ref/hs1/chm13v2.0.fa".to_string(),
|
|
|
- reference_name: "hs1".to_string(),
|
|
|
- dict_file: "/data/ref/hs1/chm13v2.0.dict".to_string(),
|
|
|
- refseq_gff: "/data/ref/hs1/chm13v2.0_RefSeq_Liftoff_v5.1_sorted.gff3.gz".to_string(),
|
|
|
-
|
|
|
- docker_max_memory_go: 400,
|
|
|
-
|
|
|
- // File structure
|
|
|
- result_dir: "/data/longreads_basic_pipe".to_string(),
|
|
|
- unarchive_tmp_dir: "/data/unarchived".to_string(),
|
|
|
-
|
|
|
- tumoral_name: "diag".to_string(),
|
|
|
- normal_name: "mrd".to_string(),
|
|
|
- haplotagged_bam_tag_name: "HP".to_string(),
|
|
|
-
|
|
|
- count_dir_name: "counts".to_string(),
|
|
|
- count_bin_size: 1_000,
|
|
|
- count_n_chunks: 1_000,
|
|
|
-
|
|
|
- bam_min_mapq: 40,
|
|
|
- bam_n_threads: 150,
|
|
|
- bam_composition_sample_size: 20_000,
|
|
|
-
|
|
|
- promethion_runs_metadata_dir: "/data/promethion-runs-metadata".to_string(),
|
|
|
- promethion_runs_input: "/data/pandora-flowcell-id.json".to_string(),
|
|
|
-
|
|
|
- db_cases_path: "/data/cases.sqlite".to_string(),
|
|
|
-
|
|
|
- //
|
|
|
- mask_bed: "{result_dir}/{id}/diag/mask.bed".to_string(),
|
|
|
-
|
|
|
- germline_phased_vcf: "{result_dir}/{id}/diag/{id}_variants_constit_phased.vcf.gz"
|
|
|
- .to_string(),
|
|
|
- conda_sh: "/data/miniconda3/etc/profile.d/conda.sh".to_string(),
|
|
|
-
|
|
|
- somatic_pipe_stats: "{result_dir}/{id}/diag/somatic_pipe_stats"
|
|
|
- .to_string(),
|
|
|
-
|
|
|
- // DeepVariant
|
|
|
- deepvariant_output_dir: "{result_dir}/{id}/{time}/DeepVariant".to_string(),
|
|
|
- deepvariant_threads: 150,
|
|
|
- deepvariant_bin_version: "1.9.0".to_string(),
|
|
|
- deepvariant_model_type: "ONT_R104".to_string(),
|
|
|
- deepvariant_force: false,
|
|
|
-
|
|
|
- // DeepSomatic
|
|
|
- deepsomatic_output_dir: "{result_dir}/{id}/{time}/DeepSomatic".to_string(),
|
|
|
- deepsomatic_threads: 150,
|
|
|
- deepsomatic_bin_version: "1.9.0".to_string(),
|
|
|
- deepsomatic_model_type: "ONT".to_string(),
|
|
|
- deepsomatic_force: false,
|
|
|
-
|
|
|
- // ClairS
|
|
|
- clairs_output_dir: "{result_dir}/{id}/diag/ClairS".to_string(),
|
|
|
- clairs_docker_tag: "latest".to_string(),
|
|
|
- clairs_threads: 155,
|
|
|
- clairs_platform: "ont_r10_dorado_sup_5khz_ssrs".to_string(),
|
|
|
- clairs_force: false,
|
|
|
-
|
|
|
- // Savana
|
|
|
- savana_bin: "savana".to_string(),
|
|
|
- // savana_bin: "/home/prom/.local/bin/savana".to_string(),
|
|
|
- savana_threads: 150,
|
|
|
- savana_output_dir: "{result_dir}/{id}/diag/savana".to_string(),
|
|
|
- savana_passed_vcf: "{output_dir}/{id}_diag_savana_PASSED.vcf.gz".to_string(),
|
|
|
- savana_copy_number: "{output_dir}/{id}_diag_{reference_name}_{haplotagged_bam_tag_name}_segmented_absolute_copy_number.tsv".to_string(),
|
|
|
- savana_read_counts: "{output_dir}/{id}_diag_{reference_name}_{haplotagged_bam_tag_name}_raw_read_counts.tsv".to_string(),
|
|
|
- savana_force: false,
|
|
|
-
|
|
|
- // Severus
|
|
|
- severus_bin: "/data/tools/MySeverus/severus.py".to_string(),
|
|
|
- severus_threads: 32,
|
|
|
- vntrs_bed: "/data/ref/hs1/vntrs_chm13.bed".to_string(),
|
|
|
- severus_pon: "/data/ref/hs1/PoN_1000G_chm13.tsv.gz".to_string(),
|
|
|
- severus_output_dir: "{result_dir}/{id}/diag/severus".to_string(),
|
|
|
- severus_solo_output_dir: "{result_dir}/{id}/{time}/severus".to_string(),
|
|
|
- severus_force: false,
|
|
|
-
|
|
|
- // Longphase
|
|
|
- longphase_bin: "/data/tools/longphase_linux-x64".to_string(),
|
|
|
- longphase_threads: 150,
|
|
|
- longphase_modcall_threads: 8, // ! out of memory
|
|
|
- longphase_modcall_vcf:
|
|
|
- "{result_dir}/{id}/{time}/5mC_5hmC/{id}_{time}_5mC_5hmC_modcall.vcf.gz".to_string(),
|
|
|
-
|
|
|
- // modkit
|
|
|
- modkit_bin: "modkit".to_string(),
|
|
|
- modkit_summary_threads: 50,
|
|
|
- modkit_summary_file: "{result_dir}/{id}/{time}/{id}_{time}_5mC_5hmC_summary.txt"
|
|
|
- .to_string(),
|
|
|
-
|
|
|
- // Nanomonsv
|
|
|
- // tabix, bgzip, mafft in PATH
|
|
|
- // pip install pysam, parasail; pip install nanomonsv
|
|
|
- nanomonsv_bin: "/home/prom/.local/bin/nanomonsv".to_string(),
|
|
|
- nanomonsv_output_dir: "{result_dir}/{id}/{time}/nanomonsv".to_string(),
|
|
|
- nanomonsv_threads: 150,
|
|
|
- nanomonsv_force: false,
|
|
|
- nanomonsv_passed_vcf: "{output_dir}/{id}_diag_nanomonsv_PASSED.vcf.gz".to_string(),
|
|
|
-
|
|
|
- nanomonsv_solo_output_dir: "{result_dir}/{id}/{time}/nanomonsv-solo".to_string(),
|
|
|
- nanomonsv_solo_passed_vcf: "{output_dir}/{id}_{time}_nanomonsv-solo_PASSED.vcf.gz"
|
|
|
- .to_string(),
|
|
|
-
|
|
|
- // Scan
|
|
|
- somatic_scan_force: false,
|
|
|
-
|
|
|
- // Pipe
|
|
|
- somatic_pipe_force: true,
|
|
|
- somatic_pipe_threads: 150,
|
|
|
- somatic_min_constit_depth: 5,
|
|
|
- somatic_max_alt_constit: 1,
|
|
|
- entropy_seq_len: 10,
|
|
|
- min_shannon_entropy: 1.0,
|
|
|
-
|
|
|
- max_depth_low_quality: 20,
|
|
|
- min_high_quality_depth: 14,
|
|
|
- min_n_callers: 1,
|
|
|
- early_bed: "/data/ref/hs1/replication_early_25_hs1.bed".to_string(),
|
|
|
- late_bed: "/data/ref/hs1/replication_late_75_hs1.bed".to_string(),
|
|
|
- panels: vec![
|
|
|
- ("OncoT".to_string(), "/data/ref/hs1/V1_V2_V3_V4_V5_intersect_targets_hs1_uniq.bed".to_string()),
|
|
|
- ("variable_chips".to_string(), "/data/ref/hs1/top_1500_sd_pos.bed".to_string()),
|
|
|
- ],
|
|
|
- cpg_bed: "/data/ref/hs1/hs1/hs1_CpG.bed".to_string(),
|
|
|
- }
|
|
|
- }
|
|
|
+ /// JSON file describing PromethION runs and flowcell IDs.
|
|
|
+ pub promethion_runs_input: String,
|
|
|
}
|
|
|
|
|
|
-#[derive(Debug, Clone)]
|
|
|
+#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
+/// Configuration for basecalling and alignment using Dorado and samtools.
|
|
|
pub struct AlignConfig {
|
|
|
+ /// Path to Dorado binary.
|
|
|
pub dorado_bin: String,
|
|
|
+
|
|
|
+ /// Arguments passed to `dorado basecaller` (e.g. devices and model name).
|
|
|
pub dorado_basecall_arg: String,
|
|
|
+
|
|
|
+ /// Reference FASTA used for alignment.
|
|
|
pub ref_fa: String,
|
|
|
+
|
|
|
+ /// Minimap2 index (`.mmi`) used by Dorado or downstream tools.
|
|
|
pub ref_mmi: String,
|
|
|
+
|
|
|
+ /// Number of threads given to `samtools view`.
|
|
|
pub samtools_view_threads: u16,
|
|
|
+
|
|
|
+ /// Number of threads given to `samtools sort`.
|
|
|
pub samtools_sort_threads: u16,
|
|
|
}
|
|
|
|
|
|
+// Here comes names that can't be changed from output of tools
|
|
|
+lazy_static! {
|
|
|
+ /// Template name for DeepVariant VCF outputs.
|
|
|
+ static ref DEEPVARIANT_OUTPUT_NAME: &'static str = "{id}_{time}_DeepVariant.vcf.gz";
|
|
|
+ /// ClairS main SNP/indel VCF name.
|
|
|
+ static ref CLAIRS_OUTPUT_NAME: &'static str = "output.vcf.gz";
|
|
|
+ /// ClairS indel-only VCF name.
|
|
|
+ static ref CLAIRS_OUTPUT_INDELS_NAME: &'static str = "indel.vcf.gz";
|
|
|
+ /// ClairS germline normal VCF name.
|
|
|
+ static ref CLAIRS_GERMLINE_NORMAL: &'static str = "clair3_normal_germline_output.vcf.gz";
|
|
|
+ /// ClairS germline tumor VCF name.
|
|
|
+ static ref CLAIRS_GERMLINE_TUMOR: &'static str = "clair3_tumor_germline_output.vcf.gz";
|
|
|
+}
|
|
|
+
|
|
|
impl Default for AlignConfig {
|
|
|
fn default() -> Self {
|
|
|
Self {
|
|
|
@@ -254,18 +349,66 @@ impl Default for AlignConfig {
|
|
|
}
|
|
|
|
|
|
impl Config {
|
|
|
+ /// Returns the config file path, e.g.:
|
|
|
+ /// `~/.local/share/pandora/pandora-config.toml`.
|
|
|
+ fn config_path() -> PathBuf {
|
|
|
+ let mut path = directories::ProjectDirs::from("", "", "pandora")
|
|
|
+ .expect("Could not determine project directory")
|
|
|
+ .config_dir()
|
|
|
+ .to_path_buf();
|
|
|
+
|
|
|
+ path.push("pandora-config.toml");
|
|
|
+ path
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Install the commented template config on disk **if it does not exist yet**.
|
|
|
+ ///
|
|
|
+ /// This writes `CONFIG_TEMPLATE` verbatim so comments are preserved.
|
|
|
+ fn write_template_if_missing() -> Result<(), Box<dyn std::error::Error>> {
|
|
|
+ let path = Self::config_path();
|
|
|
+
|
|
|
+ if path.exists() {
|
|
|
+ // Do not touch an existing user config.
|
|
|
+ return Ok(());
|
|
|
+ }
|
|
|
+
|
|
|
+ if let Some(parent) = path.parent() {
|
|
|
+ fs::create_dir_all(parent)?;
|
|
|
+ }
|
|
|
+
|
|
|
+ fs::write(&path, CONFIG_TEMPLATE)?;
|
|
|
+ info!("Config template written to: {}", path.display());
|
|
|
+
|
|
|
+ Ok(())
|
|
|
+ }
|
|
|
+
|
|
|
+ /// “Save” configuration.
|
|
|
+ ///
|
|
|
+ /// In this model, we do **not** overwrite the user config (to preserve comments).
|
|
|
+ /// `save()` only ensures the template exists on disk on first run.
|
|
|
+ pub fn save(&self) -> Result<(), Box<dyn std::error::Error>> {
|
|
|
+ Self::write_template_if_missing()
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Returns `<result_dir>/<id>/<tumoral_name>`.
|
|
|
+ #[inline]
|
|
|
pub fn tumoral_dir(&self, id: &str) -> String {
|
|
|
format!("{}/{}/{}", self.result_dir, id, self.tumoral_name)
|
|
|
}
|
|
|
|
|
|
+ /// Returns `<result_dir>/<id>/<normal_name>`.
|
|
|
+ #[inline]
|
|
|
pub fn normal_dir(&self, id: &str) -> String {
|
|
|
format!("{}/{}/{}", self.result_dir, id, self.normal_name)
|
|
|
}
|
|
|
|
|
|
+ /// Returns the directory for a "solo" run (timepoint or tag), i.e. `<result_dir>/<id>/<time>`.
|
|
|
+ #[inline]
|
|
|
pub fn solo_dir(&self, id: &str, time: &str) -> String {
|
|
|
format!("{}/{}/{}", self.result_dir, id, time)
|
|
|
}
|
|
|
|
|
|
+ /// BAM for a solo run: `<solo_dir>/<id>_<time>_<reference_name>.bam`.
|
|
|
pub fn solo_bam(&self, id: &str, time: &str) -> String {
|
|
|
format!(
|
|
|
"{}/{}_{}_{}.bam",
|
|
|
@@ -276,6 +419,7 @@ impl Config {
|
|
|
)
|
|
|
}
|
|
|
|
|
|
+ /// JSON sidecar for the solo BAM.
|
|
|
pub fn solo_bam_info_json(&self, id: &str, time: &str) -> String {
|
|
|
format!(
|
|
|
"{}/{}_{}_{}_info.json",
|
|
|
@@ -286,6 +430,7 @@ impl Config {
|
|
|
)
|
|
|
}
|
|
|
|
|
|
+ /// Tumor BAM path: `<tumoral_dir>/<id>_<tumoral_name>_<reference_name>.bam`.
|
|
|
pub fn tumoral_bam(&self, id: &str) -> String {
|
|
|
format!(
|
|
|
"{}/{}_{}_{}.bam",
|
|
|
@@ -296,6 +441,7 @@ impl Config {
|
|
|
)
|
|
|
}
|
|
|
|
|
|
+ /// Normal BAM path: `<normal_dir>/<id>_<normal_name>_<reference_name>.bam`.
|
|
|
pub fn normal_bam(&self, id: &str) -> String {
|
|
|
format!(
|
|
|
"{}/{}_{}_{}.bam",
|
|
|
@@ -306,6 +452,7 @@ impl Config {
|
|
|
)
|
|
|
}
|
|
|
|
|
|
+ /// Tumor haplotagged BAM.
|
|
|
pub fn tumoral_haplotagged_bam(&self, id: &str) -> String {
|
|
|
format!(
|
|
|
"{}/{}_{}_{}_{}.bam",
|
|
|
@@ -317,6 +464,7 @@ impl Config {
|
|
|
)
|
|
|
}
|
|
|
|
|
|
+ /// Normal haplotagged BAM.
|
|
|
pub fn normal_haplotagged_bam(&self, id: &str) -> String {
|
|
|
format!(
|
|
|
"{}/{}_{}_{}_{}.bam",
|
|
|
@@ -328,33 +476,38 @@ impl Config {
|
|
|
)
|
|
|
}
|
|
|
|
|
|
+ /// Normal count directory: `<normal_dir>/counts`.
|
|
|
pub fn normal_dir_count(&self, id: &str) -> String {
|
|
|
format!("{}/{}", self.normal_dir(id), self.count_dir_name)
|
|
|
}
|
|
|
|
|
|
+ /// Tumor count directory: `<tumoral_dir>/counts`.
|
|
|
pub fn tumoral_dir_count(&self, id: &str) -> String {
|
|
|
format!("{}/{}", self.tumoral_dir(id), self.count_dir_name)
|
|
|
}
|
|
|
|
|
|
+ /// Mask BED path with `{result_dir}` and `{id}` expanded.
|
|
|
pub fn mask_bed(&self, id: &str) -> String {
|
|
|
self.mask_bed
|
|
|
.replace("{result_dir}", &self.result_dir)
|
|
|
.replace("{id}", id)
|
|
|
}
|
|
|
|
|
|
+ /// Germline phased VCF with `{result_dir}` and `{id}` expanded.
|
|
|
pub fn germline_phased_vcf(&self, id: &str) -> String {
|
|
|
self.germline_phased_vcf
|
|
|
.replace("{result_dir}", &self.result_dir)
|
|
|
.replace("{id}", id)
|
|
|
}
|
|
|
|
|
|
+ /// Somatic pipeline stats directory with `{result_dir}` and `{id}` expanded.
|
|
|
pub fn somatic_pipe_stats(&self, id: &str) -> String {
|
|
|
self.somatic_pipe_stats
|
|
|
.replace("{result_dir}", &self.result_dir)
|
|
|
.replace("{id}", id)
|
|
|
}
|
|
|
|
|
|
- // DeepVariant
|
|
|
+ /// DeepVariant output directory for a given run (`{result_dir}`, `{id}`, `{time}`).
|
|
|
pub fn deepvariant_output_dir(&self, id: &str, time: &str) -> String {
|
|
|
self.deepvariant_output_dir
|
|
|
.replace("{result_dir}", &self.result_dir)
|
|
|
@@ -362,6 +515,7 @@ impl Config {
|
|
|
.replace("{time}", time)
|
|
|
}
|
|
|
|
|
|
+ /// DeepVariant solo VCF (raw) for `<id>, <time>`.
|
|
|
pub fn deepvariant_solo_output_vcf(&self, id: &str, time: &str) -> String {
|
|
|
format!(
|
|
|
"{}/{}",
|
|
|
@@ -372,14 +526,17 @@ impl Config {
|
|
|
.replace("{time}", time)
|
|
|
}
|
|
|
|
|
|
+ /// DeepVariant output directory for the normal sample.
|
|
|
pub fn deepvariant_normal_output_dir(&self, id: &str) -> String {
|
|
|
self.deepvariant_output_dir(id, &self.normal_name)
|
|
|
}
|
|
|
|
|
|
+ /// DeepVariant "tumoral output dir" (as in your original code – note: this actually returns the *PASSED VCF* path).
|
|
|
pub fn deepvariant_tumoral_output_dir(&self, id: &str) -> String {
|
|
|
self.deepvariant_solo_passed_vcf(id, &self.tumoral_name)
|
|
|
}
|
|
|
|
|
|
+ /// DeepVariant solo *PASSED* VCF for `<id>, <time>`.
|
|
|
pub fn deepvariant_solo_passed_vcf(&self, id: &str, time: &str) -> String {
|
|
|
format!(
|
|
|
"{}/{}_{}_DeepVariant_PASSED.vcf.gz",
|
|
|
@@ -389,15 +546,17 @@ impl Config {
|
|
|
)
|
|
|
}
|
|
|
|
|
|
+ /// DeepVariant *PASSED* VCF for the normal sample.
|
|
|
pub fn deepvariant_normal_passed_vcf(&self, id: &str) -> String {
|
|
|
self.deepvariant_solo_passed_vcf(id, &self.normal_name)
|
|
|
}
|
|
|
|
|
|
+ /// DeepVariant *PASSED* VCF for the tumor sample.
|
|
|
pub fn deepvariant_tumoral_passed_vcf(&self, id: &str) -> String {
|
|
|
self.deepvariant_solo_passed_vcf(id, &self.tumoral_name)
|
|
|
}
|
|
|
|
|
|
- // DeepSomatic
|
|
|
+ /// DeepSomatic output directory (uses `{time} = tumoral_name`).
|
|
|
pub fn deepsomatic_output_dir(&self, id: &str) -> String {
|
|
|
self.deepsomatic_output_dir
|
|
|
.replace("{result_dir}", &self.result_dir)
|
|
|
@@ -405,6 +564,7 @@ impl Config {
|
|
|
.replace("{time}", &self.tumoral_name)
|
|
|
}
|
|
|
|
|
|
+ /// DeepSomatic raw VCF.
|
|
|
pub fn deepsomatic_output_vcf(&self, id: &str) -> String {
|
|
|
format!(
|
|
|
"{}/{}_{}_DeepSomatic.vcf.gz",
|
|
|
@@ -414,6 +574,7 @@ impl Config {
|
|
|
)
|
|
|
}
|
|
|
|
|
|
+ /// DeepSomatic *PASSED* VCF.
|
|
|
pub fn deepsomatic_passed_vcf(&self, id: &str) -> String {
|
|
|
format!(
|
|
|
"{}/{}_{}_DeepSomatic_PASSED.vcf.gz",
|
|
|
@@ -423,13 +584,14 @@ impl Config {
|
|
|
)
|
|
|
}
|
|
|
|
|
|
- // ClairS
|
|
|
+ /// ClairS output directory (`{result_dir}`, `{id}`).
|
|
|
pub fn clairs_output_dir(&self, id: &str) -> String {
|
|
|
self.clairs_output_dir
|
|
|
.replace("{result_dir}", &self.result_dir)
|
|
|
.replace("{id}", id)
|
|
|
}
|
|
|
|
|
|
+ /// ClairS main SNP/indel VCFs (standard + indel-only).
|
|
|
pub fn clairs_output_vcfs(&self, id: &str) -> (String, String) {
|
|
|
let dir = self.clairs_output_dir(id);
|
|
|
(
|
|
|
@@ -438,6 +600,7 @@ impl Config {
|
|
|
)
|
|
|
}
|
|
|
|
|
|
+ /// ClairS somatic *PASSED* VCF.
|
|
|
pub fn clairs_passed_vcf(&self, id: &str) -> String {
|
|
|
format!(
|
|
|
"{}/{}_{}_clairs_PASSED.vcf.gz",
|
|
|
@@ -447,22 +610,25 @@ impl Config {
|
|
|
)
|
|
|
}
|
|
|
|
|
|
+ /// ClairS germline normal VCF.
|
|
|
pub fn clairs_germline_normal_vcf(&self, id: &str) -> String {
|
|
|
let dir = self.clairs_output_dir(id);
|
|
|
format!("{dir}/{}", *CLAIRS_GERMLINE_NORMAL)
|
|
|
}
|
|
|
|
|
|
+ /// ClairS germline tumor VCF.
|
|
|
pub fn clairs_germline_tumor_vcf(&self, id: &str) -> String {
|
|
|
let dir = self.clairs_output_dir(id);
|
|
|
format!("{dir}/{}", *CLAIRS_GERMLINE_TUMOR)
|
|
|
}
|
|
|
|
|
|
+ /// Consolidated germline *PASSED* VCF from ClairS.
|
|
|
pub fn clairs_germline_passed_vcf(&self, id: &str) -> String {
|
|
|
let dir = self.clairs_output_dir(id);
|
|
|
format!("{dir}/{id}_diag_clair3-germline_PASSED.vcf.gz")
|
|
|
}
|
|
|
|
|
|
- // Nanomonsv
|
|
|
+ /// Paired nanomonsv output directory.
|
|
|
pub fn nanomonsv_output_dir(&self, id: &str, time: &str) -> String {
|
|
|
self.nanomonsv_output_dir
|
|
|
.replace("{result_dir}", &self.result_dir)
|
|
|
@@ -470,13 +636,14 @@ impl Config {
|
|
|
.replace("{time}", time)
|
|
|
}
|
|
|
|
|
|
+ /// Paired nanomonsv *PASSED* VCF.
|
|
|
pub fn nanomonsv_passed_vcf(&self, id: &str) -> String {
|
|
|
self.nanomonsv_passed_vcf
|
|
|
.replace("{output_dir}", &self.nanomonsv_output_dir(id, "diag"))
|
|
|
.replace("{id}", id)
|
|
|
}
|
|
|
|
|
|
- // Nanomonsv solo
|
|
|
+ /// Solo nanomonsv output directory.
|
|
|
pub fn nanomonsv_solo_output_dir(&self, id: &str, time: &str) -> String {
|
|
|
self.nanomonsv_solo_output_dir
|
|
|
.replace("{result_dir}", &self.result_dir)
|
|
|
@@ -484,6 +651,7 @@ impl Config {
|
|
|
.replace("{time}", time)
|
|
|
}
|
|
|
|
|
|
+ /// Solo nanomonsv *PASSED* VCF.
|
|
|
pub fn nanomonsv_solo_passed_vcf(&self, id: &str, time: &str) -> String {
|
|
|
self.nanomonsv_solo_passed_vcf
|
|
|
.replace("{output_dir}", &self.nanomonsv_solo_output_dir(id, time))
|
|
|
@@ -491,13 +659,14 @@ impl Config {
|
|
|
.replace("{time}", time)
|
|
|
}
|
|
|
|
|
|
- // Savana
|
|
|
+ /// Savana output directory (`{result_dir}`, `{id}`).
|
|
|
pub fn savana_output_dir(&self, id: &str) -> String {
|
|
|
self.savana_output_dir
|
|
|
.replace("{result_dir}", &self.result_dir)
|
|
|
.replace("{id}", id)
|
|
|
}
|
|
|
|
|
|
+ /// Savana main somatic VCF (classified).
|
|
|
pub fn savana_output_vcf(&self, id: &str) -> String {
|
|
|
let output_dir = self.savana_output_dir(id);
|
|
|
|
|
|
@@ -507,13 +676,14 @@ impl Config {
|
|
|
)
|
|
|
}
|
|
|
|
|
|
+ /// Savana *PASSED* VCF.
|
|
|
pub fn savana_passed_vcf(&self, id: &str) -> String {
|
|
|
self.savana_passed_vcf
|
|
|
.replace("{output_dir}", &self.savana_output_dir(id))
|
|
|
.replace("{id}", id)
|
|
|
}
|
|
|
|
|
|
- // {output_dir}/{id}_diag_{reference_name}_{haplotagged_bam_tag_name}
|
|
|
+ /// Savana read counts file.
|
|
|
pub fn savana_read_counts(&self, id: &str) -> String {
|
|
|
self.savana_read_counts
|
|
|
.replace("{output_dir}", &self.savana_output_dir(id))
|
|
|
@@ -522,6 +692,7 @@ impl Config {
|
|
|
.replace("{haplotagged_bam_tag_name}", &self.haplotagged_bam_tag_name)
|
|
|
}
|
|
|
|
|
|
+ /// Savana copy-number file.
|
|
|
pub fn savana_copy_number(&self, id: &str) -> String {
|
|
|
self.savana_copy_number
|
|
|
.replace("{output_dir}", &self.savana_output_dir(id))
|
|
|
@@ -530,18 +701,20 @@ impl Config {
|
|
|
.replace("{haplotagged_bam_tag_name}", &self.haplotagged_bam_tag_name)
|
|
|
}
|
|
|
|
|
|
- // Severus
|
|
|
+ /// Severus paired output directory.
|
|
|
pub fn severus_output_dir(&self, id: &str) -> String {
|
|
|
self.severus_output_dir
|
|
|
.replace("{result_dir}", &self.result_dir)
|
|
|
.replace("{id}", id)
|
|
|
}
|
|
|
|
|
|
+ /// Severus somatic SV VCF (paired).
|
|
|
pub fn severus_output_vcf(&self, id: &str) -> String {
|
|
|
let output_dir = self.severus_output_dir(id);
|
|
|
format!("{output_dir}/somatic_SVs/severus_somatic.vcf")
|
|
|
}
|
|
|
|
|
|
+ /// Severus *PASSED* VCF (paired).
|
|
|
pub fn severus_passed_vcf(&self, id: &str) -> String {
|
|
|
format!(
|
|
|
"{}/{}_diag_severus_PASSED.vcf.gz",
|
|
|
@@ -550,7 +723,7 @@ impl Config {
|
|
|
)
|
|
|
}
|
|
|
|
|
|
- // Severus solo
|
|
|
+ /// Severus solo output directory.
|
|
|
pub fn severus_solo_output_dir(&self, id: &str, time: &str) -> String {
|
|
|
self.severus_solo_output_dir
|
|
|
.replace("{result_dir}", &self.result_dir)
|
|
|
@@ -558,11 +731,13 @@ impl Config {
|
|
|
.replace("{time}", time)
|
|
|
}
|
|
|
|
|
|
+ /// Severus solo SV VCF.
|
|
|
pub fn severus_solo_output_vcf(&self, id: &str, time: &str) -> String {
|
|
|
let output_dir = self.severus_solo_output_dir(id, time);
|
|
|
format!("{output_dir}/all_SVs/severus_all.vcf")
|
|
|
}
|
|
|
|
|
|
+ /// Severus solo *PASSED* VCF.
|
|
|
pub fn severus_solo_passed_vcf(&self, id: &str, time: &str) -> String {
|
|
|
format!(
|
|
|
"{}/{}_{}_severus-solo_PASSED.vcf.gz",
|
|
|
@@ -572,11 +747,12 @@ impl Config {
|
|
|
)
|
|
|
}
|
|
|
|
|
|
+ /// Alias for the constitutional germline VCF.
|
|
|
pub fn constit_vcf(&self, id: &str) -> String {
|
|
|
self.clairs_germline_passed_vcf(id)
|
|
|
- // format!("{}/{}_variants_constit.vcf.gz", self.tumoral_dir(id), id)
|
|
|
}
|
|
|
|
|
|
+ /// Constitutional phased VCF path in the tumor directory.
|
|
|
pub fn constit_phased_vcf(&self, id: &str) -> String {
|
|
|
format!(
|
|
|
"{}/{}_variants_constit_phased.vcf.gz",
|
|
|
@@ -585,19 +761,22 @@ impl Config {
|
|
|
)
|
|
|
}
|
|
|
|
|
|
- // SomaticScan
|
|
|
+ /// Somatic-scan output directory for a solo run (counts subdir).
|
|
|
pub fn somatic_scan_solo_output_dir(&self, id: &str, time: &str) -> String {
|
|
|
format!("{}/counts", self.solo_dir(id, time))
|
|
|
}
|
|
|
|
|
|
+ /// Somatic-scan output dir for the normal sample.
|
|
|
pub fn somatic_scan_normal_output_dir(&self, id: &str) -> String {
|
|
|
self.somatic_scan_solo_output_dir(id, &self.normal_name)
|
|
|
}
|
|
|
|
|
|
+ /// Somatic-scan output dir for the tumor sample.
|
|
|
pub fn somatic_scan_tumoral_output_dir(&self, id: &str) -> String {
|
|
|
self.somatic_scan_solo_output_dir(id, &self.tumoral_name)
|
|
|
}
|
|
|
|
|
|
+ /// Somatic-scan count file for a given contig in a solo run.
|
|
|
pub fn somatic_scan_solo_count_file(&self, id: &str, time: &str, contig: &str) -> String {
|
|
|
format!(
|
|
|
"{}/{}_count.tsv.gz",
|
|
|
@@ -606,15 +785,17 @@ impl Config {
|
|
|
)
|
|
|
}
|
|
|
|
|
|
+ /// Somatic-scan count file (normal) for a given contig.
|
|
|
pub fn somatic_scan_normal_count_file(&self, id: &str, contig: &str) -> String {
|
|
|
self.somatic_scan_solo_count_file(id, &self.normal_name, contig)
|
|
|
}
|
|
|
|
|
|
+ /// Somatic-scan count file (tumor) for a given contig.
|
|
|
pub fn somatic_scan_tumoral_count_file(&self, id: &str, contig: &str) -> String {
|
|
|
self.somatic_scan_solo_count_file(id, &self.tumoral_name, contig)
|
|
|
}
|
|
|
|
|
|
- // Modkit
|
|
|
+ /// Modkit summary file (`{result_dir}`, `{id}`, `{time}`).
|
|
|
pub fn modkit_summary_file(&self, id: &str, time: &str) -> String {
|
|
|
self.modkit_summary_file
|
|
|
.replace("{result_dir}", &self.result_dir)
|
|
|
@@ -622,6 +803,7 @@ impl Config {
|
|
|
.replace("{time}", time)
|
|
|
}
|
|
|
|
|
|
+ /// Longphase modcall VCF (`{result_dir}`, `{id}`, `{time}`).
|
|
|
pub fn longphase_modcall_vcf(&self, id: &str, time: &str) -> String {
|
|
|
self.longphase_modcall_vcf
|
|
|
.replace("{result_dir}", &self.result_dir)
|
|
|
@@ -629,3 +811,45 @@ impl Config {
|
|
|
.replace("{time}", time)
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+impl Default for Config {
|
|
|
+ fn default() -> Self {
|
|
|
+ let path = Self::config_path();
|
|
|
+
|
|
|
+ // First, ensure there is at least a file on disk (template on first run).
|
|
|
+ if let Err(e) = Self::write_template_if_missing() {
|
|
|
+ warn!(
|
|
|
+ "Warning: failed to ensure config template at {}: {}",
|
|
|
+ path.display(),
|
|
|
+ e
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ // Try to load and parse the user config file.
|
|
|
+ match fs::read_to_string(&path) {
|
|
|
+ Ok(content) => match toml::from_str::<Config>(&content) {
|
|
|
+ Ok(cfg) => cfg,
|
|
|
+ Err(e) => {
|
|
|
+ warn!(
|
|
|
+ "Warning: failed to parse user config {}: {}. Falling back to embedded template.",
|
|
|
+ path.display(),
|
|
|
+ e
|
|
|
+ );
|
|
|
+ // Fallback: parse the embedded template.
|
|
|
+ toml::from_str::<Config>(CONFIG_TEMPLATE)
|
|
|
+ .expect("embedded config template is invalid")
|
|
|
+ }
|
|
|
+ },
|
|
|
+ Err(e) => {
|
|
|
+ warn!(
|
|
|
+ "Warning: failed to read user config {}: {}. Falling back to embedded template.",
|
|
|
+ path.display(),
|
|
|
+ e
|
|
|
+ );
|
|
|
+ toml::from_str::<Config>(CONFIG_TEMPLATE)
|
|
|
+ .expect("embedded config template is invalid")
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|