From 5dc39af4af92d61923a8bee92e6352ab41de2898 Mon Sep 17 00:00:00 2001 From: LucaCappelletti94 Date: Tue, 13 Feb 2024 14:40:23 +0100 Subject: [PATCH] Fixed test for renamed crate --- bindings/sirius-bindings/.gitignore | 2 + bindings/sirius-bindings/CONTRIB.md | 513 ++++++ bindings/sirius-bindings/Cargo.toml | 23 + bindings/sirius-bindings/README.md | 220 +++ bindings/sirius-bindings/dot_env_template | 3 + bindings/sirius-bindings/fuzz/.gitignore | 4 + bindings/sirius-bindings/fuzz/Cargo.toml | 30 + bindings/sirius-bindings/fuzz/README.md | 15 + .../fuzz/fuzz_targets/random.rs | 877 +++++++++ bindings/sirius-bindings/src/builder.rs | 1575 +++++++++++++++++ bindings/sirius-bindings/src/lib.rs | 22 + .../sirius-bindings/src/parameters/canopus.rs | 50 + .../sirius-bindings/src/parameters/config.rs | 786 ++++++++ .../sirius-bindings/src/parameters/core.rs | 33 + .../src/parameters/fingerprint.rs | 50 + .../sirius-bindings/src/parameters/formula.rs | 50 + .../sirius-bindings/src/parameters/mod.rs | 32 + .../src/parameters/structure.rs | 50 + .../src/parameters/write_summaries.rs | 50 + .../sirius-bindings/src/parameters/zodiac.rs | 50 + bindings/sirius-bindings/src/sirius.rs | 203 +++ bindings/sirius-bindings/src/sirius_config.rs | 360 ++++ .../src/sirius_types/README.md | 4 + .../sirius_types/adduct_settings_enforced.rs | 37 + .../src/sirius_types/adducts.rs | 182 ++ .../sirius-bindings/src/sirius_types/atoms.rs | 645 +++++++ .../src/sirius_types/candidate_formulas.rs | 37 + .../src/sirius_types/compound_quality.rs | 72 + .../src/sirius_types/forbid_recalibration.rs | 42 + .../formula_result_ranking_score.rs | 37 + .../src/sirius_types/instruments.rs | 52 + .../src/sirius_types/isotope_ms2_settings.rs | 37 + .../src/sirius_types/mass_deviation.rs | 169 ++ .../sirius-bindings/src/sirius_types/mod.rs | 32 + .../sirius_types/noise_threshold_settings.rs | 37 + .../sirius_types/possible_adduct_switches.rs | 41 + .../src/sirius_types/search_db.rs | 193 ++ .../src/sirius_types/structure_predictors.rs | 37 + .../sirius-bindings/src/traits/enablable.rs | 8 + .../src/traits/into_default.rs | 5 + bindings/sirius-bindings/src/traits/mod.rs | 8 + .../src/traits/named_parameters_set.rs | 4 + bindings/sirius-bindings/src/versions.rs | 52 + .../tests/data/input_sirius.mgf | 1092 ++++++++++++ bindings/sirius-bindings/tests/test_sirius.rs | 166 ++ 45 files changed, 7987 insertions(+) create mode 100644 bindings/sirius-bindings/.gitignore create mode 100644 bindings/sirius-bindings/CONTRIB.md create mode 100644 bindings/sirius-bindings/Cargo.toml create mode 100644 bindings/sirius-bindings/README.md create mode 100644 bindings/sirius-bindings/dot_env_template create mode 100644 bindings/sirius-bindings/fuzz/.gitignore create mode 100644 bindings/sirius-bindings/fuzz/Cargo.toml create mode 100644 bindings/sirius-bindings/fuzz/README.md create mode 100644 bindings/sirius-bindings/fuzz/fuzz_targets/random.rs create mode 100644 bindings/sirius-bindings/src/builder.rs create mode 100644 bindings/sirius-bindings/src/lib.rs create mode 100644 bindings/sirius-bindings/src/parameters/canopus.rs create mode 100644 bindings/sirius-bindings/src/parameters/config.rs create mode 100644 bindings/sirius-bindings/src/parameters/core.rs create mode 100644 bindings/sirius-bindings/src/parameters/fingerprint.rs create mode 100644 bindings/sirius-bindings/src/parameters/formula.rs create mode 100644 bindings/sirius-bindings/src/parameters/mod.rs create mode 100644 bindings/sirius-bindings/src/parameters/structure.rs create mode 100644 bindings/sirius-bindings/src/parameters/write_summaries.rs create mode 100644 bindings/sirius-bindings/src/parameters/zodiac.rs create mode 100644 bindings/sirius-bindings/src/sirius.rs create mode 100644 bindings/sirius-bindings/src/sirius_config.rs create mode 100644 bindings/sirius-bindings/src/sirius_types/README.md create mode 100644 bindings/sirius-bindings/src/sirius_types/adduct_settings_enforced.rs create mode 100644 bindings/sirius-bindings/src/sirius_types/adducts.rs create mode 100644 bindings/sirius-bindings/src/sirius_types/atoms.rs create mode 100644 bindings/sirius-bindings/src/sirius_types/candidate_formulas.rs create mode 100644 bindings/sirius-bindings/src/sirius_types/compound_quality.rs create mode 100644 bindings/sirius-bindings/src/sirius_types/forbid_recalibration.rs create mode 100644 bindings/sirius-bindings/src/sirius_types/formula_result_ranking_score.rs create mode 100644 bindings/sirius-bindings/src/sirius_types/instruments.rs create mode 100644 bindings/sirius-bindings/src/sirius_types/isotope_ms2_settings.rs create mode 100644 bindings/sirius-bindings/src/sirius_types/mass_deviation.rs create mode 100644 bindings/sirius-bindings/src/sirius_types/mod.rs create mode 100644 bindings/sirius-bindings/src/sirius_types/noise_threshold_settings.rs create mode 100644 bindings/sirius-bindings/src/sirius_types/possible_adduct_switches.rs create mode 100644 bindings/sirius-bindings/src/sirius_types/search_db.rs create mode 100644 bindings/sirius-bindings/src/sirius_types/structure_predictors.rs create mode 100644 bindings/sirius-bindings/src/traits/enablable.rs create mode 100644 bindings/sirius-bindings/src/traits/into_default.rs create mode 100644 bindings/sirius-bindings/src/traits/mod.rs create mode 100644 bindings/sirius-bindings/src/traits/named_parameters_set.rs create mode 100644 bindings/sirius-bindings/src/versions.rs create mode 100644 bindings/sirius-bindings/tests/data/input_sirius.mgf create mode 100644 bindings/sirius-bindings/tests/test_sirius.rs diff --git a/bindings/sirius-bindings/.gitignore b/bindings/sirius-bindings/.gitignore new file mode 100644 index 00000000..9f7485a7 --- /dev/null +++ b/bindings/sirius-bindings/.gitignore @@ -0,0 +1,2 @@ +tests/data/output_sirius +tests/data/output_sirius_default \ No newline at end of file diff --git a/bindings/sirius-bindings/CONTRIB.md b/bindings/sirius-bindings/CONTRIB.md new file mode 100644 index 00000000..231e9ae1 --- /dev/null +++ b/bindings/sirius-bindings/CONTRIB.md @@ -0,0 +1,513 @@ +# Implementation details + +## First we need to login + +```bash +sirius login --user-env SIRIUS_USERNAME --password-env SIRIUS_PASSWORD +``` + + + +Example of a minimal sirius command line + +```bash +sirius -i tests/data/input_sirius.mgf --output tests/data/output_sirius --maxmz=1000 config --IsotopeSettings.filter=False formula zodiac fingerprint structure canopus write-summaries +``` + +sirius -i tests/data/input_sirius.mgf --output tests/data/output_sirius --maxmz=802.2 config --IsotopeSettings.filter=False --FormulaSearchDB=BIO canopus write-summaries + + + +Example of a typical sirius command line see https://github.com/enpkg/enpkg_full/blob/c8e649290ee72f000c3385e7669b5da2215abad8/params/user.yml#L60 + +```bash +sirius -i 'tests/data/input_sirius.mgf' --output 'tests/data/output_sirius' --maxmz 800 config --IsotopeSettings.filter=true --FormulaSearchDB=BIO --Timeout.secondsPerTree=0 --FormulaSettings.enforced=HCNOP --Timeout.secondsPerInstance=0 --AdductSettings.detectable=[[M+Na]+,[M+H3N+H]+,[M-H4O2+H]+,[M+K]+,[M+H]+,[M-H2O+H]+] --UseHeuristic.mzToUseHeuristicOnly=650 --AlgorithmProfile=qtof --IsotopeMs2Settings=IGNORE --MS2MassDeviation.allowedMassDeviation=10.0ppm --NumberOfCandidatesPerIon=1 --UseHeuristic.mzToUseHeuristic=300 --FormulaSettings.detectable=B,Cl,Br,Se,S --NumberOfCandidates=10 --ZodiacNumberOfConsideredCandidatesAt300Mz=10 --ZodiacRunInTwoSteps=true --ZodiacEdgeFilterThresholds.minLocalConnections=10 --ZodiacEdgeFilterThresholds.thresholdFilter=0.95 --ZodiacEpochs.burnInPeriod=2000 --ZodiacEpochs.numberOfMarkovChains=10 --ZodiacNumberOfConsideredCandidatesAt800Mz=50 --ZodiacEpochs.iterations=20000 --AdductSettings.enforced=, --AdductSettings.fallback=[[M+Na]+,[M-H+K+K]+,[M+K]+,[M+H]+,[M-H2O+H]+] --FormulaResultThreshold=true --InjectElGordoCompounds=true --StructureSearchDB=BIO --RecomputeResults=false formula zodiac fingerprint structure canopus write-summaries --output 'tests/data/output_sirius' +``` + +The order of the arguments is important. +This will work + +```bash +/Applications/sirius.app/Contents/MacOS/sirius -i tests/data/input_sirius.mgf --output tests/data/output_sirius --maxmz 800 config --IsotopeSettings.filter=true --FormulaSearchDB=BIO --Timeout.secondsPerTree=0 --FormulaSettings.enforced=HCNOP --Timeout.secondsPerInstance=0 --AdductSettings.detectable='[[M+H]+,[M-H4O2+H]+,[M+Na]+,[M+K]+,[M+H3N+H]+,[M-H2O+H]+]' --UseHeuristic.mzToUseHeuristicOnly=650 --AlgorithmProfile=orbitrap --IsotopeMs2Settings=IGNORE --MS2MassDeviation.allowedMassDeviation=5.0ppm --NumberOfCandidatesPerIon=1 --UseHeuristic.mzToUseHeuristic=300 --FormulaSettings.detectable=B,Cl,Br,Se,S --NumberOfCandidates=10 --ZodiacNumberOfConsideredCandidatesAt300Mz=10 --ZodiacRunInTwoSteps=true --ZodiacEdgeFilterThresholds.minLocalConnections=10 --ZodiacEdgeFilterThresholds.thresholdFilter=0.95 --ZodiacEpochs.burnInPeriod=2000 --ZodiacEpochs.numberOfMarkovChains=10 --ZodiacNumberOfConsideredCandidatesAt800Mz=50 --ZodiacEpochs.iterations=20000 --AdductSettings.enforced=, --AdductSettings.fallback='[[M+H]+,[M+Na]+,[M+K]+]' --FormulaResultThreshold=true --InjectElGordoCompounds=true --StructureSearchDB=BIO --RecomputeResults=false formula zodiac fingerprint structure canopus write-summaries +``` + + +This wil not + +```bash + +/Applications/sirius.app/Contents/MacOS/sirius -i tests/data/input_sirius.mgf --output tests/data/output_sirius --maxmz 800 formula zodiac fingerprint structure canopus write-summaries config --IsotopeSettings.filter=true --FormulaSearchDB=BIO --Timeout.secondsPerTree=0 --FormulaSettings.enforced=HCNOP --Timeout.secondsPerInstance=0 --AdductSettings.detectable='[[M+H]+,[M-H4O2+H]+,[M+Na]+,[M+K]+,[M+H3N+H]+,[M-H2O+H]+]' --UseHeuristic.mzToUseHeuristicOnly=650 --AlgorithmProfile=orbitrap --IsotopeMs2Settings=IGNORE --MS2MassDeviation.allowedMassDeviation=5.0ppm --NumberOfCandidatesPerIon=1 --UseHeuristic.mzToUseHeuristic=300 --FormulaSettings.detectable=B,Cl,Br,Se,S --NumberOfCandidates=10 --ZodiacNumberOfConsideredCandidatesAt300Mz=10 --ZodiacRunInTwoSteps=true --ZodiacEdgeFilterThresholds.minLocalConnections=10 --ZodiacEdgeFilterThresholds.thresholdFilter=0.95 --ZodiacEpochs.burnInPeriod=2000 --ZodiacEpochs.numberOfMarkovChains=10 --ZodiacNumberOfConsideredCandidatesAt800Mz=50 --ZodiacEpochs.iterations=20000 --AdductSettings.enforced=, --AdductSettings.fallback='[[M+H]+,[M+Na]+,[M+K]+]' --FormulaResultThreshold=true --InjectElGordoCompounds=true --StructureSearchDB=BIO --RecomputeResults=false +``` + +```bash + +/Applications/sirius.app/Contents/MacOS/sirius -i tests/data/input_sirius.mgf --output tests/data/output_sirius --maxmz 1000 config --IsotopeSettings.filter=true --FormulaSearchDB=BIO canopus write-summaries + +```bash +``` + + +Sirius takes nested command line arguments. + +For example : + + +## Sirius Core Params + +These are the sirius core params + +```bash +sirius [-hV] [--noCite] [--recompute] [--cores=] + [--instance-buffer=] [--log=] + [--maxmz=] [--workspace=] + [[-o=] [--no-compression] + [--update-fingerprint-version] + [--naming-convention=]] + [[[--ignore-formula] [--allow-ms1-only] -i=[, + ...] [-i=[,...]]...] + [-z= [--adduct=] [-f=] [-1=[, + ...]] -2=[,...]]...] [COMMAND] +``` + + +They are detailed below (also available with `sirius --help`) + +```bash + +-h, --help Show this help message and exit. +-V, --version Print version information and exit. +--log, --loglevel= + Set logging level of the Jobs SIRIUS will execute. + Valid values: SEVERE, WARNING, INFO, FINER, ALL + Default: WARNING +--cores, --processors= + Number of cpu cores to use. If not specified Sirius + uses all available cores. +--instance-buffer, --compound-buffer, + --initial-compound-buffer= + Number of compounds that will be loaded into the + Memory. A larger buffer ensures that there are + enough compounds available to use all cores + efficiently during computation. A smaller buffer + saves Memory. To load all compounds immediately + set it to -1. Default (numeric value 0): 3 x + --cores. Note that for the + compound buffer may have no effect because this + tools may have to load compounds simultaneously + into the memory. + Default: 0 +--workspace= + Specify sirius workspace location. This is the + directory for storing Property files, logs, + databases and caches. This is NOT for the + project-space that stores the results! Default is + $USER_HOME/.sirius- +--recompute Recompute results of ALL tools where results are + already present. Per default already present + results will be preserved and the instance will + be skipped for the corresponding Task/Tool +--maxmz= Only considers compounds with a precursor m/z lower + or equal [--maxmz]. All other compounds in the + input will be skipped. + Default: Infinity +--noCite, --noCitations, --no-citations + Do not write summary files to the project-space + +Specify OUTPUT Project-Space: +-o, --output, --project= + Specify the project-space to write into. If no + [--input] is specified it is also used as input. + For compression use the File ending .zip or . + sirius. +--naming-convention= + Specify a naming scheme for the compound + directories ins the project-space. Default % + index_%filename_%compoundname +--no-compression Does not use compressed project-space format (not + recommended) when creating the project-space. If + an existing project-space is opened this + parameter has no effect. +--update-fingerprint-version + Updates Fingerprint versions of the input project + to the one used by this SIRIUS version. + WARNING: All Fingerprint related results (CSI: + FingerID, CANOPUS) will be lost! + +Specify multi-compound inputs (.ms, .mgf, .mzML/.mzXml, .sirius): + -i, --input=[,...] + Specify the input in multi-compound input formats: + Preprocessed mass spectra in .ms or .mgf file + format, LC/MS runs in .mzML/.mzXml format or + already existing SIRIUS project-spaces + (uncompressed/compressed) but also any other file + type e.g. to provide input for STANDALONE tools. + --ignore-formula ignore given molecular formula if present in .ms or + .mgf input files. + --allow-ms1-only Allow MS1 only data to be imported. +Specify generic inputs (CSV) on per compound level: + -1, --ms1=[,...] + MS1 spectra files + -2, --ms2=[,...] + MS2 spectra files + -z, --mz, --precursor, --parentmass= + The mass of the parent ion for the specified ms2 + spectra + --adduct, --ionization= + Specify the adduct for this compound + Default: [M+?]+ + -f, --formula= Specify the neutralized formula of this compound. + This will be used for tree computation. If given + no mass decomposition will be performed. + +``` + + +## Sirius Config Params + +The sirius config params are typicall specified after the sirius core params. + +```bash +sirius config [-hV] [--AdductSettings.detectable=[M+H]+,[M+K]+,[M+Na]+, + [M+H-H2O]+,[M+H-H4O2]+,[M+NH4]+,[M-H]-,[M+Cl]-,[M-H2O-H]-, + [M+Br]-] [--AdductSettings.enforced=,] [--AdductSettings. + fallback=[M+H]+,[M-H]-,[M+Na]+,[M+K]+] + [--AlgorithmProfile=default] [--CandidateFormulas=,] + [--CompoundQuality=UNKNOWN] + [--ForbidRecalibration=ALLOWED] + [--FormulaResultRankingScore=AUTO] + [--FormulaResultThreshold=true] [--FormulaSearchDB=none] + [--FormulaSettings.detectable=S,Br,Cl,B,Se] + [--FormulaSettings.enforced=C,H,N,O,P] [--FormulaSettings. + fallback=S] [--InjectElGordoCompounds=True] + [--IsotopeMs2Settings=IGNORE] [--IsotopeSettings. + filter=True] [--IsotopeSettings.multiplier=1] + [--MedianNoiseIntensity=0.015] [--MotifDbFile=none] [--ms1. + absoluteIntensityError=0.02] [--ms1. + minimalIntensityToConsider=0.01] [--ms1. + relativeIntensityError=0.08] [--MS1MassDeviation. + allowedMassDeviation=10.0 ppm] [--MS1MassDeviation. + massDifferenceDeviation=5.0 ppm] [--MS1MassDeviation. + standardMassDeviation=10.0 ppm] [--MS2MassDeviation. + allowedMassDeviation=10.0 ppm] [--MS2MassDeviation. + standardMassDeviation=10.0 ppm] [--NoiseThresholdSettings. + absoluteThreshold=0] [--NoiseThresholdSettings. + basePeak=NOT_PRECURSOR] [--NoiseThresholdSettings. + intensityThreshold=0.005] [--NoiseThresholdSettings. + maximalNumberOfPeaks=60] [--NumberOfCandidates=10] + [--NumberOfCandidatesPerIon=1] + [--NumberOfStructureCandidates=10000] + [--PossibleAdductSwitches=[M+Na]+:[M+H]+,[M+K]+:[M+H]+, + [M+Cl]-:[M-H]-] [--PrintCitations=True] + [--RecomputeResults=False] + [--StructurePredictors=CSI_FINGERID] + [--StructureSearchDB=BIO] [--Timeout.secondsPerInstance=0] + [--Timeout.secondsPerTree=0] [--UseHeuristic. + mzToUseHeuristic=300] [--UseHeuristic. + mzToUseHeuristicOnly=650] [--ZodiacClusterCompounds=false] + [--ZodiacEdgeFilterThresholds.minLocalCandidates=1] + [--ZodiacEdgeFilterThresholds.minLocalConnections=10] + [--ZodiacEdgeFilterThresholds.thresholdFilter=0.95] + [--ZodiacEpochs.burnInPeriod=2000] [--ZodiacEpochs. + iterations=20000] [--ZodiacEpochs.numberOfMarkovChains=10] + [--ZodiacLibraryScoring.lambda=1000] + [--ZodiacLibraryScoring.minCosine=0.5] + [--ZodiacNumberOfConsideredCandidatesAt300Mz=10] + [--ZodiacNumberOfConsideredCandidatesAt800Mz=50] + [--ZodiacRatioOfConsideredCandidatesPerIonization=0.2] + [--ZodiacRunInTwoSteps=true] [COMMAND] + Override all possible default configurations of this toolbox +from the command line. +``` + +They are detailed below (also available with `sirius config --help`) + +```bash + --AdductSettings.detectable=[M+H]+,[M+K]+,[M+Na]+,[M+H-H2O]+,[M+H-H4O2]+, + [M+NH4]+,[M-H]-,[M+Cl]-,[M-H2O-H]-,[M+Br]- + Detectable ion modes which are only considered if + there is an indication in the MS1 scan (e.g. + correct mass delta). + --AdductSettings.enforced=, + Describes how to deal with Adducts: + Pos Examples: + [M+H]+,[M]+,[M+K]+,[M+Na]+,[M+H-H2O]+,[M+Na2-H]+, + [M+2K-H]+,[M+NH4]+,[M+H3O]+,[M+MeOH+H]+,[M+ACN+H]+, + [M+2ACN+H]+,[M+IPA+H]+,[M+ACN+Na]+,[M+DMSO+H]+ + Neg Examples: + [M-H]-,[M]-,[M+K-2H]-,[M+Cl]-,[M-H2O-H]-,[M+Na-2H]-, + M+FA-H]-,[M+Br]-,[M+HAc-H]-,[M+TFA-H]-,[M+ACN-H]- + Enforced ion modes that are always considered. + --AdductSettings.fallback=[M+H]+,[M-H]-,[M+Na]+,[M+K]+ + Fallback ion modes which are considered if the auto + detection did not find any indication for + an ion mode. + --AlgorithmProfile=default + Configuration profile to store instrument specific + algorithm properties. + Some of the default profiles are: 'qtof', + 'orbitrap', 'fticr'. + --CandidateFormulas=, + This configuration holds a set of user given + formulas to be used as candidates for SIRIUS + Note: This set might be merged with other sources + like formulas from databases + Set of Molecular Formulas to be used as candidates + for molecular formula estimation with + SIRIUS + --CompoundQuality=UNKNOWN + Keywords that can be assigned to a input spectrum to + judge its quality. Available keywords + are: Good, LowIntensity, NoMS1Peak, FewPeaks, + Chimeric, NotMonoisotopicPeak, + PoorlyExplained + --ForbidRecalibration=ALLOWED + Enable/Disable the hypothesen driven recalibration + of MS/MS spectra + Must be either 'ALLOWED' or 'FORBIDDEN' + --FormulaResultRankingScore=AUTO + Allows the USER to Specify the ScoreType that is + used to rank the list of Molecular Formula + Identifications + before CSI:FingerID predictions are calculated. Auto + means that this ScoreType is + automatically set depending on the executed workflow. + --FormulaResultThreshold=true + Specifies if the list of Molecular Formula + Identifications is filtered by a soft threshold + (calculateThreshold) before CSI:FingerID predictions + are calculated. + --FormulaSearchDB=none + + --FormulaSettings.detectable=S,Br,Cl,B,Se + Detectable elements are added to the chemical + alphabet, if there are indications for them + (e.g. in isotope pattern) + --FormulaSettings.enforced=C,H,N,O,P + These configurations hold the information how to + autodetect elements based on the given + formula constraints. + Note: If the compound is already assigned to a + specific molecular formula, this annotation is + ignored. + Enforced elements are always considered + --FormulaSettings.fallback=S + Fallback elements are used, if the auto-detection + fails (e.g. no isotope pattern available) + -h, --help Show this help message and exit. + --InjectElGordoCompounds=True + Candidates matching the lipid class estimated by El + Gordo will be tagged. + The lipid class will only be available if El Gordo + predicts that the MS/MS is a lipid spectrum. + If this parameter is set to 'false' El Gordo will + still be executed and e.g. improve the + fragmentation + tree, but the matching candidates will not be tagged + as lipid class. + --IsotopeMs2Settings=IGNORE + + --IsotopeSettings.filter=True + This configurations define how to deal with isotope + patterns in MS1. + When filtering is enabled, molecular formulas are + excluded if their theoretical isotope + pattern does not match + the theoretical one, even if their MS/MS pattern has + high score. + --IsotopeSettings.multiplier=1 + multiplier for the isotope score. Set to 0 to + disable isotope scoring. Otherwise, the score + from isotope + pattern analysis is multiplied with this + coefficient. Set to a value larger than one if + your + isotope + pattern data is of much better quality than your + MS/MS data. + --MedianNoiseIntensity=0.015 + + --MotifDbFile=none + --ms1.absoluteIntensityError=0.02 + The average absolute deviation between theoretical + and measured intensity of isotope + peaks. + Do not change this parameter without a good reason! + --ms1.minimalIntensityToConsider=0.01 + Ignore isotope peaks below this intensity. + This value should reflect the smallest relative + intensive which is still above noise level. + Obviously, this is hard to judge without having + absolute values. Keeping this value around 1 + percent is + fine for most settings. Set it to smaller values if + you trust your small intensities. + --ms1.relativeIntensityError=0.08 + The average relative deviation between theoretical + and measured intensity of isotope + peaks. + Do not change this parameter without a good reason! + --MS1MassDeviation.allowedMassDeviation=10.0 ppm + Mass accuracy setting for MS1 spectra. Mass + accuracies are always written as "X ppm (Y Da)" + with X and Y + are numerical values. The ppm is a relative measure + (parts per million), Da is an absolute + measure. For each mass, the + maximum of relative and absolute is used. + --MS1MassDeviation.massDifferenceDeviation=5.0 ppm + + --MS1MassDeviation.standardMassDeviation=10.0 ppm + + --MS2MassDeviation.allowedMassDeviation=10.0 ppm + Mass accuracy setting for MS2 spectra. Mass + Accuracies are always written as "X ppm (Y Da)" + with X and Y + are numerical values. The ppm is a relative measure + (parts per million), Da is an absolute + measure. For each mass, the + maximum of relative and absolute is used. + --MS2MassDeviation.standardMassDeviation=10.0 ppm + + --NoiseThresholdSettings.absoluteThreshold=0 + + --NoiseThresholdSettings.basePeak=NOT_PRECURSOR + + --NoiseThresholdSettings.intensityThreshold=0.005 + + --NoiseThresholdSettings.maximalNumberOfPeaks=60 + + --NumberOfCandidates=10 + + --NumberOfCandidatesPerIon=1 + use this parameter if you want to force to report at + least + numberOfResultsToKeepPerIonization results per + ionization. + if le 0, this parameter will have no effect and just + the top + numberOfResultsToKeep results will be reported. + --NumberOfStructureCandidates=10000 + + --PossibleAdductSwitches=[M+Na]+:[M+H]+,[M+K]+:[M+H]+,[M+Cl]-:[M-H]- + An adduct switch is a switch of the ionization mode + within a spectrum, e.g. an ion replaces an + sodium adduct + with a protonation during fragmentation. Such adduct + switches heavily increase the + complexity of the + analysis, but for certain adducts they might happen + regularly. Adduct switches are written + in the + form {@literal a -> b, a -> c, d -> c} where a, b, + c, and d are adducts and {@literal a -> b} + denotes an + allowed switch from + a to b within the MS/MS spectrum. + --PrintCitations=True + + --RecomputeResults=False + + --StructurePredictors=CSI_FINGERID + + --StructureSearchDB=BIO + + --Timeout.secondsPerInstance=0 + This configurations define a timeout for the tree + computation. As the underlying problem is + NP-hard, it might take + forever to compute trees for very challenging (e.g. + large mass) compounds. Setting an time + constraint allow the program + to continue with other instances and just skip the + challenging ones. + Note that, due to multithreading, this time + constraints are not absolutely accurate. + Set the maximum number of seconds for computing a + single compound. Set to 0 to disable the time + constraint. + --Timeout.secondsPerTree=0 + Set the maximum number of seconds for a single + molecular formula check. Set to 0 to disable the + time constraint + --UseHeuristic.mzToUseHeuristic=300 + Set minimum m/z to enable heuristic preprocessing. + The heuristic will be used to initially + rank the formula candidates. The Top + (NumberOfCandidates) candidates will then be + computed exactly by solving the ILP. + --UseHeuristic.mzToUseHeuristicOnly=650 + Set minimum m/z to only use heuristic tree + computation. No exact tree computation (ILP) will + be performed for this compounds. + -V, --version Print version information and exit. + --ZodiacClusterCompounds=false + cluster compounds before running ZODIAC + --ZodiacEdgeFilterThresholds.minLocalCandidates=1 + Minimum number of candidates per compound which are + forced to have at least + [minLocalConnections] connections to other compounds. + E.g. 2 candidates per compound must have at least 10 + connections to other compounds + --ZodiacEdgeFilterThresholds.minLocalConnections=10 + Minimum number of connections per candidate which + are forced for at least + [minLocalCandidates] candidates to other compounds. + E.g. 2 candidates per compound must have at least 10 + connections to other compounds + --ZodiacEdgeFilterThresholds.thresholdFilter=0.95 + Defines the proportion of edges of the complete + network which will be ignored. + --ZodiacEpochs.burnInPeriod=2000 + Number of epochs considered as 'burn-in period'. + Samples from the beginning of a Markov chain do not + accurately represent the desired + distribution of candidates and are not used to + estimate the ZODIAC score. + --ZodiacEpochs.iterations=20000 + Number of epochs to run the Gibbs sampling. When + multiple Markov chains are computed, all + chains iterations sum up to this value. + --ZodiacEpochs.numberOfMarkovChains=10 + Number of separate Gibbs sampling runs. + --ZodiacLibraryScoring.lambda=1000 + Lambda used in the scoring function of spectral + library hits. The higher this value the higher + are librar hits weighted in ZODIAC scoring. + --ZodiacLibraryScoring.minCosine=0.5 + Spectral library hits must have at least this cosine + or higher to be considered in scoring. + Value must be in [0,1]. + --ZodiacNumberOfConsideredCandidatesAt300Mz=10 + Maximum number of candidate molecular formulas + (fragmentation trees computed by SIRIUS) + per compound which are considered by ZODIAC. + This is the threshold used for all compounds with mz + below 300 m/z and is used to interpolate the + number of candidates for larger compounds. + If lower than 0, all available candidates are + considered. + --ZodiacNumberOfConsideredCandidatesAt800Mz=50 + Maximum number of candidate molecular formulas + (fragmentation trees computed by SIRIUS) + per compound which are considered by ZODIAC. + This is the threshold used for all compounds with mz + above 800 m/z and is used to interpolate the + number of candidates for smaller compounds. + If lower than 0, all available candidates are + considered. + --ZodiacRatioOfConsideredCandidatesPerIonization=0.2 + Ratio of candidate molecular formulas (fragmentation + trees computed by SIRIUS) per + compound which are forced for each ionization to be + considered by ZODIAC. + This depends on the number of candidates ZODIAC + considers. E.g. if 50 candidates are + considered and a ratio of 0.2 is set, at least 10 + candidates per ionization will be + considered, which might increase the number of + candidates above 50. + --ZodiacRunInTwoSteps=true + As default ZODIAC runs a 2-step approach. First + running 'good quality compounds' only, and + afterwards including the remaining. +``` \ No newline at end of file diff --git a/bindings/sirius-bindings/Cargo.toml b/bindings/sirius-bindings/Cargo.toml new file mode 100644 index 00000000..90b115ac --- /dev/null +++ b/bindings/sirius-bindings/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "sirius-bindings" +version = "0.1.0" +edition = "2021" +license = "GPL-3.0" +readme = "README.md" +keywords = ["metabolomics", "SIRIUS", "LC-MS/MS"] +authors = ["Earth Metabolome Initiative"] +repository = "https://github.com/earth-metabolome-initiative/emi-monorepo/tree/main/bindings/sirius" +description = "Rust bindings for the SIRIUS metabolomics tool" +exclude = ["CONTRIB.md", ".DS_Store", "tests/.DS_Store", ".cargo_vcs_info.json"] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +dotenvy = "0.15.7" +is_executable = "1.0.1" +arbitrary = { version = "1", optional = true, features = ["derive"] } +num_cpus = "1.16.0" + +# we only require the arbitrary derivable crate when fuzz is enabled +[features] +fuzz = ["dep:arbitrary"] diff --git a/bindings/sirius-bindings/README.md b/bindings/sirius-bindings/README.md new file mode 100644 index 00000000..c7a04a2c --- /dev/null +++ b/bindings/sirius-bindings/README.md @@ -0,0 +1,220 @@ +# Sirius +[![Build status](https://github.com/earth-metabolome-initiative/sirius-rs/actions/workflows/sirius.yml/badge.svg)](https://github.com/earth-metabolome-initiative/sirius-rs/actions) +[![Crates.io](https://img.shields.io/crates/v/sirius-rs.svg)](https://crates.io/crates/sirius-rs) +[![Documentation](https://docs.rs/sirius-rs/badge.svg)](https://docs.rs/sirius-rs) + +SIRIUS is a java-based software framework for the analysis of LC-MS/MS data of metabolites and other "small molecules of biological interest". SIRIUS integrates a collection of tools, including CSI:FingerID (with [COSMIC](https://bio.informatik.uni-jena.de/software/cosmic/), [ZODIAC](https://bio.informatik.uni-jena.de/software/zodiac/) and [CANOPUS](https://bio.informatik.uni-jena.de/software/canopus/). In particular, both the graphical user interface and the command line version of SIRIUS seamlessly integrate the CSI:FingerID and CANOPUS web services. + +For further reading we recommend you to refer to the official [Sirius website](https://bio.informatik.uni-jena.de/software/sirius/). + +## Installation +Since version 5.7.0 SIRIUS is officially available via conda ([conda-forge](https://conda-forge.org/)) under the package name [sirius-ms](https://anaconda.org/conda-forge/sirius-ms). Native MacOS arm64 (Apple Silicon) builds are solely available via conda. + +Additionally, you can install Sirius via their [GitHub repository](https://github.com/boecker-lab/sirius). + +# Sirius binding +Here we present a binding for Sirius in [Rust](https://www.rust-lang.org/). This binding is a wrapper around the Sirius command line interface (CLI) and provides a more user-friendly interface for running Sirius. It also provides a safer way to run Sirius by using type safety and error handling before running Sirius executable. + +## Usage +First you need to have Sirius installed in your system. Then you also need the following variables in your `.env` file: +```bash +SIRIUS_PATH=/path/to/sirius_executable (on macOS it should be something like `/Applications/sirius.app/Contents/MacOS/sirius`) +SIRIUS_USERNAME=your_username +SIRIUS_PASSWORD=your_password +``` + +Then you can use the Sirius binding in your Rust project. To do so add this to your `Cargo.toml`: +```toml +[dependencies] +sirius = "0.1" +``` +and this to your crate root: +```rust +use sirius::prelude::*; +``` + +## Examples +Here is an example of running Sirius with the default parameters: +```bash +sirius -i tests/data/input_sirius.mgf --output tests/data/output_sirius_default --maxmz=800.0 formula zodiac fingerprint structure canopus write-summaries +``` + +The equivalent Rust code is: +```rust +use sirius::prelude::*; +use std::path::Path; +let sirius = SiriusBuilder::::default() + .maximal_mz_default().unwrap() + .max_cpus_default().unwrap() + .enable_formula().unwrap() + .enable_zodiac().unwrap() + .enable_fingerprint().unwrap() + .enable_structure().unwrap() + .enable_canopus().unwrap() + .enable_write_summaries().unwrap() + .build(); +let input_file_path = Path::new("tests/data/input_sirius.mgf"); +let output_file_path = Path::new("tests/data/output_sirius_default"); +// Check if the path exists before attempting to remove it +if output_file_path.exists() { + let _ = std::fs::remove_dir_all(output_file_path); +} +sirius.run(input_file_path, output_file_path).unwrap(); +``` + +You can also be more specific and add other parameters. The following example uses the parameters used for the [ENPKG pipeline](https://github.com/enpkg/enpkg_full/blob/c8e649290ee72f000c3385e7669b5da2215abad8/params/user.yml#L60): + +```bash +sirius -i tests/data/input_sirius.mgf --output tests/data/output_sirius --maxmz 800 \ +config --IsotopeSettings.filter=true --FormulaSearchDB=BIO --Timeout.secondsPerTree=0 \ +--FormulaSettings.enforced=H,C,N,O,P --Timeout.secondsPerInstance=0 \ +--AdductSettings.detectable='[[M+H]+,[M-H4O2+H]+,[M+Na]+,[M+K]+,[M+H3N+H]+,[M-H2O+H]+]' \ +--UseHeuristic.mzToUseHeuristicOnly=650 --AlgorithmProfile=orbitrap --IsotopeMs2Settings=IGNORE \ +--MS2MassDeviation.allowedMassDeviation=5.0ppm --NumberOfCandidatesPerIon=1 \ +--UseHeuristic.mzToUseHeuristic=300 --FormulaSettings.detectable=B,Cl,Br,Se,S \ +--NumberOfCandidates=10 --ZodiacNumberOfConsideredCandidatesAt300Mz=10 \ +--ZodiacRunInTwoSteps=true --ZodiacEdgeFilterThresholds.minLocalConnections=10 \ +--ZodiacEdgeFilterThresholds.thresholdFilter=0.95 --ZodiacEpochs.burnInPeriod=2000 \ +--ZodiacEpochs.numberOfMarkovChains=10 --ZodiacNumberOfConsideredCandidatesAt800Mz=50 \ +--ZodiacEpochs.iterations=20000 --AdductSettings.enforced=, \ +--AdductSettings.fallback='[[M+H]+,[M+Na]+,[M+K]+]' --FormulaResultThreshold=true \ +--InjectElGordoCompounds=true --StructureSearchDB=BIO \ +--RecomputeResults=false formula zodiac fingerprint structure canopus write-summaries +``` + +The equivalent Rust code is: +```rust +use sirius::prelude::*; +use std::path::Path; +let sirius = SiriusBuilder::default() + .maximal_mz(800.0).unwrap() + .max_cpus_default().unwrap() + .isotope_settings_filter(true).unwrap() + .formula_search_db(DBVector::from(vec![SearchDB::Bio])).unwrap() + .timeout_seconds_per_tree(0).unwrap() + .formula_settings_enforced(AtomVector::from(vec![ + Atoms::H, + Atoms::C, + Atoms::N, + Atoms::O, + Atoms::P, + ])).unwrap() + .timeout_seconds_per_instance(0).unwrap() + .adduct_settings_detectable(AdductsVector::from(vec![ + Adducts::MplusHplus, + Adducts::MplusHminusTwoH2Oplus, + Adducts::MplusNaplus, + Adducts::MplusKplus, + Adducts::MplusH3NplusHplus, + Adducts::MplusHminusH2Oplus, + ])).unwrap() + .use_heuristic_mz_to_use_heuristic_only(650).unwrap() + .algorithm_profile(Instruments::Orbitrap).unwrap() + .isotope_ms2_settings(IsotopeMS2Settings::Ignore).unwrap() + .ms2_mass_deviation_allowed_mass_deviation(MassDeviation::Ppm(5.0)).unwrap() + .number_of_candidates_per_ion(1).unwrap() + .use_heuristic_mz_to_use_heuristic(300).unwrap() + .formula_settings_detectable(AtomVector::from(vec![ + Atoms::B, + Atoms::Cl, + Atoms::Se, + Atoms::S, + ])).unwrap() + .number_of_candidates(10).unwrap() + .zodiac_number_of_considered_candidates_at_300_mz(10).unwrap() + .zodiac_run_in_two_steps(true).unwrap() + .zodiac_edge_filter_thresholds_min_local_connections(10).unwrap() + .zodiac_edge_filter_thresholds_threshold_filter(0.95).unwrap() + .zodiac_epochs_burn_in_period(2000).unwrap() + .zodiac_epochs_number_of_markov_chains(10).unwrap() + .zodiac_number_of_considered_candidates_at_800_mz(50).unwrap() + .zodiac_epochs_iterations(20000).unwrap() + .adduct_settings_enforced_default().unwrap() + .adduct_settings_fallback(AdductsVector::from(vec![ + Adducts::MplusHplus, + Adducts::MplusNaplus, + Adducts::MplusKplus, + ])).unwrap() + .formula_result_threshold(true).unwrap() + .inject_el_gordo_compounds(true).unwrap() + .structure_search_db(DBVector::from(vec![SearchDB::Bio])).unwrap() + .recompute_results(false).unwrap() + .enable_formula().unwrap() + .enable_zodiac().unwrap() + .enable_fingerprint().unwrap() + .enable_structure().unwrap() + .enable_canopus().unwrap() + .enable_write_summaries().unwrap() + .build(); + +let input_file_path = Path::new("tests/data/input_sirius.mgf"); +let output_file_path = Path::new("tests/data/output_sirius"); +// Check if the path exists before attempting to remove it +if output_file_path.exists() { + let _ = std::fs::remove_dir_all(output_file_path); +} +sirius.run(input_file_path, output_file_path).unwrap(); +``` + +## Error cases +This binding also provides error handling before running the Sirius CLI. + +The following example will throw an error because the `maximal_mz` is added twice: +```should_panic +use sirius::prelude::*; +use std::path::Path; +let sirius = SiriusBuilder::::default() + .maximal_mz_default().unwrap() + .maximal_mz(70.6).unwrap() + .enable_formula().unwrap() + .enable_zodiac().unwrap() + .enable_fingerprint().unwrap() + .enable_structure().unwrap() + .enable_canopus().unwrap() + .enable_write_summaries().unwrap() + .build(); +let input_file_path = Path::new("tests/data/input_sirius.mgf"); +let output_file_path = Path::new("tests/data/output_sirius_default"); +// Check if the path exists before attempting to remove it +if output_file_path.exists() { + let _ = std::fs::remove_dir_all(output_file_path); +} +sirius.run(input_file_path, output_file_path).unwrap(); +``` + +Will result in the following error: +```bash +Error: "The core parameter MaximalMz(70.6) cannot be added to the configuration. There is already an existing parameter which is MaximalMz(800.0). You cannot add it twice." +``` + +## Limitations +For now some *config* parameters are not fully implemented and only the default values are used. + +If you are interested in looking at the default values you can either run `sirius config --help`. Here we present is a non-exhaustive list of the parameters where only the default values can be used: +* **PossibleAdductsSwitches** default is `[M+Na]+:[M+H]+,[M+K]+:[M+H]+,[M+Cl]-:[M-H]-` +* **AdductSettingsEnforced** default is `,` +* **FormulaResultRankingScore** default is `AUTO` +* **IsotopeMS2Settings** default is `IGNORE` +* **NoiseThresholdSettingsBasePeak** default is `NOT_PRECURSOR` +* **Adducts** don't have default, but some adducts are probably not included in the enumeration. + +In the future, we will add the possibility to add custom values for these parameters. In case you need to add custom values for these parameters, do not hesitate to open an issue or a pull request. + +## Documentation +You can find the documentation for the Sirius binding by running the following command in the root of the repository: +```bash +cargo doc --open +``` + +## Fuzzing +Fuzzing is a technique for finding security vulnerabilities and bugs in software by providing random input to the code. It can be an effective way of uncovering issues that might not be discovered through other testing methods. In our library, we take fuzzing seriously, and we use the [cargo fuzz](https://github.com/rust-fuzz/cargo-fuzz) tool to ensure our code is robust and secure. cargo fuzz automates the process of generating and running randomized test inputs, and it can help identify obscure bugs that would be difficult to detect through traditional testing methods. We make sure that our fuzz targets are continuously updated and run against the latest versions of the library to ensure that any vulnerabilities or bugs are quickly identified and addressed. + +You can learn more about fuzzing [here](https://github.com/earth-metabolome-initiative/emi-monorepo/tree/sirius-bindings/bindings/sirius/fuzz). + +## Citing Sirius + +```bash +Kai Dührkop, Markus Fleischauer, Marcus Ludwig, Alexander A. Aksenov, Alexey V. Melnik, Marvin Meusel, Pieter C. Dorrestein, Juho Rousu, and Sebastian Böcker, +[SIRIUS 4: Turning tandem mass spectra into metabolite structure information.](https://doi.org/10.1038/s41592-019-0344-8) +*Nature Methods* 16, 299–302, 2019. +``` \ No newline at end of file diff --git a/bindings/sirius-bindings/dot_env_template b/bindings/sirius-bindings/dot_env_template new file mode 100644 index 00000000..d0e96c27 --- /dev/null +++ b/bindings/sirius-bindings/dot_env_template @@ -0,0 +1,3 @@ +SIRIUS_PATH="path/to/your/sirius/app" +SIRIUS_USERNAME="sirius_login" +SIRIUS_PASSWORD="sirius_password" \ No newline at end of file diff --git a/bindings/sirius-bindings/fuzz/.gitignore b/bindings/sirius-bindings/fuzz/.gitignore new file mode 100644 index 00000000..4889b159 --- /dev/null +++ b/bindings/sirius-bindings/fuzz/.gitignore @@ -0,0 +1,4 @@ +target +corpus +artifacts +coverage \ No newline at end of file diff --git a/bindings/sirius-bindings/fuzz/Cargo.toml b/bindings/sirius-bindings/fuzz/Cargo.toml new file mode 100644 index 00000000..d7695030 --- /dev/null +++ b/bindings/sirius-bindings/fuzz/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "sirius-fuzz" +version = "0.0.0" +publish = false +edition = "2021" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +libfuzzer-sys = "0.4" +arbitrary = { version = "1", features = ["derive"] } +rand = { version = "0.8", features = ["small_rng"] } + +[dependencies.sirius] +path = ".." +features = ["fuzz"] + +# Prevent this from interfering with workspaces +[workspace] +members = ["."] + +[profile.release] +debug = 1 + +[[bin]] +name = "random" +path = "fuzz_targets/random.rs" +test = false +doc = false diff --git a/bindings/sirius-bindings/fuzz/README.md b/bindings/sirius-bindings/fuzz/README.md new file mode 100644 index 00000000..7a60b686 --- /dev/null +++ b/bindings/sirius-bindings/fuzz/README.md @@ -0,0 +1,15 @@ +# How to fuzz +The fuzzer allows you to test the bindings with random inputs. It uses the `cargo-fuzz` crate to generate random inputs and test the bindings with them. + +## Install cargo-fuzz +```bash +cargo install cargo-fuzz +``` + +## Run the fuzzer +```bash +cargo fuzz run random +``` + +You can stop the fuzzer at any time by pressing `Ctrl+C`. The fuzzer will print the inputs that caused the crash. + diff --git a/bindings/sirius-bindings/fuzz/fuzz_targets/random.rs b/bindings/sirius-bindings/fuzz/fuzz_targets/random.rs new file mode 100644 index 00000000..f3c569cf --- /dev/null +++ b/bindings/sirius-bindings/fuzz/fuzz_targets/random.rs @@ -0,0 +1,877 @@ +#![no_main] + +use arbitrary::Arbitrary; +use libfuzzer_sys::fuzz_target; +use rand::rngs::SmallRng; +use rand::Rng; +use rand::SeedableRng; +use sirius::prelude::*; + +#[derive(Arbitrary, Debug)] +struct FuzzCase { + random_state: u64, + maximal_mz: f64, + formula_search_db: DBVector, + isotope_settings_filter: bool, + structure_search_db: DBVector, + timeout_seconds_per_tree: u32, + number_of_candidates: u32, + number_of_candidates_per_ion: u32, + number_of_structure_candidates: u32, + recompute_results: bool, + print_citations: bool, + timeout_seconds_per_instance: u32, + formula_result_threshold: bool, + inject_el_gordo_compounds: bool, + median_noise_intensity: f32, + ms1_absolute_intensity_error: f32, + ms1_minimal_intensity_to_consider: f32, + ms1_relative_intensity_error: f32, + noise_threshold_settings_intensity_threshold: f32, + noise_threshold_settings_maximal_number_of_peaks: u32, + zodiac_cluster_compounds: bool, + zodiac_edge_filter_thresholds_min_local_candidates: u32, + zodiac_edge_filter_thresholds_min_local_connections: u32, + zodiac_edge_filter_thresholds_threshold_filter: f32, + zodiac_epochs_burn_in_period: u32, + zodiac_epochs_iterations: u32, + zodiac_epochs_number_of_markov_chains: u32, + zodiac_library_scoring_lambda: u32, + zodiac_library_scoring_min_cosine: f32, + zodiac_number_of_considered_candidates_at_300_mz: i32, + zodiac_number_of_considered_candidates_at_800_mz: i32, + zodiac_ratio_of_considered_candidates_per_ionization: f32, + zodiac_run_in_two_steps: bool, + ms1_mass_deviation_allowed_mass_deviation: MassDeviation, + ms1_mass_deviation_mass_difference_deviation: MassDeviation, + ms1_mass_deviation_standard_mass_deviation: MassDeviation, + ms2_mass_deviation_standard_mass_deviation: MassDeviation, + ms2_mass_deviation_allowed_mass_deviation: MassDeviation, + formula_settings_detectable: AtomVector, + formula_settings_enforced: AtomVector, + formula_settings_fallback: AtomVector, + forbid_recalibration: ForbidRecalibration, + use_heuristic_mz_to_use_heuristic: u32, + use_heuristic_mz_to_use_heuristic_only: u32, + adduct_settings_detectable: AdductsVector, + adduct_settings_fallback: AdductsVector, + algorithm_profile: Instruments, + compound_quality: CompoundQuality, + adduct_settings_enforced: AdductSettingsEnforced, + candidate_formulas: CandidateFormulas, + formula_result_ranking_score: FormulaResultRankingScore, + isotope_ms2_settings: IsotopeMS2Settings, + isotope_settings_multiplier: u32, + noise_threshold_settings_absolute_threshold: u32, + noise_threshold_settings_base_peak: BasePeak, + structure_predictors: StructurePredictors, + possible_adduct_switches: PossibleAdductSwitches, +} + +fuzz_target!(|params: FuzzCase| { + let rng = SmallRng::seed_from_u64(params.random_state); + let builder = SiriusBuilder::default(); + let _ = router(rng, builder, ¶ms); +}); + +const NUMBER_OF_METHODS: usize = 112; + +fn router( + mut random_state: SmallRng, + builder: SiriusBuilder, + params: &FuzzCase, +) -> Result<(), String> { + // we sample a random integer to decide which method to call + let state = random_state.gen_range(0..NUMBER_OF_METHODS + 1); + match state { + 0 => { + router(random_state, builder.maximal_mz_default()?, params)?; + } + 1 => { + router(random_state, builder.maximal_mz(params.maximal_mz)?, params)?; + } + 2 => { + router( + random_state, + builder.formula_search_db(params.formula_search_db.clone())?, + params, + )?; + } + 3 => { + router(random_state, builder.formula_search_db_default()?, params)?; + } + 4 => { + router( + random_state, + builder.isotope_settings_filter(params.isotope_settings_filter)?, + params, + )?; + } + 5 => { + router( + random_state, + builder.isotope_settings_filter_default()?, + params, + )?; + } + 6 => { + router( + random_state, + builder.structure_search_db(params.structure_search_db.clone())?, + params, + )?; + } + 7 => { + router(random_state, builder.structure_search_db_default()?, params)?; + } + 8 => { + router( + random_state, + builder.timeout_seconds_per_tree(params.timeout_seconds_per_tree)?, + params, + )?; + } + 9 => { + router( + random_state, + builder.timeout_seconds_per_tree_default()?, + params, + )?; + } + 10 => { + router( + random_state, + builder.number_of_candidates(params.number_of_candidates)?, + params, + )?; + } + 11 => { + router( + random_state, + builder.number_of_candidates_default()?, + params, + )?; + } + 12 => { + router( + random_state, + builder.number_of_candidates_per_ion(params.number_of_candidates_per_ion)?, + params, + )?; + } + 13 => { + router( + random_state, + builder.number_of_candidates_per_ion_default()?, + params, + )?; + } + 14 => { + router( + random_state, + builder.number_of_structure_candidates(params.number_of_structure_candidates)?, + params, + )?; + } + 15 => { + router( + random_state, + builder.number_of_structure_candidates_default()?, + params, + )?; + } + 16 => { + router( + random_state, + builder.recompute_results(params.recompute_results)?, + params, + )?; + } + 17 => { + router(random_state, builder.recompute_results_default()?, params)?; + } + 18 => { + router( + random_state, + builder.print_citations(params.print_citations)?, + params, + )?; + } + 19 => { + router(random_state, builder.print_citations_default()?, params)?; + } + 20 => { + router( + random_state, + builder.timeout_seconds_per_instance(params.timeout_seconds_per_instance)?, + params, + )?; + } + 21 => { + router( + random_state, + builder.timeout_seconds_per_instance_default()?, + params, + )?; + } + 22 => { + router( + random_state, + builder.formula_result_threshold(params.formula_result_threshold)?, + params, + )?; + } + 23 => { + router( + random_state, + builder.formula_result_threshold_default()?, + params, + )?; + } + 24 => { + router( + random_state, + builder.inject_el_gordo_compounds(params.inject_el_gordo_compounds)?, + params, + )?; + } + 25 => { + router( + random_state, + builder.inject_el_gordo_compounds_default()?, + params, + )?; + } + 26 => { + router( + random_state, + builder.median_noise_intensity(params.median_noise_intensity)?, + params, + )?; + } + 27 => { + router( + random_state, + builder.median_noise_intensity_default()?, + params, + )?; + } + 28 => { + router( + random_state, + builder.ms1_absolute_intensity_error(params.ms1_absolute_intensity_error)?, + params, + )?; + } + 29 => { + router( + random_state, + builder.ms1_absolute_intensity_error_default()?, + params, + )?; + } + 30 => { + router( + random_state, + builder + .ms1_minimal_intensity_to_consider(params.ms1_minimal_intensity_to_consider)?, + params, + )?; + } + 31 => { + router( + random_state, + builder.ms1_minimal_intensity_to_consider_default()?, + params, + )?; + } + 32 => { + router( + random_state, + builder.ms1_relative_intensity_error(params.ms1_relative_intensity_error)?, + params, + )?; + } + 33 => { + router( + random_state, + builder.ms1_relative_intensity_error_default()?, + params, + )?; + } + 34 => { + router( + random_state, + builder.noise_threshold_settings_intensity_threshold( + params.noise_threshold_settings_intensity_threshold, + )?, + params, + )?; + } + 35 => { + router( + random_state, + builder.noise_threshold_settings_intensity_threshold_default()?, + params, + )?; + } + 36 => { + router( + random_state, + builder.noise_threshold_settings_maximal_number_of_peaks( + params.noise_threshold_settings_maximal_number_of_peaks, + )?, + params, + )?; + } + 37 => { + router( + random_state, + builder.noise_threshold_settings_maximal_number_of_peaks_default()?, + params, + )?; + } + 38 => { + router( + random_state, + builder.zodiac_cluster_compounds(params.zodiac_cluster_compounds)?, + params, + )?; + } + 39 => { + router( + random_state, + builder.zodiac_cluster_compounds_default()?, + params, + )?; + } + 40 => { + router( + random_state, + builder.zodiac_edge_filter_thresholds_min_local_candidates( + params.zodiac_edge_filter_thresholds_min_local_candidates, + )?, + params, + )?; + } + 41 => { + router( + random_state, + builder.zodiac_edge_filter_thresholds_min_local_candidates_default()?, + params, + )?; + } + 42 => { + router( + random_state, + builder.zodiac_edge_filter_thresholds_min_local_connections( + params.zodiac_edge_filter_thresholds_min_local_connections, + )?, + params, + )?; + } + 43 => { + router( + random_state, + builder.zodiac_edge_filter_thresholds_min_local_connections_default()?, + params, + )?; + } + 44 => { + router( + random_state, + builder.zodiac_edge_filter_thresholds_threshold_filter( + params.zodiac_edge_filter_thresholds_threshold_filter, + )?, + params, + )?; + } + 45 => { + router( + random_state, + builder.zodiac_edge_filter_thresholds_threshold_filter_default()?, + params, + )?; + } + 46 => { + router( + random_state, + builder.zodiac_epochs_burn_in_period(params.zodiac_epochs_burn_in_period)?, + params, + )?; + } + 47 => { + router( + random_state, + builder.zodiac_epochs_burn_in_period_default()?, + params, + )?; + } + 48 => { + router( + random_state, + builder.zodiac_epochs_iterations(params.zodiac_epochs_iterations)?, + params, + )?; + } + 49 => { + router( + random_state, + builder.zodiac_epochs_iterations_default()?, + params, + )?; + } + 50 => { + router( + random_state, + builder.zodiac_epochs_number_of_markov_chains( + params.zodiac_epochs_number_of_markov_chains, + )?, + params, + )?; + } + 51 => { + router( + random_state, + builder.zodiac_epochs_number_of_markov_chains_default()?, + params, + )?; + } + 52 => { + router( + random_state, + builder.zodiac_library_scoring_lambda(params.zodiac_library_scoring_lambda)?, + params, + )?; + } + 53 => { + router( + random_state, + builder.zodiac_library_scoring_lambda_default()?, + params, + )?; + } + 54 => { + router( + random_state, + builder + .zodiac_library_scoring_min_cosine(params.zodiac_library_scoring_min_cosine)?, + params, + )?; + } + 55 => { + router( + random_state, + builder.zodiac_library_scoring_min_cosine_default()?, + params, + )?; + } + 56 => { + router( + random_state, + builder.zodiac_number_of_considered_candidates_at_300_mz( + params.zodiac_number_of_considered_candidates_at_300_mz, + )?, + params, + )?; + } + 57 => { + router( + random_state, + builder.zodiac_number_of_considered_candidates_at_300_mz_default()?, + params, + )?; + } + 58 => { + router( + random_state, + builder.zodiac_number_of_considered_candidates_at_800_mz( + params.zodiac_number_of_considered_candidates_at_800_mz, + )?, + params, + )?; + } + 59 => { + router( + random_state, + builder.zodiac_number_of_considered_candidates_at_800_mz_default()?, + params, + )?; + } + 60 => { + router( + random_state, + builder.zodiac_ratio_of_considered_candidates_per_ionization( + params.zodiac_ratio_of_considered_candidates_per_ionization, + )?, + params, + )?; + } + 61 => { + router( + random_state, + builder.zodiac_ratio_of_considered_candidates_per_ionization_default()?, + params, + )?; + } + 62 => { + router( + random_state, + builder.zodiac_run_in_two_steps(params.zodiac_run_in_two_steps)?, + params, + )?; + } + 63 => { + router( + random_state, + builder.zodiac_run_in_two_steps_default()?, + params, + )?; + } + 64 => { + router( + random_state, + builder.ms1_mass_deviation_allowed_mass_deviation( + params.ms1_mass_deviation_allowed_mass_deviation, + )?, + params, + )?; + } + 65 => { + router( + random_state, + builder.ms1_mass_deviation_allowed_mass_deviation_default()?, + params, + )?; + } + 66 => { + router( + random_state, + builder.ms1_mass_deviation_mass_difference_deviation( + params.ms1_mass_deviation_mass_difference_deviation, + )?, + params, + )?; + } + 67 => { + router( + random_state, + builder.ms1_mass_deviation_mass_difference_deviation_default()?, + params, + )?; + } + 68 => { + router( + random_state, + builder.ms1_mass_deviation_standard_mass_deviation( + params.ms1_mass_deviation_standard_mass_deviation, + )?, + params, + )?; + } + 69 => { + router( + random_state, + builder.ms1_mass_deviation_standard_mass_deviation_default()?, + params, + )?; + } + 70 => { + router( + random_state, + builder.ms2_mass_deviation_standard_mass_deviation( + params.ms2_mass_deviation_standard_mass_deviation, + )?, + params, + )?; + } + 71 => { + router( + random_state, + builder.ms2_mass_deviation_standard_mass_deviation_default()?, + params, + )?; + } + 72 => { + router( + random_state, + builder.ms2_mass_deviation_allowed_mass_deviation( + params.ms2_mass_deviation_allowed_mass_deviation, + )?, + params, + )?; + } + 73 => { + router( + random_state, + builder.ms2_mass_deviation_allowed_mass_deviation_default()?, + params, + )?; + } + 74 => { + router( + random_state, + builder.formula_settings_detectable(params.formula_settings_detectable.clone())?, + params, + )?; + } + 75 => { + router( + random_state, + builder.formula_settings_detectable_default()?, + params, + )?; + } + 76 => { + router( + random_state, + builder.formula_settings_enforced(params.formula_settings_enforced.clone())?, + params, + )?; + } + 77 => { + router( + random_state, + builder.formula_settings_enforced_default()?, + params, + )?; + } + 78 => { + router( + random_state, + builder.formula_settings_fallback(params.formula_settings_fallback.clone())?, + params, + )?; + } + 79 => { + router( + random_state, + builder.formula_settings_fallback_default()?, + params, + )?; + } + 80 => { + router( + random_state, + builder.forbid_recalibration(params.forbid_recalibration)?, + params, + )?; + } + 81 => { + router( + random_state, + builder.forbid_recalibration_default()?, + params, + )?; + } + 82 => { + router( + random_state, + builder + .use_heuristic_mz_to_use_heuristic(params.use_heuristic_mz_to_use_heuristic)?, + params, + )?; + } + 83 => { + router( + random_state, + builder.use_heuristic_mz_to_use_heuristic_default()?, + params, + )?; + } + 84 => { + router( + random_state, + builder.use_heuristic_mz_to_use_heuristic_only( + params.use_heuristic_mz_to_use_heuristic_only, + )?, + params, + )?; + } + 85 => { + router( + random_state, + builder.use_heuristic_mz_to_use_heuristic_only_default()?, + params, + )?; + } + 86 => { + router( + random_state, + builder.adduct_settings_detectable(params.adduct_settings_detectable.clone())?, + params, + )?; + } + 87 => { + router( + random_state, + builder.adduct_settings_detectable_default()?, + params, + )?; + } + 88 => { + router( + random_state, + builder.adduct_settings_fallback(params.adduct_settings_fallback.clone())?, + params, + )?; + } + 89 => { + router( + random_state, + builder.adduct_settings_fallback_default()?, + params, + )?; + } + 90 => { + router( + random_state, + builder.algorithm_profile(params.algorithm_profile)?, + params, + )?; + } + 91 => { + router(random_state, builder.algorithm_profile_default()?, params)?; + } + 92 => { + router( + random_state, + builder.compound_quality(params.compound_quality)?, + params, + )?; + } + 93 => { + router(random_state, builder.compound_quality_default()?, params)?; + } + 94 => { + router( + random_state, + builder.adduct_settings_enforced(params.adduct_settings_enforced)?, + params, + )?; + } + 95 => { + router( + random_state, + builder.adduct_settings_enforced_default()?, + params, + )?; + } + 96 => { + router( + random_state, + builder.candidate_formulas(params.candidate_formulas)?, + params, + )?; + } + 97 => { + router(random_state, builder.candidate_formulas_default()?, params)?; + } + 98 => { + router( + random_state, + builder.formula_result_ranking_score(params.formula_result_ranking_score)?, + params, + )?; + } + 99 => { + router( + random_state, + builder.formula_result_ranking_score_default()?, + params, + )?; + } + 100 => { + router( + random_state, + builder.isotope_ms2_settings(params.isotope_ms2_settings)?, + params, + )?; + } + 101 => { + router( + random_state, + builder.isotope_ms2_settings_default()?, + params, + )?; + } + 102 => { + router( + random_state, + builder.isotope_settings_multiplier(params.isotope_settings_multiplier)?, + params, + )?; + } + 103 => { + router( + random_state, + builder.isotope_settings_multiplier_default()?, + params, + )?; + } + 104 => { + router( + random_state, + builder.noise_threshold_settings_absolute_threshold( + params.noise_threshold_settings_absolute_threshold, + )?, + params, + )?; + } + 105 => { + router( + random_state, + builder.noise_threshold_settings_absolute_threshold_default()?, + params, + )?; + } + 106 => { + router( + random_state, + builder.noise_threshold_settings_base_peak( + params.noise_threshold_settings_base_peak, + )?, + params, + )?; + } + 107 => { + router( + random_state, + builder.noise_threshold_settings_base_peak_default()?, + params, + )?; + } + 108 => { + router( + random_state, + builder.structure_predictors(params.structure_predictors)?, + params, + )?; + } + 109 => { + router( + random_state, + builder.structure_predictors_default()?, + params, + )?; + } + 110 => { + router( + random_state, + builder.possible_adduct_switches(params.possible_adduct_switches)?, + params, + )?; + } + 111 => { + router( + random_state, + builder.possible_adduct_switches_default()?, + params, + )?; + } + _ => { + builder.build(); + } + } + Ok(()) +} diff --git a/bindings/sirius-bindings/src/builder.rs b/bindings/sirius-bindings/src/builder.rs new file mode 100644 index 00000000..e4ef7d0e --- /dev/null +++ b/bindings/sirius-bindings/src/builder.rs @@ -0,0 +1,1575 @@ +//! A builder is a type of struct that will collect configurations and once build, prodiuces a complete struct. +//! +use crate::prelude::*; +use crate::sirius_config::SiriusConfig; +use crate::traits::IntoDefault; + +/// The SiriusBuilder is used to set the parameters of the SiriusConfig. +#[derive(Default)] +pub struct SiriusBuilder { + config: SiriusConfig, +} + +/// The functions in this block are used to set the parameters of the SiriusBuilder. +/// Most of the functions come from the `sirius config` command. The comments in the functions are usually a copy-paste from the `sirius config --help` command. +impl SiriusBuilder { + /// Set the maximal value of m/z ratio on which Sirius calculation will be carried. + /// + /// # Arguments + /// + /// * `maximal_mz` - The maximal m/z ratio. + /// + /// # Example + /// + /// ``` + /// use sirius::prelude::*; + /// + /// let sirius = SiriusBuilder::default() + /// .maximal_mz(1000.0).unwrap() + /// .build(); + /// + /// assert!(SiriusBuilder::default().maximal_mz(-67.0).is_err()); + /// assert!(SiriusBuilder::default().maximal_mz(0.0).is_err()); + /// assert!(SiriusBuilder::default().maximal_mz(std::f64::NAN).is_err()); + /// assert!(SiriusBuilder::default().maximal_mz(std::f64::INFINITY).is_err()); + /// ``` + /// + pub fn maximal_mz(mut self, maximal_mz: f64) -> Result { + if maximal_mz < 0.0 { + return Err(format!( + concat!("Maximal m/z ratio must be positive. ", "You provided {}."), + maximal_mz + )); + } + if maximal_mz == 0.0 { + return Err("Maximal m/z ratio cannot be 0".to_string()); + } + if maximal_mz.is_nan() { + return Err("Maximal m/z ratio cannot be NaN".to_string()); + } + if maximal_mz.is_infinite() { + return Err("Maximal m/z ratio cannot be infinite".to_string()); + } + + self.config + .add_core_parameter(CoreV5::MaximalMz(maximal_mz))?; + Ok(self) + } + + /// Set the number of cores to use for the calculation. + /// # Arguments + /// * `n_cores` - The number of cores to use. + /// # Example + /// ``` + /// use sirius::prelude::*; + /// let sirius = SiriusBuilder::default() + /// .max_cpus(4).unwrap() + /// .build(); + /// ``` + pub fn max_cpus(mut self, n_cores: usize) -> Result { + self.config.add_core_parameter(CoreV5::NCpus(n_cores))?; + Ok(self) + } + + /// Activate the use of the isotope settings filter. + /// # Arguments + /// * `isotope_settings_filter` - Whether to enable the isotope settings filter. + /// + /// # Example + /// ``` + /// use sirius::prelude::*; + /// let sirius = SiriusBuilder::default() + /// .isotope_settings_filter(true).unwrap() + /// .build(); + /// ``` + pub fn isotope_settings_filter( + mut self, + isotope_settings_filter: bool, + ) -> Result { + self.config + .add_config_parameter(ConfigV5::IsotopeSettingsFilter(isotope_settings_filter))?; + Ok(self) + } + + /// Set the database to be used for formula search. + /// # Arguments + /// * `formula_search_db` - The database to be used for formula search. + /// # Example + /// ``` + /// use sirius::prelude::*; + /// let sirius = SiriusBuilder::default() + /// .formula_search_db(DBVector::from(vec![SearchDB::Hmdb])).unwrap() + /// .build(); + /// + /// assert!(SiriusBuilder::default().formula_search_db(DBVector::from(vec![SearchDB::Hmdb])).is_ok()); + /// ``` + pub fn formula_search_db( + mut self, + formula_search_db: crate::sirius_types::DBVector, + ) -> Result { + self.config + .add_config_parameter(ConfigV5::FormulaSearchDB(formula_search_db))?; + Ok(self) + } + + /// Set the database to be used for the structure search. + /// # Arguments + /// * `structure_search_db` - The database to be used for the structure search. + /// # Example + /// ``` + /// use sirius::prelude::*; + /// let sirius = SiriusBuilder::default() + /// .structure_search_db(DBVector::from(vec![SearchDB::Zincbio])).unwrap() + /// .build(); + /// ``` + pub fn structure_search_db( + mut self, + structure_search_db: crate::sirius_types::DBVector, + ) -> Result { + self.config + .add_config_parameter(ConfigV5::StructureSearchDB(structure_search_db))?; + Ok(self) + } + + /// Set the timeout seconds for each tree. + /// # Arguments + /// * `timeout_seconds_per_tree` - The timeout seconds for each tree. + /// # Example + /// ``` + /// use sirius::prelude::*; + /// let sirius = SiriusBuilder::default() + /// .timeout_seconds_per_tree(100).unwrap() + /// .build(); + /// ``` + pub fn timeout_seconds_per_tree( + mut self, + timeout_seconds_per_tree: u32, + ) -> Result { + self.config + .add_config_parameter(ConfigV5::TimeoutSecondsPerTree(timeout_seconds_per_tree))?; + Ok(self) + } + + /// Set the number of candidates. + /// # Arguments + /// * `number_of_candidates` - The number of candidates. + pub fn number_of_candidates(mut self, number_of_candidates: u32) -> Result { + self.config + .add_config_parameter(ConfigV5::NumberOfCandidates(number_of_candidates))?; + Ok(self) + } + + /// Set the number of candidates per ion. + /// # Arguments + /// * `number_of_candidates_per_ion` - The number of candidates per ion. + pub fn number_of_candidates_per_ion( + mut self, + number_of_candidates_per_ion: u32, + ) -> Result { + self.config + .add_config_parameter(ConfigV5::NumberOfCandidatesPerIon( + number_of_candidates_per_ion, + ))?; + Ok(self) + } + + /// Sets the number of structure candidates. + /// # Arguments + /// * `number_of_structure_candidates` - The number of structure candidates. + pub fn number_of_structure_candidates( + mut self, + number_of_structure_candidates: u32, + ) -> Result { + self.config + .add_config_parameter(ConfigV5::NumberOfStructureCandidates( + number_of_structure_candidates, + ))?; + Ok(self) + } + + /// Specify if the results should be recomputed. + /// # Arguments + /// * `recompute_results` - Whether to recompute the results. + pub fn recompute_results(mut self, recompute_results: bool) -> Result { + self.config + .add_config_parameter(ConfigV5::RecomputeResults(recompute_results))?; + Ok(self) + } + + /// Whether to print citations we Sirius has finished running. + /// # Arguments + /// * `print_citations` - Whether to print citations. + pub fn print_citations(mut self, print_citations: bool) -> Result { + self.config + .add_config_parameter(ConfigV5::PrintCitations(print_citations))?; + Ok(self) + } + + /// This configurations define a timeout for the tree computation. + /// As the underlying problem is NP-hard, it might take forever to compute trees for very challenging (e.g. large mass) compounds. + /// Setting a time constraint allow the program to continue with other instances and just skip the challenging ones. + /// Note that, due to multithreading, this time constraints are not absolutely accurate. + /// Set the maximum number of seconds for computing a single compound. Set to 0 to disable the time constraint. + /// # Arguments + /// * `timeout_seconds_per_instance` - The maximum number of seconds for computing a single compound. + pub fn timeout_seconds_per_instance( + mut self, + timeout_seconds_per_instance: u32, + ) -> Result { + self.config + .add_config_parameter(ConfigV5::TimeoutSecondsPerInstance( + timeout_seconds_per_instance, + ))?; + Ok(self) + } + + /// Specifies if the list of Molecular Formula Identifications is filtered by a soft threshold (calculateThreshold) + /// before CSI:FingerID predictions are calculated. + /// # Arguments + /// * `formula_settings_filter` - Whether to filter the list of Molecular Formula Identifications. + pub fn formula_result_threshold( + mut self, + formula_result_threshold: bool, + ) -> Result { + self.config + .add_config_parameter(ConfigV5::FormulaResultThreshold(formula_result_threshold))?; + Ok(self) + } + + /// Candidates matching the lipid class estimated by El Gordo will be tagged. + /// The lipid class will only be available if El Gordo predicts that the MS/MS is a lipid spectrum. + /// If this parameter is set to 'false' El Gordo will still be executed and e.g. improve the fragmentation tree, + /// but the matching candidates will not be tagged as lipid class. + /// # Arguments + /// * `inject_el_gordo_compounds` - Whether to inject El Gordo compounds. + pub fn inject_el_gordo_compounds( + mut self, + inject_el_gordo_compounds: bool, + ) -> Result { + self.config + .add_config_parameter(ConfigV5::InjectElGordoCompounds(inject_el_gordo_compounds))?; + Ok(self) + } + + /// Sets the median noise intensity. + /// # Arguments + /// * `median_noise_intensity` - The median noise intensity. + pub fn median_noise_intensity(mut self, median_noise_intensity: f32) -> Result { + if median_noise_intensity < 0.0 { + return Err(format!( + concat!( + "Median noise intensity must be positive. ", + "You provided {}." + ), + median_noise_intensity + )); + } + self.config + .add_config_parameter(ConfigV5::MedianNoiseIntensity(median_noise_intensity))?; + Ok(self) + } + + /// The average absolute deviation between theoretical and measured intensity of isotope peaks. + /// + /// Do not change this parameter without a good reason! + /// + /// Ideally use the `ms1_absolute_intensity_error_default()` function. + /// # Arguments + /// * `ms1_absolute_intensity_error` - The average absolute deviation between theoretical and measured intensity of isotope peaks. + pub fn ms1_absolute_intensity_error( + mut self, + ms1_absolute_intensity_error: f32, + ) -> Result { + if ms1_absolute_intensity_error < 0.0 { + return Err(format!( + concat!( + "MS1 absolute intensity error must be positive. ", + "You provided {}." + ), + ms1_absolute_intensity_error + )); + } + self.config + .add_config_parameter(ConfigV5::MS1AbsoluteIntensityError( + ms1_absolute_intensity_error, + ))?; + Ok(self) + } + + /// Ignore isotope peaks below this intensity. + /// This value should reflect the smallest relative intensive which is still above noise level. + /// Obviously, this is hard to judge without having absolute values. + /// Keeping this value around 1 percent is fine for most settings. Set it to smaller values if you trust your small intensities. + /// # Arguments + /// * `ms1_minimal_intensity_to_consider` - The minimal intensity to consider. + pub fn ms1_minimal_intensity_to_consider( + mut self, + ms1_minimal_intensity_to_consider: f32, + ) -> Result { + if ms1_minimal_intensity_to_consider < 0.0 { + return Err(format!( + concat!( + "MS1 minimal intensity to consider must be positive. ", + "You provided {}." + ), + ms1_minimal_intensity_to_consider + )); + } + self.config + .add_config_parameter(ConfigV5::MS1MinimalIntensityToConsider( + ms1_minimal_intensity_to_consider, + ))?; + Ok(self) + } + + /// The average relative deviation between theoretical and measured intensity of isotope peaks. + /// + /// Do not change this parameter without a good reason! + /// + /// Ideally use the `ms1_relative_intensity_error_default()` function. + /// # Arguments + /// * `ms1_relative_intensity_error` - The average relative deviation between theoretical and measured intensity of isotope peaks. + pub fn ms1_relative_intensity_error( + mut self, + ms1_relative_intensity_error: f32, + ) -> Result { + if ms1_relative_intensity_error < 0.0 { + return Err(format!( + concat!( + "MS1 relative intensity error must be positive. ", + "You provided {}." + ), + ms1_relative_intensity_error + )); + } + self.config + .add_config_parameter(ConfigV5::MS1RelativeIntensityError( + ms1_relative_intensity_error, + ))?; + Ok(self) + } + + /// Sets the noise threshold settings absolute threshold. + /// # Arguments + /// * `noise_threshold_settings_absolute_threshold` - The noise threshold settings absolute threshold. + pub fn noise_threshold_settings_intensity_threshold( + mut self, + noise_threshold_settings_intensity_threshold: f32, + ) -> Result { + if noise_threshold_settings_intensity_threshold < 0.0 { + return Err(format!( + concat!( + "Noise threshold settings intensity threshold must be positive. ", + "You provided {}." + ), + noise_threshold_settings_intensity_threshold + )); + } + self.config + .add_config_parameter(ConfigV5::NoiseThresholdSettingsIntensityThreshold( + noise_threshold_settings_intensity_threshold, + ))?; + Ok(self) + } + + /// Sets the noise threshold settings maximal number of peaks. + /// # Arguments + /// * `noise_threshold_settings_maximal_number_of_peaks` - The noise threshold settings maximal number of peaks. + pub fn noise_threshold_settings_maximal_number_of_peaks( + mut self, + noise_threshold_settings_maximal_number_of_peaks: u32, + ) -> Result { + self.config + .add_config_parameter(ConfigV5::NoiseThresholdSettingsMaximalNumberOfPeaks( + noise_threshold_settings_maximal_number_of_peaks, + ))?; + Ok(self) + } + + /// Sets if you want to cluster compounds before running ZODIAC. + /// # Arguments + /// * `zodiac_cluster_compounds` - Whether to cluster compounds before running ZODIAC. + pub fn zodiac_cluster_compounds( + mut self, + zodiac_cluster_compounds: bool, + ) -> Result { + self.config + .add_config_parameter(ConfigV5::ZodiacClusterCompounds(zodiac_cluster_compounds))?; + Ok(self) + } + + /// Minimum number of candidates per compound which are forced to have at least \[minLocalConnections\] connections to other compounds. + /// E.g. 2 candidates per compound must have at least 10 connections to other compounds. + /// # Arguments + /// * `zodiac_edge_filter_thresholds_min_local_candidates` - The minimum number of candidates per compound. + pub fn zodiac_edge_filter_thresholds_min_local_candidates( + mut self, + zodiac_edge_filter_thresholds_min_local_candidates: u32, + ) -> Result { + self.config.add_config_parameter( + ConfigV5::ZodiacEdgeFilterThresholdsMinLocalCandidates( + zodiac_edge_filter_thresholds_min_local_candidates, + ), + )?; + Ok(self) + } + + /// Minimum number of connections per candidate which are forced for at least \[minLocalCandidates\] candidates to other compounds. + /// E.g. 2 candidates per compound must have at least 10 connections to other compounds. + /// # Arguments + /// * `zodiac_edge_filter_thresholds_min_local_connections` - The minimum number of connections per candidate. + pub fn zodiac_edge_filter_thresholds_min_local_connections( + mut self, + zodiac_edge_filter_thresholds_min_local_connections: u32, + ) -> Result { + self.config.add_config_parameter( + ConfigV5::ZodiacEdgeFilterThresholdsMinLocalConnections( + zodiac_edge_filter_thresholds_min_local_connections, + ), + )?; + Ok(self) + } + + /// Defines the proportion of edges of the complete network which will be ignored. + /// # Arguments + /// * `zodiac_edge_filter_thresholds_threshold_filter` - The proportion of edges of the complete network which will be ignored. + pub fn zodiac_edge_filter_thresholds_threshold_filter( + mut self, + zodiac_edge_filter_thresholds_threshold_filter: f32, + ) -> Result { + if zodiac_edge_filter_thresholds_threshold_filter < 0.0 { + return Err(format!( + concat!( + "Zodiac edge filter thresholds threshold filter must be positive. ", + "You provided {}." + ), + zodiac_edge_filter_thresholds_threshold_filter + )); + } + self.config + .add_config_parameter(ConfigV5::ZodiacEdgeFilterThresholdsThresholdFilter( + zodiac_edge_filter_thresholds_threshold_filter, + ))?; + Ok(self) + } + + /// Number of epochs considered as 'burn-in period'. + /// Samples from the beginning of a Markov chain do not accurately represent the desired distribution of candidates and are not used to estimate the ZODIAC score. + /// # Arguments + /// * `zodiac_epochs_burn_in_period` - The number of epochs considered as 'burn-in period'. + pub fn zodiac_epochs_burn_in_period( + mut self, + zodiac_epochs_burn_in_period: u32, + ) -> Result { + self.config + .add_config_parameter(ConfigV5::ZodiacEpochsBurnInPeriod( + zodiac_epochs_burn_in_period, + ))?; + Ok(self) + } + + /// Number of epochs to run the Gibbs sampling. When multiple Markov chains are computed, all chains' iterations sum up to this value. + /// # Arguments + /// * `zodiac_epochs_iterations` - The number of epochs to run the Gibbs sampling. + pub fn zodiac_epochs_iterations( + mut self, + zodiac_epochs_number_of_epochs: u32, + ) -> Result { + self.config + .add_config_parameter(ConfigV5::ZodiacEpochsIterations( + zodiac_epochs_number_of_epochs, + ))?; + Ok(self) + } + + /// Number of separate Gibbs sampling runs. + /// # Arguments + /// * `zodiac_epochs_number_of_markov_chains` - The number of separate Gibbs sampling runs. + pub fn zodiac_epochs_number_of_markov_chains( + mut self, + zodiac_epochs_number_of_markov_chains: u32, + ) -> Result { + self.config + .add_config_parameter(ConfigV5::ZodiacEpochsNumberOfMarkovChains( + zodiac_epochs_number_of_markov_chains, + ))?; + Ok(self) + } + + /// Lambda used in the scoring function of spectral library hits. The higher this value the higher are librar hits weighted in ZODIAC scoring. + /// # Arguments + /// * `zodiac_library_scoring_lambda` - The lambda used in the scoring function of spectral library hits. + pub fn zodiac_library_scoring_lambda( + mut self, + zodiac_library_scoring_lambda: u32, + ) -> Result { + self.config + .add_config_parameter(ConfigV5::ZodiacLibraryScoringLambda( + zodiac_library_scoring_lambda, + ))?; + Ok(self) + } + + /// Set the minimal cosine value. Values must be between 0 and 1. + /// # Arguments + /// * `zodiac_library_scoring_min_cosine` - The minimal cosine value. + /// # Example + /// ``` + /// use sirius::prelude::*; + /// let sirius = SiriusBuilder::default() + /// .zodiac_library_scoring_min_cosine(0.5).unwrap() + /// .build(); + /// ``` + /// # Errors + /// If the value is not in the range 0 and 1. + /// # Example + /// ``` + /// use sirius::prelude::*; + /// assert!(SiriusBuilder::default().zodiac_library_scoring_min_cosine(1.1).is_err()); + /// assert!(SiriusBuilder::default().zodiac_library_scoring_min_cosine(-0.1).is_err()); + /// ``` + pub fn zodiac_library_scoring_min_cosine( + mut self, + zodiac_library_scoring_min_cosine: f32, + ) -> Result { + // Value must be in [0,1]. + if !(0.0..=1.0).contains(&zodiac_library_scoring_min_cosine) { + // fast and easy way to check interval of values in Rust. Then add the "!" to negate the condition. + return Err(format!( + concat!( + "Zodiac library scoring min cosine must be in [0,1]. ", + "You provided {}." + ), + zodiac_library_scoring_min_cosine + )); + } + self.config + .add_config_parameter(ConfigV5::ZodiacLibraryScoringMinCosine( + zodiac_library_scoring_min_cosine, + ))?; + Ok(self) + } + + /// Maximum number of candidate molecular formulas (fragmentation trees computed by SIRIUS) per compound which are considered by ZODIAC. + /// This is the threshold used for all compounds with mz below 300 m/z and is used to interpolate the number of candidates for larger compounds. + /// If lower than 0, all available candidates are considered. + /// # Arguments + /// * `zodiac_number_of_considered_candidates_at_300_mz` - The maximum number of candidate molecular formulas. + pub fn zodiac_number_of_considered_candidates_at_300_mz( + mut self, + zodiac_number_of_considered_candidates_at_300_mz: i32, + ) -> Result { + self.config + .add_config_parameter(ConfigV5::ZodiacNumberOfConsideredCandidatesAt300Mz( + zodiac_number_of_considered_candidates_at_300_mz, + ))?; + Ok(self) + } + + /// Maximum number of candidate molecular formulas (fragmentation trees computed by SIRIUS) per compound which are considered by ZODIAC. + /// This is the threshold used for all compounds with mz below 800 m/z and is used to interpolate the number of candidates for larger compounds. + /// If lower than 0, all available candidates are considered. + /// # Arguments + /// * `zodiac_number_of_considered_candidates_at_800_mz` - The maximum number of candidate molecular formulas. + pub fn zodiac_number_of_considered_candidates_at_800_mz( + mut self, + zodiac_number_of_considered_candidates_at_800_mz: i32, + ) -> Result { + self.config + .add_config_parameter(ConfigV5::ZodiacNumberOfConsideredCandidatesAt800Mz( + zodiac_number_of_considered_candidates_at_800_mz, + ))?; + Ok(self) + } + + /// Ratio of candidate molecular formulas (fragmentation trees computed by SIRIUS) per compound which are forced for each ionization to be + /// considered by ZODIAC. This depends on the number of candidates ZODIAC considers. + /// E.g. if 50 candidates are considered and a ratio of 0.2 is set, + /// at least 10 candidates per ionization will be considered, which might increase the number of candidates above 50. + /// # Arguments + /// * `zodiac_ratio_of_considered_candidates_per_ionization` - The ratio of candidate molecular formulas. + pub fn zodiac_ratio_of_considered_candidates_per_ionization( + mut self, + zodiac_ratio_of_considered_candidates_per_ionization: f32, + ) -> Result { + if !(0.0..=1.0).contains(&zodiac_ratio_of_considered_candidates_per_ionization) { + return Err(format!( + concat!( + "Zodiac ratio of considered candidates per ionization must be in [0,1]. ", + "You provided {}." + ), + zodiac_ratio_of_considered_candidates_per_ionization + )); + } + self.config.add_config_parameter( + ConfigV5::ZodiacRatioOfConsideredCandidatesPerIonization( + zodiac_ratio_of_considered_candidates_per_ionization, + ), + )?; + Ok(self) + } + + /// As default ZODIAC runs a 2-step approach. First running 'good quality compounds' only, and afterwards including the remaining. + /// # Arguments + /// * `zodiac_run_in_two_steps` - Whether to run ZODIAC in two steps. + pub fn zodiac_run_in_two_steps( + mut self, + zodiac_run_in_two_steps: bool, + ) -> Result { + self.config + .add_config_parameter(ConfigV5::ZodiacRunInTwoSteps(zodiac_run_in_two_steps))?; + Ok(self) + } + + /// Mass accuracy setting for MS1 spectra. Mass accuracies are always written as "X ppm (Y Da)" + /// with X and Y are numerical values. The ppm is a relative measure + /// (parts per million), Da is an absolute measure. For each mass, the + /// maximum of relative and absolute is used. + /// # Arguments + /// * `ms1_mass_deviation_allowed_mass_deviation` - The mass accuracy setting for MS1 spectra. + pub fn ms1_mass_deviation_allowed_mass_deviation( + mut self, + ms1_mass_deviation_allowed_mass_deviation: MassDeviation, + ) -> Result { + self.config + .add_config_parameter(ConfigV5::MS1MassDeviationAllowedMassDeviation( + ms1_mass_deviation_allowed_mass_deviation.must_be_positive()?, + ))?; + Ok(self) + } + + /// The difference is mass deviation between two masses. + /// # Arguments + /// * `ms1_mass_deviation_mass_difference_deviation` - The mass deviation between two masses. + pub fn ms1_mass_deviation_mass_difference_deviation( + mut self, + ms1_mass_deviation_mass_difference_deviation: MassDeviation, + ) -> Result { + self.config + .add_config_parameter(ConfigV5::MS1MassDeviationMassDifferenceDeviation( + ms1_mass_deviation_mass_difference_deviation.must_be_positive()?, + ))?; + Ok(self) + } + + /// The standard mass deviation for MS1 spectra. + /// # Arguments + /// * `ms1_mass_deviation_standard_mass_deviation` - The standard mass deviation for MS1 spectra. + pub fn ms1_mass_deviation_standard_mass_deviation( + mut self, + ms1_mass_deviation_standard_mass_deviation: MassDeviation, + ) -> Result { + self.config + .add_config_parameter(ConfigV5::MS1MassDeviationStandardMassDeviation( + ms1_mass_deviation_standard_mass_deviation.must_be_positive()?, + ))?; + Ok(self) + } + + /// The standard mass deviation for MS2 spectra. + /// # Arguments + /// * `ms2_mass_deviation_mass_difference_deviation` - The standard mass deviation for MS2 spectra. + pub fn ms2_mass_deviation_standard_mass_deviation( + mut self, + ms2_mass_deviation_standard_mass_deviation: MassDeviation, + ) -> Result { + self.config + .add_config_parameter(ConfigV5::MS2MassDeviationStandardMassDeviation( + ms2_mass_deviation_standard_mass_deviation.must_be_positive()?, + ))?; + Ok(self) + } + + /// Mass accuracy setting for MS2 spectra. Mass accuracies are always written as "X ppm (Y Da)" + /// with X and Y are numerical values. The ppm is a relative measure + /// (parts per million), Da is an absolute measure. For each mass, the + /// maximum of relative and absolute is used. + /// # Arguments + /// * `ms2_mass_deviation_allowed_mass_deviation` - The mass accuracy setting for MS2 spectra. + pub fn ms2_mass_deviation_allowed_mass_deviation( + mut self, + ms2_mass_deviation_allowed_mass_deviation: MassDeviation, + ) -> Result { + self.config + .add_config_parameter(ConfigV5::MS2MassDeviationAllowedMassDeviation( + ms2_mass_deviation_allowed_mass_deviation.must_be_positive()?, + ))?; + Ok(self) + } + + /// Detectable elements are added to the chemical alphabet, if there are indications for them (e.g. in isotope pattern) + /// # Arguments + /// * `formula_settings_detectable` - The detectable elements. + pub fn formula_settings_detectable( + mut self, + formula_settings_detectable: AtomVector, + ) -> Result { + self.config + .add_config_parameter(ConfigV5::FormulaSettingsDetectable( + formula_settings_detectable, + ))?; + Ok(self) + } + + /// These configurations hold the information how to + /// autodetect elements based on the given formula constraints. + /// Note: If the compound is already assigned to a + /// specific molecular formula, this annotation is + /// ignored. Enforced elements are always considered. + /// # Arguments + /// * `formula_settings_enforced` - The enforced elements. + pub fn formula_settings_enforced( + mut self, + formula_settings_enforced: AtomVector, + ) -> Result { + self.config + .add_config_parameter(ConfigV5::FormulaSettingsEnforced(formula_settings_enforced))?; + Ok(self) + } + + /// Fallback elements are used, if the auto-detection fails (e.g. no isotope pattern available) + /// # Arguments + /// * `formula_settings_fallback` - The fallback elements. + pub fn formula_settings_fallback( + mut self, + formula_settings_fallback: AtomVector, + ) -> Result { + self.config + .add_config_parameter(ConfigV5::FormulaSettingsFallback(formula_settings_fallback))?; + Ok(self) + } + + /// Enable/Disable the hypothesen driven recalibration of MS/MS spectra. + /// Must be either 'ALLOWED' or FORBIDDEN'. + /// # Arguments + /// * `forbid_recalibration` - Whether to forbid recalibration. + pub fn forbid_recalibration( + mut self, + forbid_recalibration: ForbidRecalibration, + ) -> Result { + self.config + .add_config_parameter(ConfigV5::ForbidRecalibration(forbid_recalibration))?; + Ok(self) + } + + /// Set minimum m/z to enable heuristic preprocessing. The heuristic will be used to initially rank the formula candidates. + /// The Top (NumberOfCandidates) candidates will then be computed exactly by solving the ILP. + /// # Arguments + /// * `use_heuristic_mz_to_use_heuristic` - The minimum m/z to enable heuristic preprocessing. + pub fn use_heuristic_mz_to_use_heuristic( + mut self, + use_heuristic_mz_to_use_heuristic: u32, + ) -> Result { + self.config + .add_config_parameter(ConfigV5::UseHeuristicMZToUseHeuristic( + use_heuristic_mz_to_use_heuristic, + ))?; + Ok(self) + } + + /// Set minimum m/z to only use heuristic tree computation. No exact tree computation (ILP) will be performed for this compounds. + /// # Arguments + /// * `use_heuristic_mz_to_use_heuristic_only` - The minimum m/z to only use heuristic tree computation. + pub fn use_heuristic_mz_to_use_heuristic_only( + mut self, + use_heuristic_mz_to_use_heuristic: u32, + ) -> Result { + self.config + .add_config_parameter(ConfigV5::UseHeuristicMZToUseHeuristicOnly( + use_heuristic_mz_to_use_heuristic, + ))?; + Ok(self) + } + + /// Detectable ion modes which are only considered if there is an indication in the MS1 scan (e.g. correct mass delta). + pub fn adduct_settings_detectable( + mut self, + adduct_settings_detectable: AdductsVector, + ) -> Result { + self.config + .add_config_parameter(ConfigV5::AdductSettingsDetectable( + adduct_settings_detectable, + ))?; + Ok(self) + } + + /// Fallback ion modes which are considered if the auto detection did not find any indication for an ion mode. + /// # Arguments + /// * `adduct_settings_fallback` - The fallback ion modes. + pub fn adduct_settings_fallback( + mut self, + adduct_settings_fallback: AdductsVector, + ) -> Result { + self.config + .add_config_parameter(ConfigV5::AdductSettingsFallback(adduct_settings_fallback))?; + Ok(self) + } + + /// Configuration profile to store instrument specific algorithm properties. Some of the default profiles are: 'qtof', 'orbitrap', 'fticr'. + /// # Arguments + /// * `algorithm_profile` - The algorithm profile. + pub fn algorithm_profile(mut self, algorithm_profile: Instruments) -> Result { + self.config + .add_config_parameter(ConfigV5::AlgorithmProfile(algorithm_profile))?; + Ok(self) + } + + /// Keywords that can be assigned to a input spectrum to judge its quality. + /// Available keywords are: Good, LowIntensity, NoMS1Peak, FewPeaks, Chimeric, NotMonoisotopicPeak, PoorlyExplained + /// # Arguments + /// * `compound_quality` - The compound quality. + pub fn compound_quality(mut self, compound_quality: CompoundQuality) -> Result { + self.config + .add_config_parameter(ConfigV5::CompoundQuality(compound_quality))?; + Ok(self) + } + + /// Enforced ion modes that are always considered. + /// # Arguments + /// * `adduct_settings_enforced` - The enforced ion modes. + pub fn adduct_settings_enforced( + mut self, + adduct_settings_enforced: AdductSettingsEnforced, + ) -> Result { + self.config + .add_config_parameter(ConfigV5::AdductSettingsEnforced(adduct_settings_enforced))?; + Ok(self) + } + + /// This configuration holds a set of user given formulas to be used as candidates for SIRIUS + /// Note: This set might be merged with other sources like formulas from databases + /// Set of Molecular Formulas to be used as candidates for molecular formula estimation with SIRIUS + /// # Arguments + /// * `candidate_formulas` - The candidate formulas. + pub fn candidate_formulas( + mut self, + candidate_formulas: CandidateFormulas, + ) -> Result { + self.config + .add_config_parameter(ConfigV5::CandidateFormulas(candidate_formulas))?; + Ok(self) + } + + /// Allows the USER to Specify the ScoreType that is used to rank the list of Molecular Formula Identifications + /// before CSI:FingerID predictions are calculated. Auto means that this ScoreType is automatically set depending on the executed workflow. + /// # Arguments + /// * `formula_result_ranking_score` - The score type that is used to rank the list of Molecular Formula Identifications. + pub fn formula_result_ranking_score( + mut self, + formula_result_ranking_score: FormulaResultRankingScore, + ) -> Result { + self.config + .add_config_parameter(ConfigV5::FormulaResultRankingScore( + formula_result_ranking_score, + ))?; + Ok(self) + } + + /// Wheter to use the isotopes for the MS2 spectra. + pub fn isotope_ms2_settings( + mut self, + isotope_ms2_settings: IsotopeMS2Settings, + ) -> Result { + self.config + .add_config_parameter(ConfigV5::IsotopeMS2Settings(isotope_ms2_settings))?; + Ok(self) + } + + /// multiplier for the isotope score. Set to 0 to disable isotope scoring. Otherwise, the score from isotope pattern analysis is multiplied with this coefficient. + /// Set to a value larger than one if your isotope pattern data is of much better quality than your MS/MS data. + /// # Arguments + /// * `isotope_settings_multiplier` - The multiplier for the isotope score. + pub fn isotope_settings_multiplier( + mut self, + isotope_settings_multiplier: u32, + ) -> Result { + self.config + .add_config_parameter(ConfigV5::IsotopeSettingsMultiplier( + isotope_settings_multiplier, + ))?; + Ok(self) + } + + /// The absolute threshold for the noise + /// # Arguments + /// * `noise_threshold_settings_absolute_threshold` - The absolute threshold for the noise. + pub fn noise_threshold_settings_absolute_threshold( + mut self, + noise_threshold_settings_absolute_threshold: u32, + ) -> Result { + self.config + .add_config_parameter(ConfigV5::NoiseThresholdSettingsAbsoluteThreshold( + noise_threshold_settings_absolute_threshold, + ))?; + Ok(self) + } + + /// The base peak for the noise. + /// # Arguments + /// * `noise_threshold_settings_base_peak` - The base peak for the noise. + pub fn noise_threshold_settings_base_peak( + mut self, + noise_threshold_settings_base_peak: BasePeak, + ) -> Result { + self.config + .add_config_parameter(ConfigV5::NoiseThresholdSettingsBasePeak( + noise_threshold_settings_base_peak, + ))?; + Ok(self) + } + + /// Setups the algorithm for the structure predictors. This should be CSI:FingerID + /// # Arguments + /// * `structure_predictors` - The algorithm for the structure predictors. + pub fn structure_predictors( + mut self, + structure_predictors: StructurePredictors, + ) -> Result { + self.config + .add_config_parameter(ConfigV5::StructurePredictors(structure_predictors))?; + Ok(self) + } + + /// An adduct switch is a switch of the ionization mode within a spectrum, e.g. an ion replaces an + /// sodium adduct with a protonation during fragmentation. Such adduct switches heavily increase the + /// complexity of the analysis, but for certain adducts they might happen + /// regularly. Adduct switches are written in the form {@literal a -> b, a -> c, d -> c} where a, b, + /// c, and d are adducts and {@literal a -> b} denotes an allowed switch from a to b within the MS/MS spectrum. + /// # Arguments + /// * `possible_adduct_switches` - The possible adduct switches. + pub fn possible_adduct_switches( + mut self, + possible_adduct_switches: PossibleAdductSwitches, + ) -> Result { + self.config + .add_config_parameter(ConfigV5::PossibleAdductSwitches(possible_adduct_switches))?; + Ok(self) + } + + /// End `sirius config` command parameters. + + /// + /// + /// + /// + /// Wether to enable the Formula module. + pub fn enable_formula(mut self) -> Result { + self.config.add_formula_parameter(FormulaV5::Enabled)?; + Ok(self) + } + + /// Set whether to display the help of Formula. + pub fn formula_help(mut self) -> Result { + self.config.add_formula_parameter(FormulaV5::Help)?; + Ok(self) + } + + /// Set whether to display the version of Formula. + pub fn formula_version(mut self) -> Result { + self.config.add_formula_parameter(FormulaV5::Version)?; + Ok(self) + } + + /// Wether to enable the Zodiac module. + pub fn enable_zodiac(mut self) -> Result { + self.config.add_zodiac_parameter(ZodiacV5::Enabled)?; + Ok(self) + } + + /// Set whether to display the help of Zodiac. + pub fn zodiac_help(mut self) -> Result { + self.config.add_zodiac_parameter(ZodiacV5::Help)?; + Ok(self) + } + + /// Set whether to display the version of Zodiac. + pub fn zodiac_version(mut self) -> Result { + self.config.add_zodiac_parameter(ZodiacV5::Version)?; + Ok(self) + } + + /// Wether to enable the Fingerprint module. + pub fn enable_fingerprint(mut self) -> Result { + self.config + .add_fingerprint_parameter(FingerprintV5::Enabled)?; + Ok(self) + } + + /// Set whether to display the help of Fingerprint. + pub fn fingerprint_help(mut self) -> Result { + self.config.add_fingerprint_parameter(FingerprintV5::Help)?; + Ok(self) + } + + /// Set whether to display the version of Fingerprint. + pub fn fingerprint_version(mut self) -> Result { + self.config + .add_fingerprint_parameter(FingerprintV5::Version)?; + Ok(self) + } + + /// Wether to enable the Structure module. + pub fn enable_structure(mut self) -> Result { + self.config.add_structure_parameter(StructureV5::Enabled)?; + Ok(self) + } + + /// Set whether to display the help of Structure. + pub fn structure_help(mut self) -> Result { + self.config.add_structure_parameter(StructureV5::Help)?; + Ok(self) + } + + /// Set whether to display the version of Structure. + pub fn structure_version(mut self) -> Result { + self.config.add_structure_parameter(StructureV5::Version)?; + Ok(self) + } + + /// Whether to enable the Canopus module. + pub fn enable_canopus(mut self) -> Result { + self.config.add_canopus_parameter(CanopusV5::Enabled)?; + Ok(self) + } + + /// Set whether to display the help of Canopus. + pub fn canopus_help(mut self) -> Result { + self.config.add_canopus_parameter(CanopusV5::Help)?; + Ok(self) + } + + /// Set whether to display the version of Canopus. + pub fn canopus_version(mut self) -> Result { + self.config.add_canopus_parameter(CanopusV5::Version)?; + Ok(self) + } + + /// Whether to enable the WriteSummaries module. + pub fn enable_write_summaries(mut self) -> Result { + self.config + .add_write_summaries_parameter(WriteSummariesV5::Enabled)?; + Ok(self) + } + + /// Set whether to display the help of WriteSummaries. + pub fn write_summaries_help(mut self) -> Result { + self.config + .add_write_summaries_parameter(WriteSummariesV5::Help)?; + Ok(self) + } + + /// Set whether to display the version of WriteSummaries. + pub fn write_summaries_version(mut self) -> Result { + self.config + .add_write_summaries_parameter(WriteSummariesV5::Version)?; + Ok(self) + } +} + +impl SiriusBuilder { + /// Build the Sirius instance from the configuration. + /// # Example + /// ``` + /// use sirius::prelude::*; + /// let sirius = SiriusBuilder::::default().build(); + /// ``` + pub fn build(self) -> Sirius { + Sirius::from(self.config) + } +} + +impl SiriusBuilder { + /// Set to default maximal value of m/z ratio on which Sirius calculation will be carried. + /// + /// # Example + /// + /// ``` + /// use sirius::prelude::*; + /// + /// let sirius = SiriusBuilder::default() + /// .maximal_mz_default().unwrap() + /// .build(); + /// + /// assert!(SiriusBuilder::default().maximal_mz_default().is_ok()); + /// + /// assert!(SiriusBuilder::default().maximal_mz_default().unwrap().maximal_mz_default().is_err()); + /// + /// + /// ``` + pub fn maximal_mz_default(mut self) -> Result { + self.config + .add_core_parameter(CoreV5::MaximalMz(f64::default()).into_default())?; + Ok(self) + } + + /// Set to default the number of CPUs to use. By default, all available CPUs are used. + pub fn max_cpus_default(mut self) -> Result { + self.config + .add_core_parameter(CoreV5::NCpus(usize::default()).into_default())?; + Ok(self) + } + + /// Set to default isotope settings filter. + pub fn isotope_settings_filter_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::IsotopeSettingsFilter(bool::default()).into_default(), + )?; + Ok(self) + } + + /// Set to default isotope settings intensity threshold. + pub fn formula_search_db_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::FormulaSearchDB(crate::sirius_types::DBVector::default()).into_default(), + )?; + Ok(self) + } + + /// Set to default structure search db. + pub fn structure_search_db_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::StructureSearchDB(crate::sirius_types::DBVector::default()).into_default(), + )?; + Ok(self) + } + + /// Set to default timeout seconds per tree. + pub fn timeout_seconds_per_tree_default(mut self) -> Result { + self.config + .add_config_parameter(ConfigV5::TimeoutSecondsPerTree(u32::default()).into_default())?; + Ok(self) + } + + /// Set to default number of candidates. + pub fn number_of_candidates_default(mut self) -> Result { + self.config + .add_config_parameter(ConfigV5::NumberOfCandidates(u32::default()).into_default())?; + Ok(self) + } + + /// Set to default number of candidates per ion. + pub fn number_of_candidates_per_ion_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::NumberOfCandidatesPerIon(u32::default()).into_default(), + )?; + Ok(self) + } + + /// Set to default number of structure candidates. + pub fn number_of_structure_candidates_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::NumberOfStructureCandidates(u32::default()).into_default(), + )?; + Ok(self) + } + + /// Set to default wheter to recompute results. + pub fn recompute_results_default(mut self) -> Result { + self.config + .add_config_parameter(ConfigV5::RecomputeResults(bool::default()).into_default())?; + Ok(self) + } + + /// Set to default wheter to print citations. + pub fn print_citations_default(mut self) -> Result { + self.config + .add_config_parameter(ConfigV5::PrintCitations(bool::default()).into_default())?; + Ok(self) + } + + /// Set to default timeout seconds per instance. + pub fn timeout_seconds_per_instance_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::TimeoutSecondsPerInstance(u32::default()).into_default(), + )?; + Ok(self) + } + + /// Set to default wheter to use the formula result threshold. + pub fn formula_result_threshold_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::FormulaResultThreshold(bool::default()).into_default(), + )?; + Ok(self) + } + + /// Whether to use the default El Gordo compounds setting. + pub fn inject_el_gordo_compounds_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::InjectElGordoCompounds(bool::default()).into_default(), + )?; + Ok(self) + } + + /// Set to default the median noise intensity. + pub fn median_noise_intensity_default(mut self) -> Result { + self.config + .add_config_parameter(ConfigV5::MedianNoiseIntensity(f32::default()).into_default())?; + Ok(self) + } + + /// Set to default the MS1 absolute intensity error. + pub fn ms1_absolute_intensity_error_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::MS1AbsoluteIntensityError(f32::default()).into_default(), + )?; + Ok(self) + } + + /// Set to default the MS1 minimal intensity to consider. + pub fn ms1_minimal_intensity_to_consider_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::MS1MinimalIntensityToConsider(f32::default()).into_default(), + )?; + Ok(self) + } + + /// Set to default the MS1 relative intensity error. + pub fn ms1_relative_intensity_error_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::MS1RelativeIntensityError(f32::default()).into_default(), + )?; + Ok(self) + } + + /// Set to default the noise threshold settings intensity threshold. + pub fn noise_threshold_settings_intensity_threshold_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::NoiseThresholdSettingsIntensityThreshold(f32::default()).into_default(), + )?; + Ok(self) + } + + /// Set to default the noise threshold settings maximal number of peaks. + pub fn noise_threshold_settings_maximal_number_of_peaks_default( + mut self, + ) -> Result { + self.config.add_config_parameter( + ConfigV5::NoiseThresholdSettingsMaximalNumberOfPeaks(u32::default()).into_default(), + )?; + Ok(self) + } + + /// Wheter to set to default the clustering of compounds before running zodiac. + pub fn zodiac_cluster_compounds_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::ZodiacClusterCompounds(bool::default()).into_default(), + )?; + Ok(self) + } + + /// Set to default the zodiac edge filter thresholds min local candidates. + pub fn zodiac_edge_filter_thresholds_min_local_candidates_default( + mut self, + ) -> Result { + self.config.add_config_parameter( + ConfigV5::ZodiacEdgeFilterThresholdsMinLocalCandidates(u32::default()).into_default(), + )?; + Ok(self) + } + + /// Set to default the zodiac edge filter thresholds min local connections. + pub fn zodiac_edge_filter_thresholds_min_local_connections_default( + mut self, + ) -> Result { + self.config.add_config_parameter( + ConfigV5::ZodiacEdgeFilterThresholdsMinLocalConnections(u32::default()).into_default(), + )?; + Ok(self) + } + + /// Set to default the zodiac edge filter thresholds theshold filter. + pub fn zodiac_edge_filter_thresholds_threshold_filter_default( + mut self, + ) -> Result { + self.config.add_config_parameter( + ConfigV5::ZodiacEdgeFilterThresholdsThresholdFilter(f32::default()).into_default(), + )?; + Ok(self) + } + + /// Set to default the zodiac epochs burn in period. + pub fn zodiac_epochs_burn_in_period_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::ZodiacEpochsBurnInPeriod(u32::default()).into_default(), + )?; + Ok(self) + } + + /// Set to default the zodiac epochs iterations. + pub fn zodiac_epochs_iterations_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::ZodiacEpochsIterations(u32::default()).into_default(), + )?; + Ok(self) + } + + /// Set to default the zodiac epochs number of markov chains. + pub fn zodiac_epochs_number_of_markov_chains_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::ZodiacEpochsNumberOfMarkovChains(u32::default()).into_default(), + )?; + Ok(self) + } + + /// Sdet to default the zodiac library scoring lambda. + pub fn zodiac_library_scoring_lambda_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::ZodiacLibraryScoringLambda(u32::default()).into_default(), + )?; + Ok(self) + } + + /// Set to default the zodiac library scoring min cosine. + pub fn zodiac_library_scoring_min_cosine_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::ZodiacLibraryScoringMinCosine(f32::default()).into_default(), + )?; + Ok(self) + } + + /// Set to default the number of considered candidates at 300 mz. + pub fn zodiac_number_of_considered_candidates_at_300_mz_default( + mut self, + ) -> Result { + self.config.add_config_parameter( + ConfigV5::ZodiacNumberOfConsideredCandidatesAt300Mz(i32::default()).into_default(), + )?; + Ok(self) + } + + /// Set to default the number of considered candidates at 800 mz. + pub fn zodiac_number_of_considered_candidates_at_800_mz_default( + mut self, + ) -> Result { + self.config.add_config_parameter( + ConfigV5::ZodiacNumberOfConsideredCandidatesAt800Mz(i32::default()).into_default(), + )?; + Ok(self) + } + + /// Set to default the ratio of considered candidates per ionization. + pub fn zodiac_ratio_of_considered_candidates_per_ionization_default( + mut self, + ) -> Result { + self.config.add_config_parameter( + ConfigV5::ZodiacRatioOfConsideredCandidatesPerIonization(f32::default()).into_default(), + )?; + Ok(self) + } + + /// Whether to set to default the run in two steps. + pub fn zodiac_run_in_two_steps_default(mut self) -> Result { + self.config + .add_config_parameter(ConfigV5::ZodiacRunInTwoSteps(bool::default()).into_default())?; + Ok(self) + } + + /// Set to default the allowed mass deviation for MS1 spectra. + pub fn ms1_mass_deviation_allowed_mass_deviation_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::MS1MassDeviationAllowedMassDeviation( + MassDeviation::Ppm(f32::default()).must_be_positive()?, + ) + .into_default(), + )?; + Ok(self) + } + + /// Set to default the mass difference deviation for MS1 spectra. + pub fn ms1_mass_deviation_mass_difference_deviation_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::MS1MassDeviationMassDifferenceDeviation( + MassDeviation::Ppm(f32::default()).must_be_positive()?, + ) + .into_default(), + )?; + Ok(self) + } + + /// Set to default the standard mass deviation for MS1 spectra. + pub fn ms1_mass_deviation_standard_mass_deviation_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::MS1MassDeviationStandardMassDeviation( + MassDeviation::Ppm(f32::default()).must_be_positive()?, + ) + .into_default(), + )?; + Ok(self) + } + + /// Set to default the standard mass deviation for MS2 spectra. + pub fn ms2_mass_deviation_standard_mass_deviation_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::MS2MassDeviationStandardMassDeviation( + MassDeviation::Ppm(f32::default()).must_be_positive()?, + ) + .into_default(), + )?; + Ok(self) + } + + /// Set to default the mass accuracy setting for MS2 spectra. + pub fn ms2_mass_deviation_allowed_mass_deviation_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::MS2MassDeviationAllowedMassDeviation( + MassDeviation::Ppm(f32::default()).must_be_positive()?, + ) + .into_default(), + )?; + Ok(self) + } + + /// Set to default the detectable elements. + pub fn formula_settings_detectable_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::FormulaSettingsDetectable(AtomVector::default()).into_default(), + )?; + Ok(self) + } + + /// Set to default the enforced elements. + pub fn formula_settings_enforced_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::FormulaSettingsEnforced(AtomVector::default()).into_default(), + )?; + Ok(self) + } + + /// Set to default the fallback elements. + pub fn formula_settings_fallback_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::FormulaSettingsFallback(AtomVector::default()).into_default(), + )?; + Ok(self) + } + + /// Set to default the forbid recalibration. + pub fn forbid_recalibration_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::ForbidRecalibration(ForbidRecalibration::default()).into_default(), + )?; + Ok(self) + } + + /// Set to default the minimum m/z to enable heuristic preprocessing. + pub fn use_heuristic_mz_to_use_heuristic_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::UseHeuristicMZToUseHeuristic(u32::default()).into_default(), + )?; + Ok(self) + } + + /// Set to default the minimum m/z to only use heuristic tree computation. + pub fn use_heuristic_mz_to_use_heuristic_only_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::UseHeuristicMZToUseHeuristicOnly(u32::default()).into_default(), + )?; + Ok(self) + } + + /// Set to default the detectable adducts. + pub fn adduct_settings_detectable_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::AdductSettingsDetectable(AdductsVector::default()).into_default(), + )?; + Ok(self) + } + + /// Set to default the fallback adducts. + pub fn adduct_settings_fallback_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::AdductSettingsFallback(AdductsVector::default()).into_default(), + )?; + Ok(self) + } + + /// Set to default the algorithm profile. + pub fn algorithm_profile_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::AlgorithmProfile(Instruments::default()).into_default(), + )?; + Ok(self) + } + + /// Set to default the compound quality. + pub fn compound_quality_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::CompoundQuality(CompoundQuality::default()).into_default(), + )?; + Ok(self) + } + + /// Set to default the enforced adducts. + pub fn adduct_settings_enforced_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::AdductSettingsEnforced(AdductSettingsEnforced::default()).into_default(), + )?; + Ok(self) + } + + /// Set to default the candidate formulas. + pub fn candidate_formulas_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::CandidateFormulas(CandidateFormulas::default()).into_default(), + )?; + Ok(self) + } + + /// Set to default the formula result ranking score. + pub fn formula_result_ranking_score_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::FormulaResultRankingScore(FormulaResultRankingScore::default()) + .into_default(), + )?; + Ok(self) + } + + /// Set to default the isotope ms2 settings. + pub fn isotope_ms2_settings_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::IsotopeMS2Settings(IsotopeMS2Settings::default()).into_default(), + )?; + Ok(self) + } + + /// Set to default the isotope settings multiplier. + pub fn isotope_settings_multiplier_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::IsotopeSettingsMultiplier(u32::default()).into_default(), + )?; + Ok(self) + } + + /// Set to default the noise threshold settings absolute threshold. + pub fn noise_threshold_settings_absolute_threshold_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::NoiseThresholdSettingsAbsoluteThreshold(u32::default()).into_default(), + )?; + Ok(self) + } + + /// Set to default the noise threshold settings base peak. + pub fn noise_threshold_settings_base_peak_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::NoiseThresholdSettingsBasePeak(BasePeak::default()).into_default(), + )?; + Ok(self) + } + + /// Set to default the structure predictors algorithm. + pub fn structure_predictors_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::StructurePredictors(StructurePredictors::default()).into_default(), + )?; + Ok(self) + } + + /// Set to default the possible adduct switches. + pub fn possible_adduct_switches_default(mut self) -> Result { + self.config.add_config_parameter( + ConfigV5::PossibleAdductSwitches(PossibleAdductSwitches::default()).into_default(), + )?; + Ok(self) + } +} diff --git a/bindings/sirius-bindings/src/lib.rs b/bindings/sirius-bindings/src/lib.rs new file mode 100644 index 00000000..5ada7691 --- /dev/null +++ b/bindings/sirius-bindings/src/lib.rs @@ -0,0 +1,22 @@ +//! This crate provides bindings for the Sirius executable. +//! All the parameters of sirius should be accessible from this crate. +#![doc = include_str!("../README.md")] +#![deny(missing_docs)] + +/// import the different modules +mod builder; +mod parameters; +mod sirius; +mod sirius_config; +mod sirius_types; +mod traits; +mod versions; + +/// Prelude module +pub mod prelude { + pub use crate::builder::SiriusBuilder; + pub use crate::parameters::*; + pub use crate::sirius::Sirius; + pub use crate::sirius_types::*; + pub use crate::versions::*; +} diff --git a/bindings/sirius-bindings/src/parameters/canopus.rs b/bindings/sirius-bindings/src/parameters/canopus.rs new file mode 100644 index 00000000..5fe97b2a --- /dev/null +++ b/bindings/sirius-bindings/src/parameters/canopus.rs @@ -0,0 +1,50 @@ +use crate::traits::{Enablable, IntoDefault, NamedParametersSet}; + +/// The possible canopus settings +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum CanopusV5 { + /// If the canopus is enabled + Enabled, + + /// The version for `canopus` + Version, + + /// The help for `canopus` + Help, +} + +impl ToString for CanopusV5 { + fn to_string(&self) -> String { + match self { + CanopusV5::Enabled => Self::parameter_set_name().to_string(), + CanopusV5::Help => "--help".to_string(), + CanopusV5::Version => "--version".to_string(), + } + } +} + +impl IntoDefault for CanopusV5 { + fn into_default(self) -> Self { + match self { + CanopusV5::Enabled => CanopusV5::Enabled, + CanopusV5::Help => CanopusV5::Help, + CanopusV5::Version => CanopusV5::Version, + } + } +} + +impl Enablable for CanopusV5 { + fn is_enabler(&self) -> bool { + matches!(self, CanopusV5::Enabled) + } + + fn enabler() -> Self { + CanopusV5::Enabled + } +} + +impl NamedParametersSet for CanopusV5 { + fn parameter_set_name() -> &'static str { + "canopus" + } +} diff --git a/bindings/sirius-bindings/src/parameters/config.rs b/bindings/sirius-bindings/src/parameters/config.rs new file mode 100644 index 00000000..fc3d8c58 --- /dev/null +++ b/bindings/sirius-bindings/src/parameters/config.rs @@ -0,0 +1,786 @@ +use crate::prelude::*; +use crate::traits::{Enablable, IntoDefault, NamedParametersSet}; + +/// The possible config settings +#[derive(Debug, Clone, PartialEq)] +pub enum ConfigV5 { + /// If the config is enabled + Enabled, + + /// This configurations define how to deal with isotope patterns in MS1. + /// When filtering is enabled, molecular formulas are excluded if their theoretical isotope + /// pattern does not match the theoretical one, even if their MS/MS pattern has high score. + IsotopeSettingsFilter(bool), + + /// The database from which to search formulas. This can consist of one or more search databases. + FormulaSearchDB(DBVector), + + /// The database from which to search structures. This can consist of one or more search databases. + StructureSearchDB(DBVector), + + /// The timeout seconds per tree + TimeoutSecondsPerTree(u32), + + /// The number of candidates per ion + NumberOfCandidates(u32), + + /// The number of candidates per ion + NumberOfCandidatesPerIon(u32), + + /// The number of structure candidates + NumberOfStructureCandidates(u32), + + /// Whether to recompute results + RecomputeResults(bool), + + /// Whether to print citations + PrintCitations(bool), + + /// The timeout seconds per instance + TimeoutSecondsPerInstance(u32), + + /// Specifies if the list of Molecular Formula Identifications is filtered by + /// a soft threshold (calculateThreshold) before CSI:FingerID predictions are calculated. + FormulaResultThreshold(bool), + + /// Candidates matching the lipid class estimated by El Gordo will be tagged. + /// The lipid class will only be available if El Gordo predicts that the MS/MS is a lipid spectrum. + /// + /// If this parameter is set to 'false' El Gordo will still be executed and e.g. improve the fragmentation + /// tree, but the matching candidates will not be tagged as lipid class. + /// + /// Default: `true` + InjectElGordoCompounds(bool), + + /// The median noise intensity + MedianNoiseIntensity(f32), + + /// The MS1 absolute intensity error + MS1AbsoluteIntensityError(f32), + + /// The MS1 minimal intensity to consider + MS1MinimalIntensityToConsider(f32), + + /// The MS1 relative intensity error + MS1RelativeIntensityError(f32), + + /// The noise threshold settings intensity threshold + NoiseThresholdSettingsIntensityThreshold(f32), + + /// The noise threshold settings maximal number of peaks + NoiseThresholdSettingsMaximalNumberOfPeaks(u32), + + /// Whether to cluster compounds before running zodiac + ZodiacClusterCompounds(bool), + + /// The zodiac edge filter thresholds min local candidates + ZodiacEdgeFilterThresholdsMinLocalCandidates(u32), + + /// The zodiac edge filter thresholds min local connections + ZodiacEdgeFilterThresholdsMinLocalConnections(u32), + + /// The zodiac edge filter thresholds threshold filter + ZodiacEdgeFilterThresholdsThresholdFilter(f32), + + /// The zodiac epochs burn in period + ZodiacEpochsBurnInPeriod(u32), + + /// The zodiac epochs iterations + ZodiacEpochsIterations(u32), + + /// The number of markov chains for zodiac + ZodiacEpochsNumberOfMarkovChains(u32), + + /// The zodiac library scoring lambda + ZodiacLibraryScoringLambda(u32), + + /// The zodiac library scoring min cosine + ZodiacLibraryScoringMinCosine(f32), + + /// The zodiac number of considered candidates at 300 mz + ZodiacNumberOfConsideredCandidatesAt300Mz(i32), + + /// The zodiac number of considered candidates at 800 mz + ZodiacNumberOfConsideredCandidatesAt800Mz(i32), + + /// The zodiac ratio of considered candidates per ionization + ZodiacRatioOfConsideredCandidatesPerIonization(f32), //can't be negative, higher than 1 or NaN + + /// Whether to run zodiac in two steps + ZodiacRunInTwoSteps(bool), + + /// The MS1 mass deviation allowed mass deviation + MS1MassDeviationAllowedMassDeviation(MassDeviation), + + /// The MS1 mass deviation mass difference deviation + MS1MassDeviationMassDifferenceDeviation(MassDeviation), + + /// The MS1 mass deviation standard mass deviation + MS1MassDeviationStandardMassDeviation(MassDeviation), + + /// The MS2 mass deviation allowed mass deviation + MS2MassDeviationAllowedMassDeviation(MassDeviation), + + /// The MS2 mass deviation standard mass deviation + MS2MassDeviationStandardMassDeviation(MassDeviation), + + /// Detectable elements are added to the chemical alphabet, if there are indications for them (e.g. in isotope pattern) + /// + /// Default: `S,Br,Cl,B,Se` + FormulaSettingsDetectable(AtomVector), + + ///These configurations hold the information how to autodetect elements based on the given + /// formula constraints. + /// Note: If the compound is already assigned to a specific molecular formula, this annotation is ignored. + /// + /// Enforced elements are always considered. + /// + /// Default: `C,H,N,O,P` + FormulaSettingsEnforced(AtomVector), + + /// Fallback elements are used, if the auto-detection fails (e.g. no isotope pattern available) + /// + /// Default: `S` + FormulaSettingsFallback(AtomVector), + + /// Enable/Disable the hypothesen driven recalibration of MS/MS spectra. + /// Must be either `ALLOWED` or `FORBIDDEN` + /// + /// Default: `ALLOWED` + ForbidRecalibration(ForbidRecalibration), + + /// The use heuristic mz to use heuristic + UseHeuristicMZToUseHeuristic(u32), + + /// The use heuristic mz to use heuristic only + UseHeuristicMZToUseHeuristicOnly(u32), + + /// Detectable ion modes which are only considered if there is an indication in the MS1 scan (e.g. correct mass delta). + /// + /// The default is `[M+H]+,[M+K]+,[M+Na]+,[M+H-H2O]+,[M+H-H4O2]+,[M+NH4]+,[M-H]-,[M+Cl]-,[M-H2O-H]-,[M+Br]-`. + AdductSettingsDetectable(AdductsVector), + + /// Fallback ion modes which are considered if the auto + /// detection did not find any indication for an ion mode. + /// ATTENTION: Expanding adducts from ionizations (e.g. `[M+H]+` to `[M+H-H2O]+``) + /// does not respect databases that were selected in the + /// formulas annotation step. + AdductSettingsFallback(AdductsVector), + + /// Configuration profile to store instrument specific algorithm properties. + /// Some of the default profiles are: 'qtof', 'orbitrap', 'fticr'. + /// Default: `default` + AlgorithmProfile(Instruments), + + /// Keywords that can be assigned to a input spectrum to judge its quality. Available keywords + /// are: Good, LowIntensity, NoMS1Peak, FewPeaks, Chimeric, NotMonoisotopicPeak, PoorlyExplained + /// + /// Default: `UNKNOWN` + CompoundQuality(CompoundQuality), + + /// Describes how to deal with Adducts: + /// + /// Pos Examples: + /// `[M+H]+,[M]+,[M+K]+,[M+Na]+,[M+H-H2O]+,[M+Na2-H]+,[M+2K-H]+,[M+NH4]+,[M+H3O]+,[M+MeOH+H]+,[M+ACN+H]+,[M+2ACN+H]+,[M+IPA+H]+,[M+ACN+Na]+,[M+DMSO+H]+` + /// + /// Neg Examples: + /// `[M-H]-,[M]-,[M+K-2H]-,[M+Cl]-,[M-H2O-H]-,[M+Na-2H]-,[M+FA-H]-,[M+Br]-,[M+HAc-H]-,[M+TFA-H]-,[M+ACN-H]-` + /// + /// Default: `,` + /// + /// Enforced ion modes that are always considered. + AdductSettingsEnforced(AdductSettingsEnforced), + + /// This configuration holds a set of user given formulas to be used as candidates for SIRIUS. + /// Note: This set might be merged with other sources like formulas from databases + /// Set of Molecular Formulas to be used as candidates for molecular formula estimation with SIRIUS + /// + /// Currently only the default value is supported. + /// + /// Default: `,` + CandidateFormulas(CandidateFormulas), + + /// Allows the USER to Specify the ScoreType that is used to rank the list of Molecular Formula Identifications + /// before CSI:FingerID predictions are calculated. Auto means that this ScoreType is automatically set depending on the executed workflow. + /// + /// Currently only the default value is supported. + /// + /// Default: `AUTO` + FormulaResultRankingScore(FormulaResultRankingScore), + + /// The isotope ms2 settings + IsotopeMS2Settings(IsotopeMS2Settings), + + /// multiplier for the isotope score. Set to 0 to disable isotope scoring. Otherwise, the score from isotope + /// pattern analysis is multiplied with this coefficient. Set to a value larger than one if + /// your isotope pattern data is of much better quality than your MS/MS data. + /// + /// Default: `1` + IsotopeSettingsMultiplier(u32), + + /// The noise threshold settings absolute threshold + NoiseThresholdSettingsAbsoluteThreshold(u32), + + /// The noise threshold settings base peak + NoiseThresholdSettingsBasePeak(BasePeak), + + /// The structure predictors + StructurePredictors(StructurePredictors), + + /// The possible adduct switches + PossibleAdductSwitches(PossibleAdductSwitches), +} + +impl ToString for ConfigV5 { + fn to_string(&self) -> String { + match self { + ConfigV5::Enabled => Self::parameter_set_name().to_string(), + ConfigV5::IsotopeSettingsFilter(isotope_settings_filter) => { + format!("--IsotopeSettings.filter={}", isotope_settings_filter) + } + ConfigV5::FormulaSearchDB(formula_search_db) => { + format!("--FormulaSearchDB={}", formula_search_db) + } + ConfigV5::StructureSearchDB(structure_search_db) => { + format!("--StructureSearchDB={}", structure_search_db) + } + ConfigV5::TimeoutSecondsPerTree(timeout_seconds_per_tree) => { + format!("--Timeout.secondsPerTree={}", timeout_seconds_per_tree) + } + ConfigV5::NumberOfCandidatesPerIon(number_of_candidates_per_ion) => { + format!( + "--NumberOfCandidatesPerIon={}", + number_of_candidates_per_ion + ) + } + ConfigV5::NumberOfStructureCandidates(number_of_structure_candidates) => { + format!( + "--NumberOfStructureCandidates={}", + number_of_structure_candidates + ) + } + ConfigV5::RecomputeResults(recompute_results) => { + format!("--RecomputeResults={}", recompute_results) + } + ConfigV5::PrintCitations(print_citations) => { + format!("--PrintCitations={}", print_citations) + } + ConfigV5::TimeoutSecondsPerInstance(timeout_seconds_per_instance) => { + format!( + "--Timeout.secondsPerInstance={}", + timeout_seconds_per_instance + ) + } + ConfigV5::FormulaResultThreshold(formula_result_threshold) => { + format!("--FormulaResultThreshold={}", formula_result_threshold) + } + ConfigV5::InjectElGordoCompounds(inject_el_gordo_compounds) => { + format!("--InjectElGordoCompounds={}", inject_el_gordo_compounds) + } + ConfigV5::MedianNoiseIntensity(median_noise_intensity) => { + format!("--MedianNoiseIntensity={}", median_noise_intensity) + } + ConfigV5::MS1AbsoluteIntensityError(ms1_absolute_intensity_error) => { + format!( + "--ms1.absoluteIntensityError={}", + ms1_absolute_intensity_error + ) + } + ConfigV5::MS1MinimalIntensityToConsider(ms1_minimal_intensity_to_consider) => { + format!( + "--ms1.minimalIntensityToConsider={}", + ms1_minimal_intensity_to_consider + ) + } + ConfigV5::MS1RelativeIntensityError(ms1_relative_intensity_error) => { + format!( + "--ms1.relativeIntensityError={}", + ms1_relative_intensity_error + ) + } + ConfigV5::NoiseThresholdSettingsIntensityThreshold( + noise_threshold_settings_intensity_threshold, + ) => format!( + "--NoiseThresholdSettings.intensityThreshold={}", + noise_threshold_settings_intensity_threshold + ), + ConfigV5::NoiseThresholdSettingsMaximalNumberOfPeaks( + noise_threshold_settings_maximal_number_of_peaks, + ) => format!( + "--NoiseThresholdSettings.maximalNumberOfPeaks={}", + noise_threshold_settings_maximal_number_of_peaks + ), + ConfigV5::NumberOfCandidates(number_of_candidates) => { + format!("--NumberOfCandidates={}", number_of_candidates) + } + ConfigV5::ZodiacClusterCompounds(zodiac_cluster_compounds) => { + format!("--ZodiacClusterCompounds={}", zodiac_cluster_compounds) + } + ConfigV5::ZodiacEdgeFilterThresholdsMinLocalCandidates( + zodiac_edge_filter_thresholds_min_local_candidates, + ) => format!( + "--ZodiacEdgeFilterThresholds.minLocalCandidates={}", + zodiac_edge_filter_thresholds_min_local_candidates + ), + ConfigV5::ZodiacEdgeFilterThresholdsMinLocalConnections( + zodiac_edge_filter_thresholds_min_local_connections, + ) => format!( + "--ZodiacEdgeFilterThresholds.minLocalConnections={}", + zodiac_edge_filter_thresholds_min_local_connections + ), + ConfigV5::ZodiacEdgeFilterThresholdsThresholdFilter( + zodiac_edge_filter_thresholds_threshold_filter, + ) => format!( + "--ZodiacEdgeFilterThresholds.thresholdFilter={}", + zodiac_edge_filter_thresholds_threshold_filter + ), + ConfigV5::ZodiacEpochsBurnInPeriod(zodiac_epochs_burn_in_period) => format!( + "--ZodiacEpochs.burnInPeriod={}", + zodiac_epochs_burn_in_period + ), + ConfigV5::ZodiacEpochsIterations(zodiac_epochs_iterations) => { + format!("--ZodiacEpochs.iterations={}", zodiac_epochs_iterations) + } + ConfigV5::ZodiacEpochsNumberOfMarkovChains(zodiac_epochs_number_of_markov_chains) => { + format!( + "--ZodiacEpochs.numberOfMarkovChains={}", + zodiac_epochs_number_of_markov_chains + ) + } + ConfigV5::ZodiacLibraryScoringLambda(zodiac_library_scoring_lambda) => format!( + "--ZodiacLibraryScoring.lambda={}", + zodiac_library_scoring_lambda + ), + ConfigV5::ZodiacLibraryScoringMinCosine(zodiac_library_scoring_min_cosine) => format!( + "--ZodiacLibraryScoring.minCosine={}", + zodiac_library_scoring_min_cosine + ), + ConfigV5::ZodiacNumberOfConsideredCandidatesAt300Mz( + zodiac_number_of_considered_candidates_at_300_mz, + ) => format!( + "--ZodiacNumberOfConsideredCandidatesAt300Mz={}", + zodiac_number_of_considered_candidates_at_300_mz + ), + ConfigV5::ZodiacNumberOfConsideredCandidatesAt800Mz( + zodiac_number_of_considered_candidates_at_800_mz, + ) => format!( + "--ZodiacNumberOfConsideredCandidatesAt800Mz={}", + zodiac_number_of_considered_candidates_at_800_mz + ), + ConfigV5::ZodiacRatioOfConsideredCandidatesPerIonization( + zodiac_ratio_of_considered_candidates_per_ionization, + ) => format!( + "--ZodiacRatioOfConsideredCandidatesPerIonization={}", + zodiac_ratio_of_considered_candidates_per_ionization + ), + ConfigV5::ZodiacRunInTwoSteps(zodiac_run_in_two_steps) => { + format!("--ZodiacRunInTwoSteps={}", zodiac_run_in_two_steps) + } + ConfigV5::MS1MassDeviationAllowedMassDeviation( + ms1_mass_deviation_allowed_mass_deviation, + ) => format!( + "--MS1MassDeviation.allowedMassDeviation={}", + ms1_mass_deviation_allowed_mass_deviation + ), + ConfigV5::MS1MassDeviationMassDifferenceDeviation( + ms1_mass_deviation_mass_difference_deviation, + ) => format!( + "--MS1MassDeviation.massDifferenceDeviation={}", + ms1_mass_deviation_mass_difference_deviation + ), + ConfigV5::MS1MassDeviationStandardMassDeviation( + ms1_mass_deviation_standard_mass_deviation, + ) => format!( + "--MS1MassDeviation.standardMassDeviation={}", + ms1_mass_deviation_standard_mass_deviation + ), + ConfigV5::MS2MassDeviationAllowedMassDeviation( + ms2_mass_deviation_allowed_mass_deviation, + ) => format!( + "--MS2MassDeviation.allowedMassDeviation={}", + ms2_mass_deviation_allowed_mass_deviation + ), + ConfigV5::MS2MassDeviationStandardMassDeviation( + ms2_mass_deviation_standard_mass_deviation, + ) => format!( + "--MS2MassDeviation.standardMassDeviation={}", + ms2_mass_deviation_standard_mass_deviation + ), + ConfigV5::FormulaSettingsDetectable(formula_settings_detectable) => format!( + "--FormulaSettings.detectable={}", + formula_settings_detectable + ), + ConfigV5::FormulaSettingsEnforced(formula_settings_enforced) => { + format!("--FormulaSettings.enforced={}", formula_settings_enforced) + } + ConfigV5::FormulaSettingsFallback(formula_settings_fallback) => { + format!("--FormulaSettings.fallback={}", formula_settings_fallback) + } + ConfigV5::ForbidRecalibration(forbid_recalibration) => { + format!("--ForbidRecalibration={}", forbid_recalibration) + } + ConfigV5::UseHeuristicMZToUseHeuristic(use_heuristic_mz_to_use_heuristic) => { + format!( + "--UseHeuristic.mzToUseHeuristic={}", + use_heuristic_mz_to_use_heuristic + ) + } + ConfigV5::UseHeuristicMZToUseHeuristicOnly(use_heuristic_mz_to_use_heuristic_only) => { + format!( + "--UseHeuristic.mzToUseHeuristicOnly={}", + use_heuristic_mz_to_use_heuristic_only + ) + } + ConfigV5::AdductSettingsDetectable(adduct_settings_detectable) => { + format!("--AdductSettings.detectable={}", adduct_settings_detectable) + } + ConfigV5::AdductSettingsFallback(adduct_settings_fallback) => { + format!("--AdductSettings.fallback={}", adduct_settings_fallback) + } + ConfigV5::AlgorithmProfile(algorithm_profile) => { + format!("--AlgorithmProfile={}", algorithm_profile) + } + ConfigV5::CompoundQuality(compound_quality) => { + format!("--CompoundQuality={}", compound_quality) + } + ConfigV5::AdductSettingsEnforced(adduct_settings_enforced) => { + format!("--AdductSettings.enforced={}", adduct_settings_enforced) + } + ConfigV5::CandidateFormulas(candidate_formulas) => { + format!("--CandidateFormulas={}", candidate_formulas) + } + ConfigV5::FormulaResultRankingScore(formula_result_ranking_score) => { + format!( + "--FormulaResultRankingScore={}", + formula_result_ranking_score + ) + } + ConfigV5::IsotopeMS2Settings(isotope_ms2_settings) => { + format!("--IsotopeMs2Settings={}", isotope_ms2_settings) + } + ConfigV5::IsotopeSettingsMultiplier(isotope_settings_multiplier) => { + format!( + "--IsotopeSettings.multiplier={}", + isotope_settings_multiplier + ) + } + ConfigV5::NoiseThresholdSettingsAbsoluteThreshold( + noise_threshold_settings_absolute_threshold, + ) => format!( + "--NoiseThresholdSettings.absoluteThreshold={}", + noise_threshold_settings_absolute_threshold + ), + ConfigV5::NoiseThresholdSettingsBasePeak(noise_threshold_settings_base_peak) => { + format!( + "--NoiseThresholdSettings.basePeak={}", + noise_threshold_settings_base_peak + ) + } + ConfigV5::StructurePredictors(structure_predictors) => { + format!("--StructurePredictors={}", structure_predictors) + } + ConfigV5::PossibleAdductSwitches(possible_adduct_switches) => { + format!("--PossibleAdductSwitches={}", possible_adduct_switches) + } + } + } +} + +impl IntoDefault for ConfigV5 { + fn into_default(self) -> Self { + match self { + ConfigV5::Enabled => ConfigV5::Enabled, + ConfigV5::IsotopeSettingsFilter(_) => ConfigV5::IsotopeSettingsFilter(true), + ConfigV5::FormulaSearchDB(_) => { + ConfigV5::FormulaSearchDB(DBVector::from(vec![SearchDB::None])) + } + ConfigV5::StructureSearchDB(_) => { + ConfigV5::StructureSearchDB(DBVector::from(vec![SearchDB::Bio])) + } + ConfigV5::TimeoutSecondsPerTree(_) => ConfigV5::TimeoutSecondsPerTree(0), + ConfigV5::NumberOfCandidatesPerIon(_) => ConfigV5::NumberOfCandidatesPerIon(1), + ConfigV5::NumberOfStructureCandidates(_) => { + ConfigV5::NumberOfStructureCandidates(10000) + } + ConfigV5::RecomputeResults(_) => ConfigV5::RecomputeResults(false), + ConfigV5::PrintCitations(_) => ConfigV5::PrintCitations(true), + ConfigV5::TimeoutSecondsPerInstance(_) => ConfigV5::TimeoutSecondsPerInstance(0), + ConfigV5::FormulaResultThreshold(_) => ConfigV5::FormulaResultThreshold(true), + ConfigV5::InjectElGordoCompounds(_) => ConfigV5::InjectElGordoCompounds(true), + ConfigV5::MedianNoiseIntensity(_) => ConfigV5::MedianNoiseIntensity(0.015), + ConfigV5::MS1AbsoluteIntensityError(_) => ConfigV5::MS1AbsoluteIntensityError(0.02), + ConfigV5::MS1MinimalIntensityToConsider(_) => { + ConfigV5::MS1MinimalIntensityToConsider(0.01) + } + ConfigV5::MS1RelativeIntensityError(_) => ConfigV5::MS1RelativeIntensityError(0.08), + ConfigV5::NoiseThresholdSettingsIntensityThreshold(_) => { + ConfigV5::NoiseThresholdSettingsIntensityThreshold(0.005) + } + ConfigV5::NoiseThresholdSettingsMaximalNumberOfPeaks(_) => { + ConfigV5::NoiseThresholdSettingsMaximalNumberOfPeaks(60) + } + ConfigV5::NumberOfCandidates(_) => ConfigV5::NumberOfCandidates(10), + ConfigV5::ZodiacClusterCompounds(_) => ConfigV5::ZodiacClusterCompounds(false), + ConfigV5::ZodiacEdgeFilterThresholdsMinLocalCandidates(_) => { + ConfigV5::ZodiacEdgeFilterThresholdsMinLocalCandidates(1) + } + ConfigV5::ZodiacEdgeFilterThresholdsMinLocalConnections(_) => { + ConfigV5::ZodiacEdgeFilterThresholdsMinLocalConnections(10) + } + ConfigV5::ZodiacEdgeFilterThresholdsThresholdFilter(_) => { + ConfigV5::ZodiacEdgeFilterThresholdsThresholdFilter(0.95) + } + ConfigV5::ZodiacEpochsBurnInPeriod(_) => ConfigV5::ZodiacEpochsBurnInPeriod(2000), + ConfigV5::ZodiacEpochsIterations(_) => ConfigV5::ZodiacEpochsIterations(20000), + ConfigV5::ZodiacEpochsNumberOfMarkovChains(_) => { + ConfigV5::ZodiacEpochsNumberOfMarkovChains(10) + } + ConfigV5::ZodiacLibraryScoringLambda(_) => ConfigV5::ZodiacLibraryScoringLambda(1000), + ConfigV5::ZodiacLibraryScoringMinCosine(_) => { + ConfigV5::ZodiacLibraryScoringMinCosine(0.5) + } + ConfigV5::ZodiacNumberOfConsideredCandidatesAt300Mz(_) => { + ConfigV5::ZodiacNumberOfConsideredCandidatesAt300Mz(10) + } + ConfigV5::ZodiacNumberOfConsideredCandidatesAt800Mz(_) => { + ConfigV5::ZodiacNumberOfConsideredCandidatesAt800Mz(50) + } + ConfigV5::ZodiacRatioOfConsideredCandidatesPerIonization(_) => { + ConfigV5::ZodiacRatioOfConsideredCandidatesPerIonization(0.2) + } + ConfigV5::ZodiacRunInTwoSteps(_) => ConfigV5::ZodiacRunInTwoSteps(true), + ConfigV5::MS1MassDeviationAllowedMassDeviation(_) => { + ConfigV5::MS1MassDeviationAllowedMassDeviation(MassDeviation::ppm(10.0)) + } + ConfigV5::MS1MassDeviationMassDifferenceDeviation(_) => { + ConfigV5::MS1MassDeviationMassDifferenceDeviation(MassDeviation::ppm(5.0)) + } + ConfigV5::MS1MassDeviationStandardMassDeviation(_) => { + ConfigV5::MS1MassDeviationStandardMassDeviation(MassDeviation::ppm(10.0)) + } + ConfigV5::MS2MassDeviationAllowedMassDeviation(_) => { + ConfigV5::MS2MassDeviationAllowedMassDeviation(MassDeviation::ppm(10.0)) + } + ConfigV5::MS2MassDeviationStandardMassDeviation(_) => { + ConfigV5::MS2MassDeviationStandardMassDeviation(MassDeviation::ppm(10.0)) + } + ConfigV5::FormulaSettingsDetectable(_) => { + ConfigV5::FormulaSettingsDetectable(AtomVector::from(vec![ + Atoms::S, + Atoms::Br, + Atoms::Cl, + Atoms::B, + Atoms::Se, + ])) + } + ConfigV5::FormulaSettingsEnforced(_) => { + ConfigV5::FormulaSettingsEnforced(AtomVector::from(vec![ + Atoms::C, + Atoms::H, + Atoms::N, + Atoms::O, + Atoms::P, + ])) + } + ConfigV5::FormulaSettingsFallback(_) => { + ConfigV5::FormulaSettingsFallback(AtomVector::from(vec![Atoms::S])) + } + ConfigV5::ForbidRecalibration(_) => { + ConfigV5::ForbidRecalibration(ForbidRecalibration::Allowed) + } + ConfigV5::UseHeuristicMZToUseHeuristic(_) => { + ConfigV5::UseHeuristicMZToUseHeuristic(300) + } + ConfigV5::UseHeuristicMZToUseHeuristicOnly(_) => { + ConfigV5::UseHeuristicMZToUseHeuristicOnly(650) + } + ConfigV5::AdductSettingsDetectable(_) => { + ConfigV5::AdductSettingsDetectable(AdductsVector::from(vec![ + Adducts::MplusHplus, + Adducts::MplusKplus, + Adducts::MplusNaplus, + Adducts::MplusHminusH2Oplus, + Adducts::MplusHminusTwoH2Oplus, + Adducts::MplusNH4plus, + Adducts::MminusHminus, + Adducts::MplusClminus, + Adducts::MminusH20minusHminus, + Adducts::MplusBromideminus, + ])) + } + ConfigV5::AdductSettingsFallback(_) => { + ConfigV5::AdductSettingsFallback(AdductsVector::from(vec![ + Adducts::MplusHplus, + Adducts::MminusHminus, + Adducts::MplusKplus, + Adducts::MplusNaplus, + ])) + } + ConfigV5::AlgorithmProfile(_) => ConfigV5::AlgorithmProfile(Instruments::Default), + ConfigV5::CompoundQuality(_) => ConfigV5::CompoundQuality(CompoundQuality::Unknown), + ConfigV5::AdductSettingsEnforced(_) => { + ConfigV5::AdductSettingsEnforced(AdductSettingsEnforced::Comma) + } + ConfigV5::CandidateFormulas(_) => ConfigV5::CandidateFormulas(CandidateFormulas::Comma), + ConfigV5::FormulaResultRankingScore(_) => { + ConfigV5::FormulaResultRankingScore(FormulaResultRankingScore::Auto) + } + ConfigV5::IsotopeMS2Settings(_) => { + ConfigV5::IsotopeMS2Settings(IsotopeMS2Settings::Ignore) + } + ConfigV5::IsotopeSettingsMultiplier(_) => ConfigV5::IsotopeSettingsMultiplier(1), + ConfigV5::NoiseThresholdSettingsAbsoluteThreshold(_) => { + ConfigV5::NoiseThresholdSettingsAbsoluteThreshold(0) + } + ConfigV5::NoiseThresholdSettingsBasePeak(_) => { + ConfigV5::NoiseThresholdSettingsBasePeak(BasePeak::NotPrecursor) + } + ConfigV5::StructurePredictors(_) => { + ConfigV5::StructurePredictors(StructurePredictors::CsiFingerId) + } + ConfigV5::PossibleAdductSwitches(_) => { + ConfigV5::PossibleAdductSwitches(PossibleAdductSwitches::DefaultAdductsSwitches) + } + } + } +} + +impl Enablable for ConfigV5 { + fn is_enabler(&self) -> bool { + matches!(self, ConfigV5::Enabled) + } + + fn enabler() -> Self { + ConfigV5::Enabled + } +} + +impl NamedParametersSet for ConfigV5 { + fn parameter_set_name() -> &'static str { + "config" + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_default_isotope_settings_filter() { + assert_eq!( + ConfigV5::IsotopeSettingsFilter(true), + ConfigV5::IsotopeSettingsFilter(false).into_default() + ); + assert_ne!( + ConfigV5::IsotopeSettingsFilter(false), + ConfigV5::IsotopeSettingsFilter(false).into_default() + ); + } + + #[test] + fn test_default_formula_search_db() { + assert_eq!( + ConfigV5::FormulaSearchDB(DBVector::from(vec![SearchDB::None])), + ConfigV5::FormulaSearchDB(DBVector::from(vec![SearchDB::Gnps])).into_default() + ); + assert_eq!( + ConfigV5::FormulaSearchDB(DBVector::from(vec![SearchDB::default()])), + ConfigV5::FormulaSearchDB(DBVector::from(vec![SearchDB::None])) + ); + assert_ne!( + ConfigV5::FormulaSearchDB(DBVector::from(vec![SearchDB::Gnps])), + ConfigV5::FormulaSearchDB(DBVector::from(vec![SearchDB::Gnps])).into_default() + ); + } + + #[test] + fn test_formula_settings_detectable_to_string() { + assert_eq!( + "--FormulaSettings.detectable=S,Br,Cl,B,Se", + ConfigV5::FormulaSettingsDetectable(AtomVector::from(vec![ + Atoms::S, + Atoms::Br, + Atoms::Cl, + Atoms::B, + Atoms::Se, + ])) + .to_string() + ); + } + + #[test] + fn test_default_settings_detectable() { + assert_eq!( + ConfigV5::FormulaSettingsDetectable(AtomVector::from(vec![ + Atoms::S, + Atoms::Br, + Atoms::Cl, + Atoms::B, + Atoms::Se, + ])), + ConfigV5::FormulaSettingsDetectable(AtomVector::from(vec![ + Atoms::S, + Atoms::Br, + Atoms::Cl, + Atoms::B, + Atoms::Fe, + ])) + .into_default() + ); + assert_ne!( + ConfigV5::FormulaSettingsDetectable(AtomVector::from(vec![ + Atoms::S, + Atoms::Br, + Atoms::Cl, + Atoms::B, + Atoms::Se, + ])), + ConfigV5::FormulaSettingsDetectable(AtomVector::from(vec![ + Atoms::S, + Atoms::Br, + Atoms::Cl, + Atoms::B, + Atoms::Fe, + ])) + ); + } + #[test] + fn test_adducts_settings_detectable_to_string() { + assert_ne!( + "--AdductSettings.detectable=H,K,Na,M-H2O+H,M+H-2H2O,NH4,M-H,Cl,M-H2O-H,M+Br", + ConfigV5::AdductSettingsDetectable(AdductsVector::from(vec![ + Adducts::MplusH2OplusHplus, + Adducts::MplusHplus, + Adducts::MplusClminus, + Adducts::MplusHminusH2Oplus, + Adducts::MplusHminusTwoH2Oplus, + Adducts::MplusNH4plus, + Adducts::MminusHminus, + Adducts::MplusClminus, + Adducts::MminusH20minusHminus, + Adducts::MplusBromideminus, + ])) + .to_string() + ); + assert_eq!( + "--AdductSettings.detectable=[M+H]+,[M+K]+,[M+Na]+", + ConfigV5::AdductSettingsDetectable(AdductsVector::from(vec![ + Adducts::MplusHplus, + Adducts::MplusKplus, + Adducts::MplusNaplus, + ])) + .to_string() + ); + assert_eq!( + "--AdductSettings.detectable=[M+H]+,[M+K]+,[M+Na]+,[M+H-H2O]+,[M+H-H4O2]+,[M+NH4]+,[M-H]-,[M+Cl]-,[M-H2O-H]-,[M+Br]-", + ConfigV5::AdductSettingsDetectable(AdductsVector::from(vec![Adducts::MplusHplus])) + .into_default() + .to_string() + ) + } + #[test] + fn check_negative_mass_deviation_panics() { + assert!(std::panic::catch_unwind(|| MassDeviation::ppm(-1.0)).is_err()); + assert!(std::panic::catch_unwind(|| MassDeviation::da(-1.0)).is_err()); + } +} diff --git a/bindings/sirius-bindings/src/parameters/core.rs b/bindings/sirius-bindings/src/parameters/core.rs new file mode 100644 index 00000000..5c57f3b3 --- /dev/null +++ b/bindings/sirius-bindings/src/parameters/core.rs @@ -0,0 +1,33 @@ +use crate::traits::IntoDefault; + +/// The possible core settings +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum CoreV5 { + /// The maximal mz is in the core parameters + MaximalMz(f64), + + /// The maximal number of logical cpus to use. + NCpus(usize), +} + +impl ToString for CoreV5 { + fn to_string(&self) -> String { + match self { + CoreV5::MaximalMz(maximal_mz) => { + format!("--maxmz={}", maximal_mz) + } + CoreV5::NCpus(n_cores) => { + format!("--cores={}", n_cores) + } + } + } +} + +impl IntoDefault for CoreV5 { + fn into_default(self) -> Self { + match self { + CoreV5::MaximalMz(_) => CoreV5::MaximalMz(800.0), + CoreV5::NCpus(_) => CoreV5::NCpus(num_cpus::get()), + } + } +} diff --git a/bindings/sirius-bindings/src/parameters/fingerprint.rs b/bindings/sirius-bindings/src/parameters/fingerprint.rs new file mode 100644 index 00000000..feabfce3 --- /dev/null +++ b/bindings/sirius-bindings/src/parameters/fingerprint.rs @@ -0,0 +1,50 @@ +use crate::traits::{Enablable, IntoDefault, NamedParametersSet}; + +/// The possible fingerprint settings +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum FingerprintV5 { + /// If the fingerprint is enabled + Enabled, + + /// The version for `fingerprint` + Version, + + /// The help for `fingerprint` + Help, +} + +impl ToString for FingerprintV5 { + fn to_string(&self) -> String { + match self { + FingerprintV5::Enabled => Self::parameter_set_name().to_string(), + FingerprintV5::Help => "--help".to_string(), + FingerprintV5::Version => "--version".to_string(), + } + } +} + +impl IntoDefault for FingerprintV5 { + fn into_default(self) -> Self { + match self { + FingerprintV5::Enabled => FingerprintV5::Enabled, + FingerprintV5::Help => FingerprintV5::Help, + FingerprintV5::Version => FingerprintV5::Version, + } + } +} + +impl Enablable for FingerprintV5 { + fn is_enabler(&self) -> bool { + matches!(self, FingerprintV5::Enabled) + } + + fn enabler() -> Self { + FingerprintV5::Enabled + } +} + +impl NamedParametersSet for FingerprintV5 { + fn parameter_set_name() -> &'static str { + "fingerprint" + } +} diff --git a/bindings/sirius-bindings/src/parameters/formula.rs b/bindings/sirius-bindings/src/parameters/formula.rs new file mode 100644 index 00000000..57b9db7b --- /dev/null +++ b/bindings/sirius-bindings/src/parameters/formula.rs @@ -0,0 +1,50 @@ +use crate::traits::{Enablable, IntoDefault, NamedParametersSet}; + +/// The possible formula settings +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum FormulaV5 { + /// If the formula is enabled + Enabled, + + /// The version for `formula` + Version, + + /// The help for `formula` + Help, +} + +impl ToString for FormulaV5 { + fn to_string(&self) -> String { + match self { + FormulaV5::Enabled => Self::parameter_set_name().to_string(), + FormulaV5::Help => "--help".to_string(), + FormulaV5::Version => "--version".to_string(), + } + } +} + +impl IntoDefault for FormulaV5 { + fn into_default(self) -> Self { + match self { + FormulaV5::Enabled => FormulaV5::Enabled, + FormulaV5::Help => FormulaV5::Help, + FormulaV5::Version => FormulaV5::Version, + } + } +} + +impl Enablable for FormulaV5 { + fn is_enabler(&self) -> bool { + matches!(self, FormulaV5::Enabled) + } + + fn enabler() -> Self { + FormulaV5::Enabled + } +} + +impl NamedParametersSet for FormulaV5 { + fn parameter_set_name() -> &'static str { + "formula" + } +} diff --git a/bindings/sirius-bindings/src/parameters/mod.rs b/bindings/sirius-bindings/src/parameters/mod.rs new file mode 100644 index 00000000..36a5064d --- /dev/null +++ b/bindings/sirius-bindings/src/parameters/mod.rs @@ -0,0 +1,32 @@ +/// Get the settings for canopus +pub mod canopus; + +/// Get the settings for config +pub mod config; + +/// Get the settings for core +pub mod core; + +/// Get the settings for fingerprint +pub mod fingerprint; + +/// Get the settings for formula +pub mod formula; + +/// Get the settings for structure +pub mod structure; + +/// Get the settings for write_summaries +pub mod write_summaries; + +/// Get the settings for zodiac +pub mod zodiac; + +pub use canopus::*; +pub use config::*; +pub use core::*; +pub use fingerprint::*; +pub use formula::*; +pub use structure::*; +pub use write_summaries::*; +pub use zodiac::*; diff --git a/bindings/sirius-bindings/src/parameters/structure.rs b/bindings/sirius-bindings/src/parameters/structure.rs new file mode 100644 index 00000000..151c4544 --- /dev/null +++ b/bindings/sirius-bindings/src/parameters/structure.rs @@ -0,0 +1,50 @@ +use crate::traits::{Enablable, IntoDefault, NamedParametersSet}; + +/// The possible structure settings +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum StructureV5 { + /// If the structure is enabled + Enabled, + + /// The version for `structure`` + Version, + + /// The help for `structure`` + Help, +} + +impl ToString for StructureV5 { + fn to_string(&self) -> String { + match self { + StructureV5::Enabled => Self::parameter_set_name().to_string(), + StructureV5::Help => "--help".to_string(), + StructureV5::Version => "--version".to_string(), + } + } +} + +impl IntoDefault for StructureV5 { + fn into_default(self) -> Self { + match self { + StructureV5::Enabled => StructureV5::Enabled, + StructureV5::Help => StructureV5::Help, + StructureV5::Version => StructureV5::Version, + } + } +} + +impl Enablable for StructureV5 { + fn is_enabler(&self) -> bool { + matches!(self, StructureV5::Enabled) + } + + fn enabler() -> Self { + StructureV5::Enabled + } +} + +impl NamedParametersSet for StructureV5 { + fn parameter_set_name() -> &'static str { + "structure" + } +} diff --git a/bindings/sirius-bindings/src/parameters/write_summaries.rs b/bindings/sirius-bindings/src/parameters/write_summaries.rs new file mode 100644 index 00000000..9b0f8c05 --- /dev/null +++ b/bindings/sirius-bindings/src/parameters/write_summaries.rs @@ -0,0 +1,50 @@ +use crate::traits::{Enablable, IntoDefault, NamedParametersSet}; + +/// The possible write summaries settings +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum WriteSummariesV5 { + /// If the write summaries is enabled + Enabled, + + /// The version of the write summaries + Version, + + /// The help for the write summaries + Help, +} + +impl ToString for WriteSummariesV5 { + fn to_string(&self) -> String { + match self { + WriteSummariesV5::Enabled => Self::parameter_set_name().to_string(), + WriteSummariesV5::Help => "--help".to_string(), + WriteSummariesV5::Version => "--version".to_string(), + } + } +} + +impl IntoDefault for WriteSummariesV5 { + fn into_default(self) -> Self { + match self { + WriteSummariesV5::Enabled => WriteSummariesV5::Enabled, + WriteSummariesV5::Help => WriteSummariesV5::Help, + WriteSummariesV5::Version => WriteSummariesV5::Version, + } + } +} + +impl Enablable for WriteSummariesV5 { + fn is_enabler(&self) -> bool { + matches!(self, WriteSummariesV5::Enabled) + } + + fn enabler() -> Self { + WriteSummariesV5::Enabled + } +} + +impl NamedParametersSet for WriteSummariesV5 { + fn parameter_set_name() -> &'static str { + "write-summaries" + } +} diff --git a/bindings/sirius-bindings/src/parameters/zodiac.rs b/bindings/sirius-bindings/src/parameters/zodiac.rs new file mode 100644 index 00000000..86f98677 --- /dev/null +++ b/bindings/sirius-bindings/src/parameters/zodiac.rs @@ -0,0 +1,50 @@ +use crate::traits::{Enablable, IntoDefault, NamedParametersSet}; + +/// The possible zodiac settings +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum ZodiacV5 { + /// If the zodiac is enabled + Enabled, + + /// The version of the zodiac + Version, + + /// The help for the zodiac + Help, +} + +impl ToString for ZodiacV5 { + fn to_string(&self) -> String { + match self { + ZodiacV5::Enabled => Self::parameter_set_name().to_string(), + ZodiacV5::Help => "--help".to_string(), + ZodiacV5::Version => "--version".to_string(), + } + } +} + +impl IntoDefault for ZodiacV5 { + fn into_default(self) -> Self { + match self { + ZodiacV5::Enabled => ZodiacV5::Enabled, + ZodiacV5::Help => ZodiacV5::Help, + ZodiacV5::Version => ZodiacV5::Version, + } + } +} + +impl Enablable for ZodiacV5 { + fn is_enabler(&self) -> bool { + matches!(self, ZodiacV5::Enabled) + } + + fn enabler() -> Self { + ZodiacV5::Enabled + } +} + +impl NamedParametersSet for ZodiacV5 { + fn parameter_set_name() -> &'static str { + "zodiac" + } +} diff --git a/bindings/sirius-bindings/src/sirius.rs b/bindings/sirius-bindings/src/sirius.rs new file mode 100644 index 00000000..fc3b2e62 --- /dev/null +++ b/bindings/sirius-bindings/src/sirius.rs @@ -0,0 +1,203 @@ +//! # Sirius +use crate::sirius_config::SiriusConfig; +use crate::versions::Version; +use dotenvy::dotenv; +use is_executable::IsExecutable; +use std::env; +use std::path::Path; +use std::process::Command; + +/// The main struct for the Sirius bindings +pub struct Sirius { + config: SiriusConfig, +} + +impl From> for Sirius { + fn from(config: SiriusConfig) -> Self { + Sirius { config } + } +} + +impl Sirius { + /// Run the sirius command with the given input and output file paths. + /// + /// The sirius executable is expected to be available in the environment variable SIRIUS_PATH. + /// The username and password for the sirius account are expected to be available in the environment variables SIRIUS_USERNAME and SIRIUS_PASSWORD. + /// + /// This function gets the parameters that where set in the SiriusBuilder struct and runs the sirius command with the given input and output file paths. + /// + /// # Arguments + /// * `input_file_path` - The path to the input file + /// * `output_file_path` - The path to the output file + pub fn run(&self, input_file_path: &Path, output_file_path: &Path) -> Result<(), String> { + // Load environment variables from .env file + dotenv().ok(); + + // Fetch the path of the sirius command from environment variables + let sirius_path = env::var("SIRIUS_PATH").map_err(|_| { + concat!( + "The environment variable SIRIUS_PATH is not set. ", + "We expected there to exist a .env file in the current directory ", + "with the SIRIUS_PATH variable set to the path of the sirius executable. ", + "The variable may also be set in the environment directly, for instance ", + "in the .bashrc file." + ) + .to_string() + })?; + + // We need to verify that the SIRIUS_PATH is a valid path to a file, and not a directory. + + let sirius_path = Path::new(&sirius_path); + + if !sirius_path.exists() { + return Err(format!("The sirius path {:?} does not exist", sirius_path)); + } + + if !sirius_path.is_file() { + return Err(format!("The sirius path {:?} is not a file", sirius_path)); + } + + // We also need to check whether the file is executable, but this will be different + // depending on the operating system. Fortunately, the complexity of this is hidden + // behind the is_executable crate. + if !sirius_path.is_executable() { + return Err(format!( + "The sirius executable at {:?} is not executable", + sirius_path + )); + } + + // Fetch the SIRIUS_USERNAME and the SIRIUS_PASSWORD from environment variables + // in order to login before launching the sirius command + + let sirius_username = env::var("SIRIUS_USERNAME").map_err(|_| { + concat!( + "The environment variable SIRIUS_USERNAME is not set. ", + "We expected there to exist a .env file in the current directory ", + "with the SIRIUS_USERNAME variable set to the username of the sirius account. ", + "The variable may also be set in the environment directly, for instance ", + "in the .bashrc file." + ) + .to_string() + })?; + + let sirius_password = env::var("SIRIUS_PASSWORD").map_err(|_| { + concat!( + "The environment variable SIRIUS_PASSWORD is not set. ", + "We expected there to exist a .env file in the current directory ", + "with the SIRIUS_PASSWORD variable set to the password of the sirius account. ", + "The variable may also be set in the environment directly, for instance ", + "in the .bashrc file." + ) + .to_string() + })?; + + // We check that the provided sirius username and password are not empty + if sirius_username.is_empty() { + return Err(concat!( + "The sirius username provided in the environment variable SIRIUS_USERNAME is empty. ", + "We expected there to exist a .env file in the current directory ", + "with the SIRIUS_USERNAME variable set to the username of the sirius account. ", + "The variable may also be set in the environment directly, for instance ", + "in the .bashrc file." + ).to_string()); + } + + if sirius_password.is_empty() { + return Err(concat!( + "The sirius password provided in the environment variable SIRIUS_PASSWORD is empty. ", + "We expected there to exist a .env file in the current directory ", + "with the SIRIUS_PASSWORD variable set to the password of the sirius account. ", + "The variable may also be set in the environment directly, for instance ", + "in the .bashrc file." + ).to_string()); + } + + // Prepare and execute the login command + let mut binding = Command::new(sirius_path); + + // Set both SIRIUS_PASSWORD and SIRIUS_USERNAME environment variables + binding + .env("SIRIUS_USERNAME", &sirius_username) + .env("SIRIUS_PASSWORD", &sirius_password); + + let login_command_status = binding + .args([ + "login", + "--user-env", + "SIRIUS_USERNAME", + "--password-env", + "SIRIUS_PASSWORD", + ]) + .status() + .map_err(|e| format!("Failed to execute Sirius login command: {}", e))?; + + // We make sure to print the login command status for debugging + + if !login_command_status.success() { + return Err("Sirius login command failed".to_string()); + } + + // We now check that the input file exists and is a file and not a directory + if !input_file_path.exists() { + return Err(format!( + "The input file {:?} does not exist", + input_file_path + )); + } + + if !input_file_path.is_file() { + return Err(format!( + "The input file {:?} is not a file", + input_file_path + )); + } + + // We check that the extension of the input file is MGF, in either upper or lower case + let input_file_extension = input_file_path.extension().ok_or_else(|| { + format!( + concat!( + "The input file {:?} does not have an extension. ", + "We expected the input file to have the extension .mgf" + ), + input_file_path + ) + })?; + + if input_file_extension.to_string_lossy().to_lowercase() != "mgf" { + return Err(format!( + "The input file {:?} does not have the extension .mgf", + input_file_path + )); + } + + // Prepare the command + let mut command = Command::new(sirius_path); + + // Start building the argument list + // Add input and output file paths with their respective flags + let mut args = vec![ + "-i".to_string(), + input_file_path.to_str().unwrap().to_string(), + "--output".to_string(), + output_file_path.to_str().unwrap().to_string(), + ]; + + // Add arguments from config directly + args.extend(self.config.args().iter().cloned()); + + // Add arguments and spawn the command + // let mut child = command.args(&args).spawn().expect("Sirius failed to start"); + // let status = child.wait().expect("Failed to wait on child"); + let status = command + .args(&args) + .status() + .expect("Sirius failed to start"); + + if !status.success() { + return Err("Sirius failed".to_string()); + } + + Ok(()) + } +} diff --git a/bindings/sirius-bindings/src/sirius_config.rs b/bindings/sirius-bindings/src/sirius_config.rs new file mode 100644 index 00000000..1a45a5af --- /dev/null +++ b/bindings/sirius-bindings/src/sirius_config.rs @@ -0,0 +1,360 @@ +use crate::prelude::*; +use crate::traits::Enablable; + +/// Struct providing the configuration for Sirius. +/// +/// # Implementative details +/// This struct MUST be a private struct. It is only used by the [`SiriusBuilder`](crate::builder::SiriusBuilder) to +/// build the [`Sirius`](crate::sirius::Sirius) struct, and through the builder we can evaluate all of the provided +/// parameters. If we make this struct public, we would allow the user to create a [`Sirius`](crate::sirius::Sirius) +/// struct with invalid parameters. DO NOT MAKE THIS STRUCT PUBLIC. +/// +pub(crate) struct SiriusConfig { + core_parameters: Vec, + config_parameters: Vec, + formula_parameters: Vec, + zodiac_parameters: Vec, + fingerprint_parameters: Vec, + structure_parameters: Vec, + canopus_parameters: Vec, + write_summaries_parameters: Vec, +} + +impl Default for SiriusConfig { + fn default() -> Self { + SiriusConfig { + core_parameters: Vec::new(), + config_parameters: Vec::new(), + formula_parameters: Vec::new(), + zodiac_parameters: Vec::new(), + fingerprint_parameters: Vec::new(), + structure_parameters: Vec::new(), + canopus_parameters: Vec::new(), + write_summaries_parameters: Vec::new(), + } + } +} + +impl SiriusConfig { + /// Add a parameter to the core configuration. + /// + /// # Arguments + /// + /// * `parameter` - The parameter to add. + /// + pub fn add_core_parameter(&mut self, parameter: V::Core) -> Result<(), String> { + // We check if the parameter is already present in the vector + // If it is, we return an error + if let Some(existing_parameter) = self + .core_parameters + .iter() + .find(|&p| std::mem::discriminant(p) == std::mem::discriminant(¶meter)) + { + Err(format!( + concat!( + "The core parameter {:?} cannot be added to the configuration. ", + "There is already an existing parameter which is {:?}. ", + "You cannot add it twice." + ), + parameter, existing_parameter + )) + } else { + self.core_parameters.push(parameter); + Ok(()) + } + } + + /// Add a parameter to the config configuration. + /// + /// # Arguments + /// + /// * `parameter` - The parameter to add. + /// + pub fn add_config_parameter(&mut self, parameter: V::Config) -> Result<(), String> { + // We check if the parameter is already present in the vector + // If it is, we return an error + if let Some(existing_parameter) = self + .config_parameters + .iter() + .find(|&p| std::mem::discriminant(p) == std::mem::discriminant(¶meter)) + { + Err(format!( + concat!( + "The config parameter {:?} cannot be added to the configuration. ", + "There is already an existing parameter which is {:?}. ", + "You cannot add it twice." + ), + parameter, existing_parameter + )) + } else { + if !parameter.is_enabler() { + // If the current parameter is not an enabler, we make sure that the enabler variant + // is present in the vector by trying to insert it without checking if it is already + // present. + let _ = self.add_config_parameter(V::Config::enabler()); + } + self.config_parameters.push(parameter); + Ok(()) + } + } + + /// Add a parameter to the formula configuration. + /// + /// # Arguments + /// + /// * `parameter` - The parameter to add. + /// + pub fn add_formula_parameter(&mut self, parameter: V::Formula) -> Result<(), String> { + // We check if the parameter is already present in the vector + // If it is, we return an error + if let Some(existing_parameter) = self + .formula_parameters + .iter() + .find(|&p| std::mem::discriminant(p) == std::mem::discriminant(¶meter)) + { + Err(format!( + concat!( + "The formula parameter {:?} cannot be added to the configuration. ", + "There is already an existing parameter which is {:?}. ", + "You cannot add it twice." + ), + parameter, existing_parameter + )) + } else { + if !parameter.is_enabler() { + // If the current parameter is not an enabler, we make sure that the enabler variant + // is present in the vector by trying to insert it without checking if it is already + // present. + let _ = self.add_formula_parameter(V::Formula::enabler()); + } + self.formula_parameters.push(parameter); + Ok(()) + } + } + + /// Add a parameter to the zodiac configuration. + /// + /// # Arguments + /// + /// * `parameter` - The parameter to add. + /// + pub fn add_zodiac_parameter(&mut self, parameter: V::Zodiac) -> Result<(), String> { + // We check if the parameter is already present in the vector + // If it is, we return an error + if let Some(existing_parameter) = self + .zodiac_parameters + .iter() + .find(|&p| std::mem::discriminant(p) == std::mem::discriminant(¶meter)) + { + Err(format!( + concat!( + "The zodiac parameter {:?} cannot be added to the configuration. ", + "There is already an existing parameter which is {:?}. ", + "You cannot add it twice." + ), + parameter, existing_parameter + )) + } else { + if !parameter.is_enabler() { + // If the current parameter is not an enabler, we make sure that the enabler variant + // is present in the vector by trying to insert it without checking if it is already + // present. + let _ = self.add_zodiac_parameter(V::Zodiac::enabler()); + } + self.zodiac_parameters.push(parameter); + Ok(()) + } + } + + /// Add a parameter to the fingerprint configuration. + /// + /// # Arguments + /// + /// * `parameter` - The parameter to add. + /// + pub fn add_fingerprint_parameter(&mut self, parameter: V::Fingerprint) -> Result<(), String> { + // We check if the parameter is already present in the vector + // If it is, we return an error + if let Some(existing_parameter) = self + .fingerprint_parameters + .iter() + .find(|&p| std::mem::discriminant(p) == std::mem::discriminant(¶meter)) + { + Err(format!( + concat!( + "The fingerprint parameter {:?} cannot be added to the configuration. ", + "There is already an existing parameter which is {:?}. ", + "You cannot add it twice." + ), + parameter, existing_parameter + )) + } else { + if !parameter.is_enabler() { + // If the current parameter is not an enabler, we make sure that the enabler variant + // is present in the vector by trying to insert it without checking if it is already + // present. + let _ = self.add_fingerprint_parameter(V::Fingerprint::enabler()); + } + self.fingerprint_parameters.push(parameter); + Ok(()) + } + } + + /// Add a parameter to the structure configuration. + /// + /// # Arguments + /// + /// * `parameter` - The parameter to add. + /// + pub fn add_structure_parameter(&mut self, parameter: V::Structure) -> Result<(), String> { + // We check if the parameter is already present in the vector + // If it is, we return an error + if let Some(existing_parameter) = self + .structure_parameters + .iter() + .find(|&p| std::mem::discriminant(p) == std::mem::discriminant(¶meter)) + { + Err(format!( + concat!( + "The structure parameter {:?} cannot be added to the configuration. ", + "There is already an existing parameter which is {:?}. ", + "You cannot add it twice." + ), + parameter, existing_parameter + )) + } else { + if !parameter.is_enabler() { + // If the current parameter is not an enabler, we make sure that the enabler variant + // is present in the vector by trying to insert it without checking if it is already + // present. + let _ = self.add_structure_parameter(V::Structure::enabler()); + } + self.structure_parameters.push(parameter); + Ok(()) + } + } + + /// Add a parameter to the canopus configuration. + /// + /// # Arguments + /// + /// * `parameter` - The parameter to add. + /// + pub fn add_canopus_parameter(&mut self, parameter: V::Canopus) -> Result<(), String> { + // We check if the parameter is already present in the vector + // If it is, we return an error + if let Some(existing_parameter) = self + .canopus_parameters + .iter() + .find(|&p| std::mem::discriminant(p) == std::mem::discriminant(¶meter)) + { + Err(format!( + concat!( + "The canopus parameter {:?} cannot be added to the configuration. ", + "There is already an existing parameter which is {:?}. ", + "You cannot add it twice." + ), + parameter, existing_parameter + )) + } else { + if !parameter.is_enabler() { + // If the current parameter is not an enabler, we make sure that the enabler variant + // is present in the vector by trying to insert it without checking if it is already + // present. + let _ = self.add_canopus_parameter(V::Canopus::enabler()); + } + self.canopus_parameters.push(parameter); + Ok(()) + } + } + + /// Add a parameter to the write_summaries configuration. + /// + /// # Arguments + /// + /// * `parameter` - The parameter to add. + /// + pub fn add_write_summaries_parameter( + &mut self, + parameter: V::WriteSummaries, + ) -> Result<(), String> { + // We check if the parameter is already present in the vector + // If it is, we return an error + if let Some(existing_parameter) = self + .write_summaries_parameters + .iter() + .find(|&p| std::mem::discriminant(p) == std::mem::discriminant(¶meter)) + { + Err(format!( + concat!( + "The write_summaries parameter {:?} cannot be added to the configuration. ", + "There is already an existing parameter which is {:?}. ", + "You cannot add it twice." + ), + parameter, existing_parameter + )) + } else { + if !parameter.is_enabler() { + // If the current parameter is not an enabler, we make sure that the enabler variant + // is present in the vector by trying to insert it without checking if it is already + // present. + let _ = self.add_write_summaries_parameter(V::WriteSummaries::enabler()); + } + self.write_summaries_parameters.push(parameter); + Ok(()) + } + } + + pub fn args(&self) -> Vec { + self.core_parameters + .iter() + .map(|p| p.to_string()) + .chain(self.config_parameters.iter().map(|p| p.to_string())) + .chain(self.formula_parameters.iter().map(|p| p.to_string())) + .chain(self.zodiac_parameters.iter().map(|p| p.to_string())) + .chain(self.fingerprint_parameters.iter().map(|p| p.to_string())) + .chain(self.structure_parameters.iter().map(|p| p.to_string())) + .chain(self.canopus_parameters.iter().map(|p| p.to_string())) + .chain( + self.write_summaries_parameters + .iter() + .map(|p| p.to_string()), + ) + .collect::>() + } +} + +impl ToString for SiriusConfig { + fn to_string(&self) -> String { + self.args().join(" ") + } +} + +#[cfg(test)] + +mod tests { + use super::*; + + #[test] + fn test_sirius_config() { + let mut config: SiriusConfig = SiriusConfig::default(); + config + .add_config_parameter(ConfigV5::IsotopeSettingsFilter(true)) + .unwrap(); + config + .add_config_parameter(ConfigV5::FormulaSearchDB(DBVector::from(vec![ + SearchDB::Bio, + ]))) + .unwrap(); + + assert!(config + .add_config_parameter(ConfigV5::IsotopeSettingsFilter(true)) + .is_err()); + + assert!(config + .add_config_parameter(ConfigV5::FormulaSearchDB(DBVector::from(vec![ + SearchDB::Bio + ]))) + .is_err()); + } +} diff --git a/bindings/sirius-bindings/src/sirius_types/README.md b/bindings/sirius-bindings/src/sirius_types/README.md new file mode 100644 index 00000000..d41df355 --- /dev/null +++ b/bindings/sirius-bindings/src/sirius_types/README.md @@ -0,0 +1,4 @@ +# Sirius types +This directory contains the types for the parameters of the `sirius config` command. + +This dir is subject to changes as we advance in the project and see if there are some errors. diff --git a/bindings/sirius-bindings/src/sirius_types/adduct_settings_enforced.rs b/bindings/sirius-bindings/src/sirius_types/adduct_settings_enforced.rs new file mode 100644 index 00000000..9fd09b78 --- /dev/null +++ b/bindings/sirius-bindings/src/sirius_types/adduct_settings_enforced.rs @@ -0,0 +1,37 @@ +use std::fmt::Display; + +/// The possible adduct settings enforced +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Copy)] +pub enum AdductSettingsEnforced { + /// The default adduct settings enforced + #[default] + Comma, +} + +impl Display for AdductSettingsEnforced { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + AdductSettingsEnforced::Comma => write!(f, ","), + } + } +} + +impl<'a> TryFrom<&'a str> for AdductSettingsEnforced { + type Error = String; + + fn try_from(s: &'a str) -> Result { + match s { + "," => Ok(AdductSettingsEnforced::Comma), + _ => Err(format!("Unknown adduct settings enforced: {}", s)), + } + } +} + +impl TryFrom for AdductSettingsEnforced { + type Error = String; + + fn try_from(s: String) -> Result { + AdductSettingsEnforced::try_from(s.as_str()) + } +} diff --git a/bindings/sirius-bindings/src/sirius_types/adducts.rs b/bindings/sirius-bindings/src/sirius_types/adducts.rs new file mode 100644 index 00000000..d31ce7f5 --- /dev/null +++ b/bindings/sirius-bindings/src/sirius_types/adducts.rs @@ -0,0 +1,182 @@ +use std::fmt::Display; + +/// The possible adducts +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum Adducts { + /// \[M+H\]+ + MplusHplus, + + /// \[M+K\]+ + MplusKplus, + /// \[M+Na\]+ + MplusNaplus, + + /// \[M+NH4\]+ + MplusNH4plus, + + /// \[M+Cl\]- + MplusClminus, + + /// \[M\]+ + Mplus, + + /// \[M+H-H2O\]+ + MplusHminusH2Oplus, + + /// \[M+H3N+H\]+ + MplusH3NplusHplus, + + /// \[M+H2O+H\]+ + MplusH2OplusHplus, + + /// \[M+CH4O+H\]+ + MplusCH4OplusHplus, + + /// \[M+C2H3N+H\]+ + MplusC2H3NplusHplus, + + /// \[M+C4H6N2+H\]+ + MplusC4H6N2plusHplus, + + /// \[M+C3H8O+H\]+ + MplusC3H8OplusHplus, + + /// \[M+C2H6OS+H\]+ + MplusC2H6OSplusHplus, + + /// \[M-H+K+K\]+ + MminusHplusKKplus, + + /// \[M-H+Na+Na\]+ + MminusHplusNaNaplus, + + /// \[M+C2H3N+Na\]+ + MplusC2H3NplusNaplus, + + /// \[M-H\]- + MminusHminus, + + /// \[M+H-H4O2\]+ + MplusHminusTwoH2Oplus, + + /// \[M-H2O-H\]- + MminusH20minusHminus, + + /// \[M+Br\]- + MplusBromideminus, +} + +impl Display for Adducts { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Adducts::MplusHplus => write!(f, "[M+H]+"), + Adducts::MplusKplus => write!(f, "[M+K]+"), + Adducts::MplusNaplus => write!(f, "[M+Na]+"), + Adducts::MplusNH4plus => write!(f, "[M+NH4]+"), + Adducts::MplusClminus => write!(f, "[M+Cl]-"), + Adducts::Mplus => write!(f, "[M]+"), + Adducts::MplusHminusH2Oplus => write!(f, "[M+H-H2O]+"), + Adducts::MplusH3NplusHplus => write!(f, "[M+H3N+H]+"), + Adducts::MplusH2OplusHplus => write!(f, "[M+H2O+H]+"), + Adducts::MplusCH4OplusHplus => write!(f, "[M+CH4O+H]+"), + Adducts::MplusC2H3NplusHplus => write!(f, "[M+C2H3N+H]+"), + Adducts::MplusC4H6N2plusHplus => write!(f, "[M+C4H6N2+H]+"), + Adducts::MplusC3H8OplusHplus => write!(f, "[M+C3H8O+H]+"), + Adducts::MplusC2H6OSplusHplus => write!(f, "[M+C2H6OS+H]+"), + Adducts::MminusHplusKKplus => write!(f, "[M-H+K+K]+"), + Adducts::MminusHplusNaNaplus => write!(f, "[M-H+Na+Na]+"), + Adducts::MplusC2H3NplusNaplus => write!(f, "[M+C2H3N+Na]+"), + Adducts::MminusHminus => write!(f, "[M-H]-"), + Adducts::MplusHminusTwoH2Oplus => write!(f, "[M+H-H4O2]+"), + Adducts::MminusH20minusHminus => write!(f, "[M-H2O-H]-"), + Adducts::MplusBromideminus => write!(f, "[M+Br]-"), + } + } +} + +impl<'a> TryFrom<&'a str> for Adducts { + type Error = String; + + fn try_from(s: &'a str) -> Result { + match s { + "[M+H]+" => Ok(Adducts::MplusHplus), + "[M+K]+" => Ok(Adducts::MplusKplus), + "[M+Na]+" => Ok(Adducts::MplusNaplus), + "[M+NH4]+" => Ok(Adducts::MplusNH4plus), + "[M+Cl]-" => Ok(Adducts::MplusClminus), + "[M]+" => Ok(Adducts::Mplus), + "[M+H-H2O]+" => Ok(Adducts::MplusHminusH2Oplus), + "[M+H3N+H]+" => Ok(Adducts::MplusH3NplusHplus), + "[M+H2O+H]+" => Ok(Adducts::MplusH2OplusHplus), + "[M+CH4O+H]+" => Ok(Adducts::MplusCH4OplusHplus), + "[M+C2H3N+H]+" => Ok(Adducts::MplusC2H3NplusHplus), + "[M+C4H6N2+H]+" => Ok(Adducts::MplusC4H6N2plusHplus), + "[M+C3H8O+H]+" => Ok(Adducts::MplusC3H8OplusHplus), + "[M+C2H6OS+H]+" => Ok(Adducts::MplusC2H6OSplusHplus), + "[M-H+K+K]+" => Ok(Adducts::MminusHplusKKplus), + "[M-H+Na+Na]+" => Ok(Adducts::MminusHplusNaNaplus), + "[M+C2H3N+Na]+" => Ok(Adducts::MplusC2H3NplusNaplus), + "[M-H]-" => Ok(Adducts::MminusHminus), + "[M+H-H4O2]+" => Ok(Adducts::MplusHminusTwoH2Oplus), + "[M-H2O-H]-" => Ok(Adducts::MminusH20minusHminus), + "[M+Br]-" => Ok(Adducts::MplusBromideminus), + _ => Err(format!("Unknown adduct: {}", s)), + } + } +} + +impl TryFrom for Adducts { + type Error = String; + + fn try_from(s: String) -> Result { + Adducts::try_from(s.as_str()) + } +} + +/// Creates a vector of adducts +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] +pub struct AdductsVector(Vec); + +impl Display for AdductsVector { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut adducts_string = self.0.iter(); + if let Some(adduct) = adducts_string.next() { + write!(f, "{}", adduct)?; + for adduct in adducts_string { + write!(f, ",{}", adduct)?; + } + } + Ok(()) + } +} + +impl From> for AdductsVector { + fn from(adducts: Vec) -> Self { + AdductsVector(adducts) + } +} + +impl<'a> TryFrom<&'a str> for AdductsVector { + type Error = String; + + fn try_from(s: &'a str) -> Result { + let adducts = s + .split(',') + .map(|adduct| { + Adducts::try_from(adduct) + .map_err(|e| format!("Cannot parse adduct: {} ({}). Maybe forgot to put a comma between adducts ?", adduct, e)) + }) + .collect::, _>>()?; + Ok(AdductsVector(adducts)) + } +} + +impl TryFrom for AdductsVector { + type Error = String; + + fn try_from(s: String) -> Result { + AdductsVector::try_from(s.as_str()) + } +} diff --git a/bindings/sirius-bindings/src/sirius_types/atoms.rs b/bindings/sirius-bindings/src/sirius_types/atoms.rs new file mode 100644 index 00000000..1afbee15 --- /dev/null +++ b/bindings/sirius-bindings/src/sirius_types/atoms.rs @@ -0,0 +1,645 @@ +use std::fmt::Display; + +/// Enumaration of all the atoms +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum Atoms { + /// Hydrogen + H, + /// Helium + He, + /// Lithium + Li, + /// Beryllium + Be, + /// Boron + B, + /// Carbon + C, + /// Nitrogen + N, + /// Oxygen + O, + /// Fluorine + F, + /// Neon + Ne, + /// Sodium + Na, + /// Magnesium + Mg, + /// Aluminium + Al, + /// Silicon + Si, + /// Phosphorus + P, + /// Sulfur + S, + /// Chlorine + Cl, + /// Argon + Ar, + /// Potassium + K, + /// Calcium + Ca, + /// Scandium + Sc, + /// Titanium + Ti, + /// Vanadium + V, + /// Chromium + Cr, + /// Manganese + Mn, + /// Iron + Fe, + /// Cobalt + Co, + /// Nickel + Ni, + /// Copper + Cu, + /// Zinc + Zn, + /// Gallium + Ga, + /// Germanium + Ge, + /// Arsenic + As, + /// Selenium + Se, + /// Bromine + Br, + /// Krypton + Kr, + /// Rubidium + Rb, + /// Strontium + Sr, + /// Yttrium + Y, + /// Zirconium + Zr, + /// Niobium + Nb, + /// Molybdenum + Mo, + /// Technetium + Tc, + /// Ruthenium + Ru, + /// Rhodium + Rh, + /// Palladium + Pd, + /// Silver + Ag, + /// Cadmium + Cd, + /// Indium + In, + /// Tin + Sn, + /// Antimony + Sb, + /// Tellurium + Te, + /// Iodine + I, + /// Xenon + Xe, + /// Cesium + Cs, + /// Barium + Ba, + /// Lanthanum + La, + /// Cerium + Ce, + /// Praseodymium + Pr, + /// Neodymium + Nd, + /// Promethium + Pm, + /// Samarium + Sm, + /// Europium + Eu, + /// Gadolinium + Gd, + /// Terbium + Tb, + /// Dysprosium + Dy, + /// Holmium + Ho, + /// Erbium + Er, + /// Thulium + Tm, + /// Ytterbium + Yb, + /// Lutetium + Lu, + /// Hafnium + Hf, + /// Tantalum + Ta, + /// Tungsten + W, + /// Rhenium + Re, + /// Osmium + Os, + /// Iridium + Ir, + /// Platinum + Pt, + /// Gold + Au, + /// Mercury + Hg, + /// Thallium + Tl, + /// Lead + Pb, + /// Bismuth + Bi, + /// Polonium + Po, + /// Astatine + At, + /// Radon + Rn, + /// Francium + Fr, + /// Radium + Ra, + /// Actinium + Ac, + /// Thorium + Th, + /// Protactinium + Pa, + /// Uranium + U, + /// Neptunium + Np, + /// Plutonium + Pu, + /// Americium + Am, + /// Curium + Cm, + /// Berkelium + Bk, + /// Californium + Cf, + /// Einsteinium + Es, + /// Fermium + Fm, + /// Mendelevium + Md, + /// Nobelium + No, + /// Lawrencium + Lr, + /// Rutherfordium + Rf, + /// Dubnium + Db, + /// Seaborgium + Sg, + /// Bohrium + Bh, + /// Hassium + Hs, + /// Meitnerium + Mt, + /// Darmstadtium + Ds, + /// Roentgenium + Rg, + /// Copernicium + Cn, + /// Nihonium + Nh, + /// Flerovium + Fl, + /// Moscovium + Mc, + /// Livermorium + Lv, + /// Tennessine + Ts, + /// Oganesson + Og, +} + +impl Display for Atoms { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Atoms::H => write!(f, "H"), + Atoms::He => write!(f, "He"), + Atoms::Li => write!(f, "Li"), + Atoms::Be => write!(f, "Be"), + Atoms::B => write!(f, "B"), + Atoms::C => write!(f, "C"), + Atoms::N => write!(f, "N"), + Atoms::O => write!(f, "O"), + Atoms::F => write!(f, "F"), + Atoms::Ne => write!(f, "Ne"), + Atoms::Na => write!(f, "Na"), + Atoms::Mg => write!(f, "Mg"), + Atoms::Al => write!(f, "Al"), + Atoms::Si => write!(f, "Si"), + Atoms::P => write!(f, "P"), + Atoms::S => write!(f, "S"), + Atoms::Cl => write!(f, "Cl"), + Atoms::Ar => write!(f, "Ar"), + Atoms::K => write!(f, "K"), + Atoms::Ca => write!(f, "Ca"), + Atoms::Sc => write!(f, "Sc"), + Atoms::Ti => write!(f, "Ti"), + Atoms::V => write!(f, "V"), + Atoms::Cr => write!(f, "Cr"), + Atoms::Mn => write!(f, "Mn"), + Atoms::Fe => write!(f, "Fe"), + Atoms::Co => write!(f, "Co"), + Atoms::Ni => write!(f, "Ni"), + Atoms::Cu => write!(f, "Cu"), + Atoms::Zn => write!(f, "Zn"), + Atoms::Ga => write!(f, "Ga"), + Atoms::Ge => write!(f, "Ge"), + Atoms::As => write!(f, "As"), + Atoms::Se => write!(f, "Se"), + Atoms::Br => write!(f, "Br"), + Atoms::Kr => write!(f, "Kr"), + Atoms::Rb => write!(f, "Rb"), + Atoms::Sr => write!(f, "Sr"), + Atoms::Y => write!(f, "Y"), + Atoms::Zr => write!(f, "Zr"), + Atoms::Nb => write!(f, "Nb"), + Atoms::Mo => write!(f, "Mo"), + Atoms::Tc => write!(f, "Tc"), + Atoms::Ru => write!(f, "Ru"), + Atoms::Rh => write!(f, "Rh"), + Atoms::Pd => write!(f, "Pd"), + Atoms::Ag => write!(f, "Ag"), + Atoms::Cd => write!(f, "Cd"), + Atoms::In => write!(f, "In"), + Atoms::Sn => write!(f, "Sn"), + Atoms::Sb => write!(f, "Sb"), + Atoms::Te => write!(f, "Te"), + Atoms::I => write!(f, "I"), + Atoms::Xe => write!(f, "Xe"), + Atoms::Cs => write!(f, "Cs"), + Atoms::Ba => write!(f, "Ba"), + Atoms::La => write!(f, "La"), + Atoms::Ce => write!(f, "Ce"), + Atoms::Pr => write!(f, "Pr"), + Atoms::Nd => write!(f, "Nd"), + Atoms::Pm => write!(f, "Pm"), + Atoms::Sm => write!(f, "Sm"), + Atoms::Eu => write!(f, "Eu"), + Atoms::Gd => write!(f, "Gd"), + Atoms::Tb => write!(f, "Tb"), + Atoms::Dy => write!(f, "Dy"), + Atoms::Ho => write!(f, "Ho"), + Atoms::Er => write!(f, "Er"), + Atoms::Tm => write!(f, "Tm"), + Atoms::Yb => write!(f, "Yb"), + Atoms::Lu => write!(f, "Lu"), + Atoms::Hf => write!(f, "Hf"), + Atoms::Ta => write!(f, "Ta"), + Atoms::W => write!(f, "W"), + Atoms::Re => write!(f, "Re"), + Atoms::Os => write!(f, "Os"), + Atoms::Ir => write!(f, "Ir"), + Atoms::Pt => write!(f, "Pt"), + Atoms::Au => write!(f, "Au"), + Atoms::Hg => write!(f, "Hg"), + Atoms::Tl => write!(f, "Tl"), + Atoms::Pb => write!(f, "Pb"), + Atoms::Bi => write!(f, "Bi"), + Atoms::Po => write!(f, "Po"), + Atoms::At => write!(f, "At"), + Atoms::Rn => write!(f, "Rn"), + Atoms::Fr => write!(f, "Fr"), + Atoms::Ra => write!(f, "Ra"), + Atoms::Ac => write!(f, "Ac"), + Atoms::Th => write!(f, "Th"), + Atoms::Pa => write!(f, "Pa"), + Atoms::U => write!(f, "U"), + Atoms::Np => write!(f, "Np"), + Atoms::Pu => write!(f, "Pu"), + Atoms::Am => write!(f, "Am"), + Atoms::Cm => write!(f, "Cm"), + Atoms::Bk => write!(f, "Bk"), + Atoms::Cf => write!(f, "Cf"), + Atoms::Es => write!(f, "Es"), + Atoms::Fm => write!(f, "Fm"), + Atoms::Md => write!(f, "Md"), + Atoms::No => write!(f, "No"), + Atoms::Lr => write!(f, "Lr"), + Atoms::Rf => write!(f, "Rf"), + Atoms::Db => write!(f, "Db"), + Atoms::Sg => write!(f, "Sg"), + Atoms::Bh => write!(f, "Bh"), + Atoms::Hs => write!(f, "Hs"), + Atoms::Mt => write!(f, "Mt"), + Atoms::Ds => write!(f, "Ds"), + Atoms::Rg => write!(f, "Rg"), + Atoms::Cn => write!(f, "Cn"), + Atoms::Nh => write!(f, "Nh"), + Atoms::Fl => write!(f, "Fl"), + Atoms::Mc => write!(f, "Mc"), + Atoms::Lv => write!(f, "Lv"), + Atoms::Ts => write!(f, "Ts"), + Atoms::Og => write!(f, "Og"), + } + } +} + +impl<'a> TryFrom<&'a str> for Atoms { + type Error = String; + + fn try_from(s: &'a str) -> Result { + match s { + "H" => Ok(Atoms::H), + "He" => Ok(Atoms::He), + "Li" => Ok(Atoms::Li), + "Be" => Ok(Atoms::Be), + "B" => Ok(Atoms::B), + "C" => Ok(Atoms::C), + "N" => Ok(Atoms::N), + "O" => Ok(Atoms::O), + "F" => Ok(Atoms::F), + "Ne" => Ok(Atoms::Ne), + "Na" => Ok(Atoms::Na), + "Mg" => Ok(Atoms::Mg), + "Al" => Ok(Atoms::Al), + "Si" => Ok(Atoms::Si), + "P" => Ok(Atoms::P), + "S" => Ok(Atoms::S), + "Cl" => Ok(Atoms::Cl), + "Ar" => Ok(Atoms::Ar), + "K" => Ok(Atoms::K), + "Ca" => Ok(Atoms::Ca), + "Sc" => Ok(Atoms::Sc), + "Ti" => Ok(Atoms::Ti), + "V" => Ok(Atoms::V), + "Cr" => Ok(Atoms::Cr), + "Mn" => Ok(Atoms::Mn), + "Fe" => Ok(Atoms::Fe), + "Co" => Ok(Atoms::Co), + "Ni" => Ok(Atoms::Ni), + "Cu" => Ok(Atoms::Cu), + "Zn" => Ok(Atoms::Zn), + "Ga" => Ok(Atoms::Ga), + "Ge" => Ok(Atoms::Ge), + "As" => Ok(Atoms::As), + "Se" => Ok(Atoms::Se), + "Br" => Ok(Atoms::Br), + "Kr" => Ok(Atoms::Kr), + "Rb" => Ok(Atoms::Rb), + "Sr" => Ok(Atoms::Sr), + "Y" => Ok(Atoms::Y), + "Zr" => Ok(Atoms::Zr), + "Nb" => Ok(Atoms::Nb), + "Mo" => Ok(Atoms::Mo), + "Tc" => Ok(Atoms::Tc), + "Ru" => Ok(Atoms::Ru), + "Rh" => Ok(Atoms::Rh), + "Pd" => Ok(Atoms::Pd), + "Ag" => Ok(Atoms::Ag), + "Cd" => Ok(Atoms::Cd), + "In" => Ok(Atoms::In), + "Sn" => Ok(Atoms::Sn), + "Sb" => Ok(Atoms::Sb), + "Te" => Ok(Atoms::Te), + "I" => Ok(Atoms::I), + "Xe" => Ok(Atoms::Xe), + "Cs" => Ok(Atoms::Cs), + "Ba" => Ok(Atoms::Ba), + "La" => Ok(Atoms::La), + "Ce" => Ok(Atoms::Ce), + "Pr" => Ok(Atoms::Pr), + "Nd" => Ok(Atoms::Nd), + "Pm" => Ok(Atoms::Pm), + "Sm" => Ok(Atoms::Sm), + "Eu" => Ok(Atoms::Eu), + "Gd" => Ok(Atoms::Gd), + "Tb" => Ok(Atoms::Tb), + "Dy" => Ok(Atoms::Dy), + "Ho" => Ok(Atoms::Ho), + "Er" => Ok(Atoms::Er), + "Tm" => Ok(Atoms::Tm), + "Yb" => Ok(Atoms::Yb), + "Lu" => Ok(Atoms::Lu), + "Hf" => Ok(Atoms::Hf), + "Ta" => Ok(Atoms::Ta), + "W" => Ok(Atoms::W), + "Re" => Ok(Atoms::Re), + "Os" => Ok(Atoms::Os), + "Ir" => Ok(Atoms::Ir), + "Pt" => Ok(Atoms::Pt), + "Au" => Ok(Atoms::Au), + "Hg" => Ok(Atoms::Hg), + "Tl" => Ok(Atoms::Tl), + "Pb" => Ok(Atoms::Pb), + "Bi" => Ok(Atoms::Bi), + "Po" => Ok(Atoms::Po), + "At" => Ok(Atoms::At), + "Rn" => Ok(Atoms::Rn), + "Fr" => Ok(Atoms::Fr), + "Ra" => Ok(Atoms::Ra), + "Ac" => Ok(Atoms::Ac), + "Th" => Ok(Atoms::Th), + "Pa" => Ok(Atoms::Pa), + "U" => Ok(Atoms::U), + "Np" => Ok(Atoms::Np), + "Pu" => Ok(Atoms::Pu), + "Am" => Ok(Atoms::Am), + "Cm" => Ok(Atoms::Cm), + "Bk" => Ok(Atoms::Bk), + "Cf" => Ok(Atoms::Cf), + "Es" => Ok(Atoms::Es), + "Fm" => Ok(Atoms::Fm), + "Md" => Ok(Atoms::Md), + "No" => Ok(Atoms::No), + "Lr" => Ok(Atoms::Lr), + "Rf" => Ok(Atoms::Rf), + "Db" => Ok(Atoms::Db), + "Sg" => Ok(Atoms::Sg), + "Bh" => Ok(Atoms::Bh), + "Hs" => Ok(Atoms::Hs), + "Mt" => Ok(Atoms::Mt), + "Ds" => Ok(Atoms::Ds), + "Rg" => Ok(Atoms::Rg), + "Cn" => Ok(Atoms::Cn), + "Nh" => Ok(Atoms::Nh), + "Fl" => Ok(Atoms::Fl), + "Mc" => Ok(Atoms::Mc), + "Lv" => Ok(Atoms::Lv), + "Ts" => Ok(Atoms::Ts), + "Og" => Ok(Atoms::Og), + _ => Err(format!("Unknown atom: {}", s)), + } + } +} + +impl TryFrom for Atoms { + type Error = String; + + fn try_from(s: String) -> Result { + Atoms::try_from(s.as_str()) + } +} + +/// Create a vector of atoms +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] +pub struct AtomVector(Vec); + +impl Display for AtomVector { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut atoms = self.0.iter(); + if let Some(atom) = atoms.next() { + write!(f, "{}", atom)?; + for atom in atoms { + write!(f, ",{}", atom)?; + } + } + Ok(()) + } +} + +/// Implement the `new` method for AtomVector +impl From> for AtomVector { + /// Create a new AtomVector from a vector of Atoms + fn from(atoms: Vec) -> Self { + AtomVector(atoms) + } +} + +impl<'a> TryFrom<&'a str> for AtomVector { + type Error = String; + + fn try_from(s: &'a str) -> Result { + let atoms = s + .split(',') + .map(|atom| { + Atoms::try_from(atom).map_err(|e| { + format!( + "Cannot parse atom: {} ({}). Maybe forgot to put a comma between atoms ?", + atom, e + ) + }) + }) + .collect::, _>>()?; + Ok(AtomVector(atoms)) + } +} + +impl TryFrom for AtomVector { + type Error = String; + + fn try_from(s: String) -> Result { + AtomVector::try_from(s.as_str()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_atoms() { + assert_eq!(Atoms::try_from("H").unwrap(), Atoms::H); + assert_eq!(Atoms::try_from("He").unwrap(), Atoms::He); + assert_eq!(Atoms::try_from("Li").unwrap(), Atoms::Li); + assert_eq!(Atoms::try_from("Be").unwrap(), Atoms::Be); + assert_eq!(Atoms::try_from("B").unwrap(), Atoms::B); + assert_eq!(Atoms::try_from("C").unwrap(), Atoms::C); + assert_eq!(Atoms::try_from("N").unwrap(), Atoms::N); + assert_eq!(Atoms::try_from("O").unwrap(), Atoms::O); + assert_eq!(Atoms::try_from("F").unwrap(), Atoms::F); + assert_eq!(Atoms::try_from("Ne").unwrap(), Atoms::Ne); + assert_eq!(Atoms::try_from("Na").unwrap(), Atoms::Na); + assert_eq!(Atoms::try_from("Mg").unwrap(), Atoms::Mg); + assert_eq!(Atoms::try_from("Al").unwrap(), Atoms::Al); + assert_eq!(Atoms::try_from("Si").unwrap(), Atoms::Si); + assert_eq!(Atoms::try_from("P").unwrap(), Atoms::P); + assert_eq!(Atoms::try_from("S").unwrap(), Atoms::S); + assert_eq!(Atoms::try_from("Cl").unwrap(), Atoms::Cl); + assert_eq!(Atoms::try_from("Ar").unwrap(), Atoms::Ar); + assert_eq!(Atoms::try_from("K").unwrap(), Atoms::K); + assert_eq!(Atoms::try_from("Ca").unwrap(), Atoms::Ca); + assert_eq!(Atoms::try_from("Sc").unwrap(), Atoms::Sc); + assert_eq!(Atoms::try_from("Ti").unwrap(), Atoms::Ti); + assert_eq!(Atoms::try_from("V").unwrap(), Atoms::V); + assert_eq!(Atoms::try_from("Cr").unwrap(), Atoms::Cr); + assert_eq!(Atoms::try_from("Mn").unwrap(), Atoms::Mn); + assert_eq!(Atoms::try_from("Fe").unwrap(), Atoms::Fe); + } + #[test] + #[should_panic] + fn test_from_string_fail() { + assert_eq!( + AtomVector::try_from("H He Li Be B C N O F Ne").unwrap(), + AtomVector::from(vec![ + Atoms::H, + Atoms::He, + Atoms::Li, + Atoms::Be, + Atoms::B, + Atoms::C, + Atoms::N, + Atoms::O, + Atoms::F, + Atoms::Ne + ]) + ); + } + #[test] + fn test_from_string() { + assert_eq!( + AtomVector::try_from("H,He,Li,Be,B,C,N,O,F,Ne").unwrap(), + AtomVector::from(vec![ + Atoms::H, + Atoms::He, + Atoms::Li, + Atoms::Be, + Atoms::B, + Atoms::C, + Atoms::N, + Atoms::O, + Atoms::F, + Atoms::Ne + ]) + ); + assert_eq!( + AtomVector::try_from("N").unwrap(), + AtomVector::from(vec![Atoms::N]) + ); + assert_eq!( + AtomVector::try_from("N,O").unwrap(), + AtomVector::from(vec![Atoms::N, Atoms::O]) + ); + } + #[test] + fn test_display() { + println!("{}", AtomVector::from(vec![Atoms::N])); + assert_eq!( + AtomVector::from(vec![Atoms::N, Atoms::O, Atoms::P]).to_string(), + "N,O,P" + ); + } +} diff --git a/bindings/sirius-bindings/src/sirius_types/candidate_formulas.rs b/bindings/sirius-bindings/src/sirius_types/candidate_formulas.rs new file mode 100644 index 00000000..a14bd2cb --- /dev/null +++ b/bindings/sirius-bindings/src/sirius_types/candidate_formulas.rs @@ -0,0 +1,37 @@ +use std::fmt::Display; + +/// The possible candidate formulas +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Copy)] +pub enum CandidateFormulas { + /// The default candidate formulas + #[default] + Comma, +} + +impl Display for CandidateFormulas { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + CandidateFormulas::Comma => write!(f, ","), + } + } +} + +impl<'a> TryFrom<&'a str> for CandidateFormulas { + type Error = String; + + fn try_from(s: &'a str) -> Result { + match s { + "," => Ok(CandidateFormulas::Comma), + _ => Err(format!("Unknown adduct settings enforced: {}", s)), + } + } +} + +impl TryFrom for CandidateFormulas { + type Error = String; + + fn try_from(s: String) -> Result { + CandidateFormulas::try_from(s.as_str()) + } +} diff --git a/bindings/sirius-bindings/src/sirius_types/compound_quality.rs b/bindings/sirius-bindings/src/sirius_types/compound_quality.rs new file mode 100644 index 00000000..5df45395 --- /dev/null +++ b/bindings/sirius-bindings/src/sirius_types/compound_quality.rs @@ -0,0 +1,72 @@ +use std::fmt::Display; + +/// The possible compound qualities if known +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] +pub enum CompoundQuality { + /// The default compound quality: unknown + #[default] + Unknown, + + /// Is good + Good, + + /// Is low intensity + LowIntensity, + + /// Has no MS1 peak + NoMS1Peak, + + /// Has few peaks + FewPeaks, + + /// Is chimeric + Chimeric, + + /// Is not a monoisotopic peak + NotMonoisotopicPeak, + + /// Is poorly explained + PoorlyExplained, +} + +impl Display for CompoundQuality { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + CompoundQuality::Unknown => write!(f, "UNKNOWN"), + CompoundQuality::Good => write!(f, "Good"), + CompoundQuality::LowIntensity => write!(f, "LowIntensity"), + CompoundQuality::NoMS1Peak => write!(f, "NoMS1Peak"), + CompoundQuality::FewPeaks => write!(f, "FewPeaks"), + CompoundQuality::Chimeric => write!(f, "Chimeric"), + CompoundQuality::NotMonoisotopicPeak => write!(f, "NotMonoisotopicPeak"), + CompoundQuality::PoorlyExplained => write!(f, "PoorlyExplained"), + } + } +} + +impl<'a> TryFrom<&'a str> for CompoundQuality { + type Error = String; + + fn try_from(s: &'a str) -> Result { + match s { + "UNKNOWN" => Ok(CompoundQuality::Unknown), + "Good" => Ok(CompoundQuality::Good), + "LowIntensity" => Ok(CompoundQuality::LowIntensity), + "NoMS1Peak" => Ok(CompoundQuality::NoMS1Peak), + "FewPeaks" => Ok(CompoundQuality::FewPeaks), + "Chimeric" => Ok(CompoundQuality::Chimeric), + "NotMonoisotopicPeak" => Ok(CompoundQuality::NotMonoisotopicPeak), + "PoorlyExplained" => Ok(CompoundQuality::PoorlyExplained), + _ => Err(format!("Unknown compound quality: {}", s)), + } + } +} + +impl TryFrom for CompoundQuality { + type Error = String; + + fn try_from(s: String) -> Result { + CompoundQuality::try_from(s.as_str()) + } +} diff --git a/bindings/sirius-bindings/src/sirius_types/forbid_recalibration.rs b/bindings/sirius-bindings/src/sirius_types/forbid_recalibration.rs new file mode 100644 index 00000000..cf5facef --- /dev/null +++ b/bindings/sirius-bindings/src/sirius_types/forbid_recalibration.rs @@ -0,0 +1,42 @@ +use std::fmt::Display; + +/// Allow or forbid recalibration +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Copy)] +pub enum ForbidRecalibration { + /// The default recalibration setting : ALLOWED + #[default] + Allowed, + + /// Forbid recalibration + Forbidden, +} + +impl Display for ForbidRecalibration { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ForbidRecalibration::Allowed => write!(f, "ALLOWED"), + ForbidRecalibration::Forbidden => write!(f, "FORBIDDEN"), + } + } +} + +impl<'a> TryFrom<&'a str> for ForbidRecalibration { + type Error = String; + + fn try_from(s: &'a str) -> Result { + match s { + "ALLOWED" => Ok(ForbidRecalibration::Allowed), + "FORBIDDEN" => Ok(ForbidRecalibration::Forbidden), + _ => Err(format!("Unknown value: {}", s)), + } + } +} + +impl TryFrom for ForbidRecalibration { + type Error = String; + + fn try_from(s: String) -> Result { + ForbidRecalibration::try_from(s.as_str()) + } +} diff --git a/bindings/sirius-bindings/src/sirius_types/formula_result_ranking_score.rs b/bindings/sirius-bindings/src/sirius_types/formula_result_ranking_score.rs new file mode 100644 index 00000000..7b851968 --- /dev/null +++ b/bindings/sirius-bindings/src/sirius_types/formula_result_ranking_score.rs @@ -0,0 +1,37 @@ +use std::fmt::Display; + +/// The possible formula result ranking score +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Copy)] +pub enum FormulaResultRankingScore { + /// The default formula result ranking score + #[default] + Auto, +} + +impl Display for FormulaResultRankingScore { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + FormulaResultRankingScore::Auto => write!(f, "AUTO"), + } + } +} + +impl<'a> TryFrom<&'a str> for FormulaResultRankingScore { + type Error = String; + + fn try_from(s: &'a str) -> Result { + match s { + "AUTO" => Ok(FormulaResultRankingScore::Auto), + _ => Err(format!("Unknown adduct settings enforced: {}", s)), + } + } +} + +impl TryFrom for FormulaResultRankingScore { + type Error = String; + + fn try_from(s: String) -> Result { + FormulaResultRankingScore::try_from(s.as_str()) + } +} diff --git a/bindings/sirius-bindings/src/sirius_types/instruments.rs b/bindings/sirius-bindings/src/sirius_types/instruments.rs new file mode 100644 index 00000000..ef463830 --- /dev/null +++ b/bindings/sirius-bindings/src/sirius_types/instruments.rs @@ -0,0 +1,52 @@ +use std::fmt::Display; + +/// The possible instruments for the mass spectrometry +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] +pub enum Instruments { + /// The default instrument + #[default] + Default, + + /// The qtof instrument + Qtof, + + /// The orbitrap instrument + Orbitrap, + + /// The fticr instrument + Fticr, +} + +impl Display for Instruments { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Instruments::Default => write!(f, "default"), + Instruments::Qtof => write!(f, "qtof"), + Instruments::Orbitrap => write!(f, "orbitrap"), + Instruments::Fticr => write!(f, "fticr"), + } + } +} + +impl<'a> TryFrom<&'a str> for Instruments { + type Error = String; + + fn try_from(s: &'a str) -> Result { + match s { + "default" => Ok(Instruments::Default), + "qtof" => Ok(Instruments::Qtof), + "orbitrap" => Ok(Instruments::Orbitrap), + "fticr" => Ok(Instruments::Fticr), + _ => Err(format!("Unknown instrument: {}", s)), + } + } +} + +impl TryFrom for Instruments { + type Error = String; + + fn try_from(s: String) -> Result { + Instruments::try_from(s.as_str()) + } +} diff --git a/bindings/sirius-bindings/src/sirius_types/isotope_ms2_settings.rs b/bindings/sirius-bindings/src/sirius_types/isotope_ms2_settings.rs new file mode 100644 index 00000000..5b7340a2 --- /dev/null +++ b/bindings/sirius-bindings/src/sirius_types/isotope_ms2_settings.rs @@ -0,0 +1,37 @@ +use std::fmt::Display; + +/// The possible isotope MS2 settings +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Copy)] +pub enum IsotopeMS2Settings { + /// Ignore the isotope MS2 settings (default) + #[default] + Ignore, +} + +impl Display for IsotopeMS2Settings { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + IsotopeMS2Settings::Ignore => write!(f, "IGNORE"), + } + } +} + +impl<'a> TryFrom<&'a str> for IsotopeMS2Settings { + type Error = String; + + fn try_from(s: &'a str) -> Result { + match s { + "IGNORE" => Ok(IsotopeMS2Settings::Ignore), + _ => Err(format!("Unknown adduct settings enforced: {}", s)), + } + } +} + +impl TryFrom for IsotopeMS2Settings { + type Error = String; + + fn try_from(s: String) -> Result { + IsotopeMS2Settings::try_from(s.as_str()) + } +} diff --git a/bindings/sirius-bindings/src/sirius_types/mass_deviation.rs b/bindings/sirius-bindings/src/sirius_types/mass_deviation.rs new file mode 100644 index 00000000..0e1ae447 --- /dev/null +++ b/bindings/sirius-bindings/src/sirius_types/mass_deviation.rs @@ -0,0 +1,169 @@ +use std::fmt::Display; + +/// The possible mass deviations +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum MassDeviation { + /// The mass deviation in ppm + Ppm(f32), + + /// The mass deviation in Da + Da(f32), +} + +impl MassDeviation { + /// Create a new mass deviation in ppm + /// # Panics + /// If the value is negative + /// # Example + /// ``` + /// use sirius::prelude::*; + /// let ppm = MassDeviation::ppm(10.0); + /// ``` + /// # Panics + /// If the value is negative + /// ```should_panic + /// use sirius::prelude::*; + /// let ppm = MassDeviation::ppm(-10.0); + /// ``` + pub fn ppm(value: f32) -> Self { + // ppm can't be negative + if value < 0.0 { + panic!("ppm value can't be negative"); + } + MassDeviation::Ppm(value) + } + + /// Create a new mass deviation in Da + /// # Panics + /// If the value is negative + /// # Example + /// ``` + /// use sirius::prelude::*; + /// let da = MassDeviation::da(0.1); + /// ``` + /// # Panics + /// If the value is negative + /// ```should_panic + /// use sirius::prelude::*; + /// let x = MassDeviation::da(-0.1); + /// ``` + pub fn da(value: f32) -> Self { + // Da can't be negative + if value < 0.0 { + panic!("Da value can't be negative"); + } + MassDeviation::Da(value) + } + + /// Check if the value is positive + /// # Errors + /// If the value is negative + /// # Example + /// ``` + /// use sirius::prelude::*; + /// let ppm = MassDeviation::ppm(10.0); + /// assert_eq!(ppm.must_be_positive().unwrap(), MassDeviation::Ppm(10.0)); + /// ``` + /// # Errors + /// If the value is negative + /// ``` + /// use sirius::prelude::*; + /// let ppm = MassDeviation::Ppm(-10.0); + /// assert!(ppm.must_be_positive().is_err()); + /// ``` + pub fn must_be_positive(&self) -> Result { + match self { + MassDeviation::Ppm(value) => { + if *value < 0.0 { + Err("ppm value can't be negative".to_string()) + } else { + Ok(*self) + } + } + MassDeviation::Da(value) => { + if *value < 0.0 { + Err("Da value can't be negative".to_string()) + } else { + Ok(*self) + } + } + } + } +} + +impl Display for MassDeviation { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + MassDeviation::Ppm(value) => write!(f, "{} ppm", value), + MassDeviation::Da(value) => write!(f, "{} Da", value), + } + } +} + +impl<'a> TryFrom<&'a str> for MassDeviation { + type Error = String; + + fn try_from(s: &'a str) -> Result { + let mut split = s.split_whitespace(); + let value = split + .next() + .ok_or_else(|| "No value provided".to_string())?; + let unit = split.next().ok_or_else(|| "No unit provided".to_string())?; + + match unit { + "ppm" => Ok(MassDeviation::Ppm( + value + .parse() + .map_err(|e| format!("Cannot parse value: {}", e))?, + )), + "Da" => Ok(MassDeviation::Da( + value + .parse() + .map_err(|e| format!("Cannot parse value: {}", e))?, + )), + _ => Err(format!("Unknown unit: {}", unit)), + } + } +} + +impl TryFrom for MassDeviation { + type Error = String; + + fn try_from(s: String) -> Result { + MassDeviation::try_from(s.as_str()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_mass_deviation_display() { + let ppm = MassDeviation::Ppm(10.0); + assert_eq!(format!("{}", ppm), "10 ppm"); + + let da = MassDeviation::Da(0.1); + assert_eq!(format!("{}", da), "0.1 Da"); + } + #[test] + fn test_error_if_negative_value_with_funtion_call() { + let error = std::panic::catch_unwind(|| MassDeviation::ppm(-10.0)).unwrap_err(); + assert_eq!( + error.downcast_ref::<&str>(), + Some(&"ppm value can't be negative") + ); + + let error = std::panic::catch_unwind(|| MassDeviation::da(-0.1)).unwrap_err(); + assert_eq!( + error.downcast_ref::<&str>(), + Some(&"Da value can't be negative") + ); + } + + #[test] + #[should_panic] + fn test_error_if_negative_value_with_enum() { + let _ = MassDeviation::Ppm(-10.0).must_be_positive().unwrap(); + } +} diff --git a/bindings/sirius-bindings/src/sirius_types/mod.rs b/bindings/sirius-bindings/src/sirius_types/mod.rs new file mode 100644 index 00000000..e4686982 --- /dev/null +++ b/bindings/sirius-bindings/src/sirius_types/mod.rs @@ -0,0 +1,32 @@ +mod adduct_settings_enforced; +mod adducts; +mod atoms; +mod candidate_formulas; +mod compound_quality; +mod forbid_recalibration; +mod formula_result_ranking_score; +mod instruments; +mod isotope_ms2_settings; +mod mass_deviation; +mod noise_threshold_settings; +mod possible_adduct_switches; +mod search_db; +mod structure_predictors; + +pub use adduct_settings_enforced::AdductSettingsEnforced; +pub use adducts::Adducts; +pub use adducts::AdductsVector; +pub use atoms::AtomVector; +pub use atoms::Atoms; +pub use candidate_formulas::CandidateFormulas; +pub use compound_quality::CompoundQuality; +pub use forbid_recalibration::ForbidRecalibration; +pub use formula_result_ranking_score::FormulaResultRankingScore; +pub use instruments::Instruments; +pub use isotope_ms2_settings::IsotopeMS2Settings; +pub use mass_deviation::MassDeviation; +pub use noise_threshold_settings::BasePeak; +pub use possible_adduct_switches::PossibleAdductSwitches; +pub use search_db::DBVector; +pub use search_db::SearchDB; +pub use structure_predictors::StructurePredictors; diff --git a/bindings/sirius-bindings/src/sirius_types/noise_threshold_settings.rs b/bindings/sirius-bindings/src/sirius_types/noise_threshold_settings.rs new file mode 100644 index 00000000..13134323 --- /dev/null +++ b/bindings/sirius-bindings/src/sirius_types/noise_threshold_settings.rs @@ -0,0 +1,37 @@ +use std::fmt::Display; + +/// The noise threshold settings +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Copy)] +pub enum BasePeak { + /// Default noise threshold settings + #[default] + NotPrecursor, +} + +impl Display for BasePeak { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + BasePeak::NotPrecursor => write!(f, "NOT_PRECURSOR"), + } + } +} + +impl<'a> TryFrom<&'a str> for BasePeak { + type Error = String; + + fn try_from(s: &'a str) -> Result { + match s { + "NOT_PRECURSOR" => Ok(BasePeak::NotPrecursor), + _ => Err(format!("Unknown base peak: {}", s)), + } + } +} + +impl TryFrom for BasePeak { + type Error = String; + + fn try_from(s: String) -> Result { + BasePeak::try_from(s.as_str()) + } +} diff --git a/bindings/sirius-bindings/src/sirius_types/possible_adduct_switches.rs b/bindings/sirius-bindings/src/sirius_types/possible_adduct_switches.rs new file mode 100644 index 00000000..a2ba140b --- /dev/null +++ b/bindings/sirius-bindings/src/sirius_types/possible_adduct_switches.rs @@ -0,0 +1,41 @@ +use std::fmt::Display; + +/// The possible adduct switches. For now only the default adducts switches are supported. +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Copy)] +pub enum PossibleAdductSwitches { + /// The default adducts switches. + #[default] + DefaultAdductsSwitches, +} + +impl Display for PossibleAdductSwitches { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + PossibleAdductSwitches::DefaultAdductsSwitches => { + write!(f, "[M+Na]+:[M+H]+,[M+K]+:[M+H]+,[M+Cl]-:[M-H]-") + } + } + } +} + +impl<'a> TryFrom<&'a str> for PossibleAdductSwitches { + type Error = String; + + fn try_from(s: &'a str) -> Result { + match s { + "[M+Na]+:[M+H]+,[M+K]+:[M+H]+,[M+Cl]-:[M-H]-" => { + Ok(PossibleAdductSwitches::DefaultAdductsSwitches) + } + _ => Err(format!("Unknown adduct settings enforced: {}", s)), + } + } +} + +impl TryFrom for PossibleAdductSwitches { + type Error = String; + + fn try_from(s: String) -> Result { + PossibleAdductSwitches::try_from(s.as_str()) + } +} diff --git a/bindings/sirius-bindings/src/sirius_types/search_db.rs b/bindings/sirius-bindings/src/sirius_types/search_db.rs new file mode 100644 index 00000000..2da7c9de --- /dev/null +++ b/bindings/sirius-bindings/src/sirius_types/search_db.rs @@ -0,0 +1,193 @@ +use std::fmt::Display; + +/// The databases to search structures and formulas +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] +pub enum SearchDB { + /// No search db, the default + #[default] + None, + + /// The BIO search db + Bio, + + /// The METACYC search db + Metacyc, + + /// The CHEBI search db + Chebi, + + /// The COCONUT search db + Coconut, + + /// The ECOCYCMINE search db + Ecocycmine, + + /// The GNPS search db + Gnps, + + /// The HMDB search db + Hmdb, + + /// The HSDB search db + Hsdb, + + /// The KEGG search db + Kegg, + + /// The KEGGMINE search db + Keggmine, + + /// The KNAPSACK search db + Knapsack, + + /// The MACONDA search db + Maconda, + + /// The MESH search db + Mesh, + + /// The NORMAN search db + Norman, + + /// The UNDP search db + Undp, + + /// The PLANTCYC search db + Plantcyc, + + /// The PUBCHEM search db + Pubchem, + + /// The PUBMED search db + Pubmed, + + /// The YMDB search db + Ymdb, + + /// The YMDBMINE search db + Ymdbmine, + + /// The ZINCBIO search db + Zincbio, +} + +impl Display for SearchDB { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + SearchDB::None => write!(f, "none"), + SearchDB::Bio => write!(f, "BIO"), + SearchDB::Metacyc => write!(f, "METACYC"), + SearchDB::Chebi => write!(f, "CHEBI"), + SearchDB::Coconut => write!(f, "COCONUT"), + SearchDB::Ecocycmine => write!(f, "ECOCYCMINE"), + SearchDB::Gnps => write!(f, "GNPS"), + SearchDB::Hmdb => write!(f, "HMDB"), + SearchDB::Hsdb => write!(f, "HSDB"), + SearchDB::Kegg => write!(f, "KEGG"), + SearchDB::Keggmine => write!(f, "KEGGMINE"), + SearchDB::Knapsack => write!(f, "KNAPSACK"), + SearchDB::Maconda => write!(f, "MACONDA"), + SearchDB::Mesh => write!(f, "MESH"), + SearchDB::Norman => write!(f, "NORMAN"), + SearchDB::Undp => write!(f, "UNDP"), + SearchDB::Plantcyc => write!(f, "PLANTCYC"), + SearchDB::Pubchem => write!(f, "PUBCHEM"), + SearchDB::Pubmed => write!(f, "PUBMED"), + SearchDB::Ymdb => write!(f, "YMDB"), + SearchDB::Ymdbmine => write!(f, "YMDBMINE"), + SearchDB::Zincbio => write!(f, "ZINCBIO"), + } + } +} + +impl<'a> TryFrom<&'a str> for SearchDB { + type Error = String; + + fn try_from(s: &'a str) -> Result { + match s { + "none" => Ok(SearchDB::None), + "BIO" => Ok(SearchDB::Bio), + "METACYC" => Ok(SearchDB::Metacyc), + "CHEBI" => Ok(SearchDB::Chebi), + "COCONUT" => Ok(SearchDB::Coconut), + "ECOCYCMINE" => Ok(SearchDB::Ecocycmine), + "GNPS" => Ok(SearchDB::Gnps), + "HMDB" => Ok(SearchDB::Hmdb), + "HSDB" => Ok(SearchDB::Hsdb), + "KEGG" => Ok(SearchDB::Kegg), + "KEGGMINE" => Ok(SearchDB::Keggmine), + "KNAPSACK" => Ok(SearchDB::Knapsack), + "MACONDA" => Ok(SearchDB::Maconda), + "MESH" => Ok(SearchDB::Mesh), + "NORMAN" => Ok(SearchDB::Norman), + "UNDP" => Ok(SearchDB::Undp), + "PLANTCYC" => Ok(SearchDB::Plantcyc), + "PUBCHEM" => Ok(SearchDB::Pubchem), + "PUBMED" => Ok(SearchDB::Pubmed), + "YMDB" => Ok(SearchDB::Ymdb), + "YMDBMINE" => Ok(SearchDB::Ymdbmine), + "ZINCBIO" => Ok(SearchDB::Zincbio), + _ => Err(format!("Unknown formula search db: {}", s)), + } + } +} + +impl TryFrom for SearchDB { + type Error = String; + + fn try_from(s: String) -> Result { + SearchDB::try_from(s.as_str()) + } +} + +/// A vector of databases to search structures and formulas +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] +pub struct DBVector(Vec); + +impl Display for DBVector { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut atoms = self.0.iter(); + if let Some(atom) = atoms.next() { + write!(f, "{}", atom)?; + for atom in atoms { + write!(f, ",{}", atom)?; + } + } + Ok(()) + } +} + +impl From> for DBVector { + fn from(v: Vec) -> Self { + DBVector(v) + } +} + +impl<'a> TryFrom<&'a str> for DBVector { + type Error = String; + + fn try_from(s: &'a str) -> Result { + let v = s + .split(',') + .map(|db| { + SearchDB::try_from(db).map_err(|e| { + format!( + "Cannot parse database: {} ({}). Maybe forgot to put a comma between databases ?", + db, e + ) + }) + }) + .collect::, _>>()?; + Ok(DBVector(v)) + } +} + +impl TryFrom for DBVector { + type Error = String; + + fn try_from(s: String) -> Result { + DBVector::try_from(s.as_str()) + } +} diff --git a/bindings/sirius-bindings/src/sirius_types/structure_predictors.rs b/bindings/sirius-bindings/src/sirius_types/structure_predictors.rs new file mode 100644 index 00000000..c6b22b3c --- /dev/null +++ b/bindings/sirius-bindings/src/sirius_types/structure_predictors.rs @@ -0,0 +1,37 @@ +use std::fmt::Display; + +/// The structure predictors. For now, only CSI_FINGERID is supported. +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Copy)] +pub enum StructurePredictors { + #[default] + /// The CSI_FINGERID structure predictor + CsiFingerId, +} + +impl Display for StructurePredictors { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + StructurePredictors::CsiFingerId => write!(f, "CSI_FINGERID"), + } + } +} + +impl<'a> TryFrom<&'a str> for StructurePredictors { + type Error = String; + + fn try_from(s: &'a str) -> Result { + match s { + "CSI_FINGERID" => Ok(StructurePredictors::CsiFingerId), + _ => Err(format!("Unknown structure predictor: {}", s)), + } + } +} + +impl TryFrom for StructurePredictors { + type Error = String; + + fn try_from(s: String) -> Result { + StructurePredictors::try_from(s.as_str()) + } +} diff --git a/bindings/sirius-bindings/src/traits/enablable.rs b/bindings/sirius-bindings/src/traits/enablable.rs new file mode 100644 index 00000000..d0f89334 --- /dev/null +++ b/bindings/sirius-bindings/src/traits/enablable.rs @@ -0,0 +1,8 @@ +/// Trait for enablable objects +pub trait Enablable { + /// Returns true if the object is an enabler + fn is_enabler(&self) -> bool; + + /// Returns the enabler variant of the object + fn enabler() -> Self; +} diff --git a/bindings/sirius-bindings/src/traits/into_default.rs b/bindings/sirius-bindings/src/traits/into_default.rs new file mode 100644 index 00000000..22e24f96 --- /dev/null +++ b/bindings/sirius-bindings/src/traits/into_default.rs @@ -0,0 +1,5 @@ +/// A trait to convert a type into its default value, as defined +/// by the authors of the current crate. +pub trait IntoDefault { + fn into_default(self) -> Self; +} diff --git a/bindings/sirius-bindings/src/traits/mod.rs b/bindings/sirius-bindings/src/traits/mod.rs new file mode 100644 index 00000000..a14a4148 --- /dev/null +++ b/bindings/sirius-bindings/src/traits/mod.rs @@ -0,0 +1,8 @@ +//! Module providing traits for the Sirius bindings library. +mod enablable; +mod into_default; +mod named_parameters_set; + +pub use enablable::Enablable; +pub use into_default::IntoDefault; +pub use named_parameters_set::NamedParametersSet; diff --git a/bindings/sirius-bindings/src/traits/named_parameters_set.rs b/bindings/sirius-bindings/src/traits/named_parameters_set.rs new file mode 100644 index 00000000..b5bb091d --- /dev/null +++ b/bindings/sirius-bindings/src/traits/named_parameters_set.rs @@ -0,0 +1,4 @@ +/// Returns the name of the parameter set +pub trait NamedParametersSet { + fn parameter_set_name() -> &'static str; +} diff --git a/bindings/sirius-bindings/src/versions.rs b/bindings/sirius-bindings/src/versions.rs new file mode 100644 index 00000000..0989aa44 --- /dev/null +++ b/bindings/sirius-bindings/src/versions.rs @@ -0,0 +1,52 @@ +//! Trait and structs to define the different versions of Sirius +use std::fmt::Debug; + +use crate::traits::Enablable; +use crate::traits::IntoDefault; +use crate::traits::NamedParametersSet; + +/// Trait for the different versions of Sirius +pub trait Version: Default { + /// The version number + const VERSION: usize; + + /// The core parameters + type Core: ToString + Debug + IntoDefault; + + /// The config parameters + type Config: ToString + Debug + IntoDefault + NamedParametersSet + Enablable; + + /// The Canopus parameters + type Canopus: ToString + Debug + IntoDefault + NamedParametersSet + Enablable; + + /// The Formula parameters + type Formula: ToString + Debug + IntoDefault + NamedParametersSet + Enablable; + + /// The Zodiac parameters + type Zodiac: ToString + Debug + IntoDefault + NamedParametersSet + Enablable; + + /// The Fingerprint parameters + type Fingerprint: ToString + Debug + IntoDefault + NamedParametersSet + Enablable; + + /// The Structure parameters + type Structure: ToString + Debug + IntoDefault + NamedParametersSet + Enablable; + + /// Whether to write summaries + type WriteSummaries: ToString + Debug + IntoDefault + NamedParametersSet + Enablable; +} + +/// Implementation of the Sirius version 5 +#[derive(Default)] +pub struct Version5; + +impl Version for Version5 { + const VERSION: usize = 5; + type Core = crate::parameters::core::CoreV5; + type Config = crate::parameters::config::ConfigV5; + type Canopus = crate::parameters::canopus::CanopusV5; + type Formula = crate::parameters::formula::FormulaV5; + type Zodiac = crate::parameters::zodiac::ZodiacV5; + type Fingerprint = crate::parameters::fingerprint::FingerprintV5; + type Structure = crate::parameters::structure::StructureV5; + type WriteSummaries = crate::parameters::write_summaries::WriteSummariesV5; +} diff --git a/bindings/sirius-bindings/tests/data/input_sirius.mgf b/bindings/sirius-bindings/tests/data/input_sirius.mgf new file mode 100644 index 00000000..1fe73577 --- /dev/null +++ b/bindings/sirius-bindings/tests/data/input_sirius.mgf @@ -0,0 +1,1092 @@ +BEGIN IONS +FEATURE_ID=1 +PEPMASS=520.3401184082031 +CHARGE=1 +RTINSECONDS=450.62851800000004 +SPECTYPE=CORRELATED MS +MSLEVEL=1 +FILENAME=20221018_PMA_OA_AG_B135.mzML;20221018_PMA_OA_AG_B91.mzML;20221018_PMA_OA_AG_B5.mzML;20221018_PMA_OA_AG_V8.mzML +SCANS=-1 +520.3401184082031 1.5E9 +521.343017578125 4.4E8 +522.3461303710938 8.3E7 +523.347900390625 1.1E7 +END IONS + +BEGIN IONS +FEATURE_ID=1 +PEPMASS=520.3401184082031 +CHARGE=1 +RTINSECONDS=450.62851800000004 +MSLEVEL=2 +FILENAME=20221018_PMA_OA_AG_V8.mzML +SCANS=1 +MERGED_SCANS=4114 +MERGED_STATS=1 / 4 (3 removed due to low quality, 0 removed due to low cosine). +51.556331634521484 1.8E6 +55.05524826049805 1.1E6 +57.815757751464844 4.0E6 +58.06614685058594 2.5E6 +60.08164978027344 4.2E7 +60.508689880371094 1.1E6 +67.05503845214844 5.5E6 +69.07072448730469 2.3E6 +71.07373809814453 9.4E6 +79.05477142333984 2.5E6 +81.070556640625 5.1E6 +82.82237243652344 1.3E6 +83.08595275878906 1.4E6 +86.09703826904297 1.2E8 +87.95624542236328 1.2E6 +93.07064819335938 1.9E6 +94.95048522949219 1.2E6 +95.0859375 5.8E6 +96.22694396972656 1.1E6 +96.87842559814453 4.1E6 +98.61199951171875 1.1E6 +98.98477172851562 3.3E6 +104.09294128417969 2.0E6 +104.10742950439453 3.3E8 +104.11273956298828 7.3E6 +107.08605194091797 1.7E6 +109.1014633178711 2.1E6 +123.11724090576172 1.3E6 +124.99996948242188 5.9E7 +125.00931549072266 1.7E6 +137.55953979492188 1.3E6 +163.01519775390625 3.1E6 +184.03955078125 1.8E6 +184.07337951660156 3.3E8 +236.634033203125 1.2E6 +258.1107177734375 2.4E6 +337.2723083496094 2.6E6 +434.93133544921875 1.3E6 +438.89288330078125 1.3E6 +459.9193420410156 1.4E6 +502.32794189453125 1.3E7 +520.3394165039062 7.3E7 +END IONS + +BEGIN IONS +FEATURE_ID=2 +PEPMASS=265.1039123535156 +CHARGE=1 +RTINSECONDS=345.76646999999997 +SPECTYPE=CORRELATED MS +MSLEVEL=1 +FILENAME=20221018_PMA_OA_AG_B135.mzML;20221018_PMA_OA_AG_B91.mzML;20221018_PMA_OA_AG_B5.mzML;20221018_PMA_OA_AG_BK_MeOH.mzML;20221018_PMA_OA_AG_V8.mzML +SCANS=-1 +265.1039123535156 1.3E9 +266.1067199707031 1.6E8 +END IONS + +BEGIN IONS +FEATURE_ID=2 +PEPMASS=265.1039123535156 +CHARGE=1 +RTINSECONDS=345.76646999999997 +MSLEVEL=2 +FILENAME=20221018_PMA_OA_AG_B5.mzML;20221018_PMA_OA_AG_V8.mzML +SCANS=2 +MERGED_SCANS=3148,3197 +MERGED_STATS=2 / 4 (2 removed due to low quality, 0 removed due to low cosine). +53.24924850463867 4.8E4 +59.03312683105469 3.3E5 +60.93905258178711 4.7E4 +61.97976303100586 3.6E5 +69.65663146972656 3.5E5 +72.0368423461914 1.3E5 +72.04512393369323 4.9E7 +72.05311584472656 1.2E5 +72.89566040039062 3.8E5 +73.61070251464844 7.4E4 +96.88144193068415 1.8E6 +100.06218719482422 5.0E5 +100.06537628173828 3.8E5 +100.0761059345832 1.9E8 +100.08685302734375 4.4E5 +100.08971405029297 5.6E5 +100.09367370605469 2.2E5 +100.09712982177734 8.4E4 +106.16968536376953 5.0E5 +110.83744049072266 4.8E4 +121.22425842285156 3.5E5 +128.6090087890625 5.2E4 +156.61549377441406 3.7E5 +158.57980346679688 3.5E5 +161.81263732910156 3.7E5 +193.59625244140625 3.8E5 +195.59902954101562 3.4E5 +233.7845001220703 5.0E4 +243.5304412841797 4.9E4 +247.086669921875 5.9E4 +265.10339994544023 5.8E6 +265.1990661621094 1.6E5 +282.2235107421875 5.1E4 +END IONS + +BEGIN IONS +FEATURE_ID=3 +PEPMASS=496.3390808105469 +CHARGE=1 +RTINSECONDS=472.858992 +SPECTYPE=CORRELATED MS +MSLEVEL=1 +FILENAME=20221018_PMA_OA_AG_B135.mzML;20221018_PMA_OA_AG_B91.mzML;20221018_PMA_OA_AG_B5.mzML;20221018_PMA_OA_AG_V8.mzML +SCANS=-1 +496.3390808105469 9.5E8 +497.3422393798828 2.5E8 +498.3435363769531 4.6E7 +499.3481140136719 5.5E6 +500.35227966308594 6.0E5 +END IONS + +BEGIN IONS +FEATURE_ID=3 +PEPMASS=496.3390808105469 +CHARGE=1 +RTINSECONDS=472.858992 +MSLEVEL=2 +FILENAME=20221018_PMA_OA_AG_V8.mzML +SCANS=3 +MERGED_SCANS=4312 +MERGED_STATS=1 / 3 (2 removed due to low quality, 0 removed due to low cosine). +53.14780044555664 3.1E5 +55.054931640625 9.6E4 +55.146018981933594 4.7E5 +55.14904022216797 1.6E6 +56.0502815246582 2.0E5 +57.034427642822266 9.2E4 +57.070716857910156 1.2E6 +58.06562805175781 5.6E5 +58.94950866699219 9.6E4 +59.073822021484375 9.1E4 +60.081565856933594 7.2E6 +60.084861755371094 9.6E4 +67.05488586425781 2.3E5 +69.070556640625 1.0E5 +71.07357788085938 1.3E6 +71.08612060546875 6.8E5 +73.09951782226562 1.2E5 +77.59848022460938 7.3E4 +81.07049560546875 3.4E5 +83.08609771728516 1.8E5 +85.10186004638672 4.3E5 +86.09690856933594 2.1E7 +90.80677795410156 8.4E4 +95.08586883544922 5.7E5 +96.87696838378906 2.1E5 +96.88324737548828 1.6E5 +96.95369720458984 8.8E4 +97.10169219970703 1.5E5 +98.98442840576172 3.5E5 +104.09290313720703 4.3E5 +104.10724639892578 7.3E7 +104.11859130859375 2.7E5 +104.12175750732422 5.7E5 +109.10143280029297 3.2E5 +123.11669158935547 1.2E5 +124.999755859375 9.1E6 +163.01498413085938 4.9E5 +181.025146484375 1.7E5 +184.0391387939453 5.0E5 +184.04608154296875 4.9E5 +184.07308959960938 7.3E7 +184.10023498535156 3.9E5 +184.1070556640625 5.5E5 +184.1168212890625 1.8E5 +195.45582580566406 7.8E4 +199.03713989257812 9.8E4 +239.23655700683594 9.4E4 +240.09945678710938 1.2E5 +258.1099853515625 5.6E5 +313.2727355957031 1.4E6 +399.3384094238281 7.9E4 +419.2538757324219 3.4E5 +478.3282775878906 5.9E6 +478.3986511230469 1.9E5 +488.1447448730469 8.7E4 +496.3389892578125 3.1E7 +496.4853515625 1.7E5 +END IONS + +BEGIN IONS +FEATURE_ID=4 +PEPMASS=338.3423156738281 +CHARGE=1 +RTINSECONDS=646.28586 +SPECTYPE=CORRELATED MS +MSLEVEL=1 +FILENAME=20221018_PMA_OA_AG_B135.mzML;20221018_PMA_OA_AG_B91.mzML;20221018_PMA_OA_AG_B5.mzML;20221018_PMA_OA_AG_BK_MeOH.mzML;20221018_PMA_OA_AG_V8.mzML +SCANS=-1 +338.3423156738281 9.0E8 +339.34559631347656 2.2E8 +340.3486022949219 2.7E7 +341.3511962890625 2.3E6 +END IONS + +BEGIN IONS +FEATURE_ID=5 +PEPMASS=193.12222290039062 +CHARGE=1 +RTINSECONDS=363.430848 +SPECTYPE=CORRELATED MS +MSLEVEL=1 +FILENAME=20221018_PMA_OA_AG_B135.mzML;20221018_PMA_OA_AG_V8.mzML +SCANS=-1 +193.12222290039062 7.3E8 +194.12560272216797 9.6E7 +195.1283721923828 6.7E6 +196.13079071044922 5.4E5 +END IONS + +BEGIN IONS +FEATURE_ID=5 +PEPMASS=193.12222290039062 +CHARGE=1 +RTINSECONDS=363.430848 +MSLEVEL=2 +FILENAME=20221018_PMA_OA_AG_V8.mzML +SCANS=5 +MERGED_SCANS=3322 +MERGED_STATS=1 / 2 (1 removed due to low quality, 0 removed due to low cosine). +51.56062698364258 5.5E4 +53.039310455322266 7.3E4 +55.05482864379883 7.2E4 +56.66499328613281 4.3E4 +59.618621826171875 4.0E4 +67.05500793457031 2.0E5 +69.07048797607422 1.5E5 +71.04985809326172 9.2E4 +71.08635711669922 7.3E4 +73.77326965332031 5.4E4 +77.03924560546875 7.7E4 +79.05471801757812 4.4E5 +81.0704345703125 5.1E5 +83.0495376586914 8.2E4 +91.04874420166016 7.7E4 +91.05465698242188 2.5E6 +91.06063079833984 7.1E4 +91.75212860107422 4.7E4 +93.06424713134766 5.7E4 +93.07026672363281 2.5E6 +95.04950714111328 2.0E5 +95.0860824584961 8.9E4 +96.88138580322266 6.7E4 +105.07010650634766 1.5E6 +107.08568572998047 3.4E5 +109.06497955322266 3.3E5 +111.04412078857422 7.4E4 +117.07015228271484 7.8E4 +119.04906463623047 3.2E5 +119.08570861816406 6.5E5 +121.10108947753906 9.8E4 +123.04408264160156 2.7E5 +125.05909729003906 5.0E4 +129.06951904296875 1.5E5 +131.0853271484375 9.1E4 +133.06463623046875 2.3E5 +133.10079956054688 1.7E5 +135.1168212890625 6.0E4 +137.05950927734375 5.6E6 +145.10116577148438 9.7E4 +147.10415649414062 6.0E4 +147.11654663085938 4.1E6 +149.13250732421875 7.1E4 +151.0758819580078 6.9E4 +157.1010284423828 1.5E5 +165.12725830078125 8.0E4 +175.11148071289062 1.4E6 +175.1476593017578 6.2E4 +193.10414123535156 6.6E4 +193.1220245361328 1.3E6 +END IONS + +BEGIN IONS +FEATURE_ID=6 +PEPMASS=457.3996887207031 +CHARGE=1 +RTINSECONDS=442.275198 +SPECTYPE=CORRELATED MS +MSLEVEL=1 +FILENAME=20221018_PMA_OA_AG_B135.mzML;20221018_PMA_OA_AG_B91.mzML;20221018_PMA_OA_AG_B5.mzML;20221018_PMA_OA_AG_V8.mzML +SCANS=-1 +457.3996887207031 6.5E8 +458.4028015136719 1.9E8 +459.40574645996094 3.2E7 +460.4074249267578 4.0E6 +461.4125061035156 4.2E5 +END IONS + +BEGIN IONS +FEATURE_ID=6 +PEPMASS=457.3996887207031 +CHARGE=1 +RTINSECONDS=442.275198 +MSLEVEL=2 +FILENAME=20221018_PMA_OA_AG_B5.mzML;20221018_PMA_OA_AG_V8.mzML +SCANS=6 +MERGED_SCANS=4026,4060,4115 +MERGED_STATS=3 / 5 (2 removed due to low quality, 0 removed due to low cosine). +50.59883499145508 1.1E5 +50.81840133666992 6.0E4 +50.81987380981445 4.1E5 +50.82200464136102 4.6E6 +53.039493560791016 1.2E4 +53.99809265136719 1.2E4 +55.01887512207031 3.7E4 +55.05262756347656 2.1E4 +55.05522261339731 1.1E6 +57.03422546386719 1.2E4 +57.07080720692714 8.1E5 +57.073726654052734 1.4E4 +60.04534644450345 7.8E5 +60.08168488938473 6.7E6 +60.542274475097656 3.1E4 +63.23574447631836 1.1E5 +67.05510586886383 5.2E5 +68.05036926269531 5.9E4 +69.03438568115234 1.6E4 +69.0668716430664 1.8E4 +69.0706703417542 1.8E6 +69.07467651367188 1.6E4 +70.05789184570312 6.5E4 +70.06598724337712 2.0E7 +70.07406616210938 5.0E4 +70.51114654541016 1.1E5 +71.04187774658203 3.6E4 +71.04998799651067 8.6E6 +71.0863708139044 4.6E5 +72.08149718159389 4.2E5 +72.10182189941406 2.8E4 +73.06554412841797 3.1E4 +74.0202865600586 7.0E4 +74.02440651201884 4.4E6 +75.53913879394531 1.3E5 +77.41617584228516 1.1E4 +78.49652099609375 9.2E4 +79.05495876542524 1.9E5 +81.07063779365588 6.0E5 +83.04986572265625 2.2E4 +83.08620582595039 1.4E6 +84.08145141601562 2.0E4 +85.10169077827963 3.7E5 +85.97119903564453 1.0E5 +86.06067689258546 1.2E5 +88.03996849117969 3.5E5 +88.07626116081727 2.8E6 +92.62091064453125 9.5E4 +93.0705254156422 2.9E5 +94.06568908691406 2.7E4 +95.08611270256978 8.2E5 +95.09220123291016 1.4E4 +96.04500314781417 1.3E5 +96.08132934570312 5.8E4 +96.87691497802734 2.8E4 +96.88129265744445 4.1E5 +97.06544059583126 3.4E5 +97.10169143565308 1.3E6 +98.06058704533682 3.9E5 +99.0444322234466 4.9E5 +99.96923828125 3.2E4 +107.08597890177188 2.6E5 +109.10161876433098 3.8E5 +110.06070709228516 2.8E4 +111.08083279215217 9.7E4 +111.11717719993426 4.6E5 +112.07602712715564 3.7E6 +112.86222076416016 9.9E4 +114.0553753128694 1.1E6 +114.09177249735248 5.0E5 +114.34817461968342 1.1E5 +116.0346450805664 2.7E4 +116.0490493774414 2.4E4 +116.05399322509766 8.9E4 +116.05732727050781 5.8E4 +116.0623779296875 2.7E5 +116.0645980834961 1.7E5 +116.07092317031595 2.8E7 +116.08452606201172 8.9E4 +116.08798217773438 8.6E4 +116.09285736083984 3.1E4 +117.0550930412822 7.1E4 +121.10139098376804 7.3E5 +123.11705048913575 1.4E5 +124.63168334960938 1.1E5 +125.09658813476562 2.5E4 +125.13265866706902 8.6E4 +130.0865478515625 1.8E4 +130.12342834472656 1.6E4 +130.48243713378906 3.4E4 +134.08148193359375 1.9E4 +135.11707009880467 6.5E5 +137.13272094726562 2.1E4 +138.09142480596162 9.3E4 +138.17279052734375 3.1E4 +139.11241149902344 2.4E4 +140.07052241079123 1.7E5 +142.04993396548974 2.2E5 +149.13258822519634 1.7E5 +152.10885620117188 1.3E4 +153.12705993652344 2.0E4 +158.05450439453125 2.6E4 +158.0812591470102 8.9E6 +160.21556091308594 1.2E4 +163.148193359375 3.8E4 +165.0910186767578 3.7E4 +165.16366577148438 1.5E4 +166.02224731445312 1.3E4 +171.2499237060547 1.5E4 +175.14441780385184 3.4E5 +177.16366577148438 2.4E4 +179.1573486328125 1.1E5 +179.51011657714844 3.4E4 +184.07374572753906 2.5E4 +191.17971801757812 2.2E4 +217.15530395507812 9.0E4 +221.50875854492188 1.3E4 +227.22402954101562 1.2E5 +227.93316650390625 1.0E5 +245.05621337890625 3.4E4 +247.24209961819577 1.4E5 +265.0878601074219 1.2E5 +265.2530503215247 2.3E5 +282.2793769604872 5.5E5 +282.6676330566406 1.1E5 +292.26678466796875 2.0E4 +300.2899659044939 1.5E5 +308.2944641113281 2.2E4 +330.93280029296875 3.6E4 +334.3094760038545 4.8E5 +336.32672629811714 1.3E5 +352.3196105957031 3.9E4 +354.33536040683947 2.1E5 +362.3060607910156 3.7E4 +380.31590312701354 3.9E6 +398.2201232910156 2.2E4 +398.3264527088551 8.7E6 +403.94122314453125 3.4E4 +413.4101257324219 2.1E4 +439.390380859375 3.8E4 +457.0784606933594 2.0E4 +457.22967529296875 6.3E4 +457.266683296715 3.4E5 +457.29387196495486 2.4E5 +457.40016549020476 7.4E7 +457.50664707180846 3.2E5 +457.53353328794407 3.7E5 +457.5729675292969 6.9E4 +457.609375 3.5E4 +457.68524169921875 2.2E4 +478.4005126953125 1.1E5 +END IONS + +BEGIN IONS +FEATURE_ID=7 +PEPMASS=535.3995666503906 +CHARGE=2 +RTINSECONDS=614.49954 +SPECTYPE=CORRELATED MS +MSLEVEL=1 +FILENAME=20221018_PMA_OA_AG_B135.mzML;20221018_PMA_OA_AG_B91.mzML;20221018_PMA_OA_AG_B5.mzML;20221018_PMA_OA_AG_BK_MeOH.mzML;20221018_PMA_OA_AG_V8.mzML +SCANS=-1 +535.3995666503906 6.5E8 +535.9007873535156 3.8E8 +536.4026794433594 1.3E8 +536.90380859375 3.4E7 +537.4048461914062 7.8E6 +END IONS + +BEGIN IONS +FEATURE_ID=7 +PEPMASS=535.3995666503906 +CHARGE=2 +RTINSECONDS=614.49954 +MSLEVEL=2 +FILENAME=20221018_PMA_OA_AG_B135.mzML;20221018_PMA_OA_AG_B91.mzML;20221018_PMA_OA_AG_B5.mzML;20221018_PMA_OA_AG_V8.mzML +SCANS=7 +MERGED_SCANS=5699,5733,5644,5611,5574,5541,5456 +MERGED_STATS=7 / 7 (0 removed due to low quality, 0 removed due to low cosine). +74.03258514404297 9.2E3 +74.18396759033203 9.2E3 +76.71076202392578 1.6E5 +77.38215637207031 7.7E3 +78.55590057373047 9.1E3 +79.78794860839844 6.5E4 +80.0550136790191 6.0E6 +80.16835021972656 4.8E3 +80.55675649206611 2.8E4 +81.070584005092 3.8E4 +81.56903839111328 1.6E5 +82.27395629882812 5.6E3 +82.67939758300781 1.2E4 +83.0860907462069 2.8E6 +84.3614730834961 1.4E5 +85.10177510420147 8.6E6 +85.93215942382812 1.4E5 +86.10513305664062 1.1E4 +87.03902364626279 1.5E5 +87.04464803825694 9.2E6 +88.04788970947266 7.4E3 +89.04869079589844 3.0E4 +89.05419056887081 1.2E5 +89.06024739483041 7.4E7 +89.06938968473911 4.3E5 +89.07151794433594 3.1E4 +89.07807922363281 6.8E3 +90.06331765160189 1.2E6 +91.07581394111062 1.8E5 +92.51527025746486 2.1E4 +95.04969787597656 7.6E3 +95.08601172403498 2.8E4 +96.87570190429688 1.9E4 +96.88131192293837 4.3E6 +97.09561920166016 1.9E4 +97.10157901427151 3.6E6 +98.10509502951382 1.7E4 +99.11719832529978 9.6E5 +101.40930938720703 7.3E4 +102.06779093442893 5.6E6 +102.0747299194336 1.4E4 +102.56952620752537 5.2E4 +103.29576873779297 5.3E4 +107.07036886791784 1.1E5 +111.07296724795798 3.3E5 +111.11720366151314 1.6E6 +111.23039245605469 4.8E4 +113.0598911807861 4.9E5 +113.13270568847656 3.0E5 +115.07547251454383 1.3E5 +117.09116128691599 5.1E6 +117.10405731201172 6.4E3 +118.09468841552734 1.0E4 +123.11695098876953 6.5E3 +124.07170867919922 1.1E4 +124.08065728002381 2.8E6 +124.58226013183594 2.5E4 +125.13260622839202 3.1E5 +127.14805358939894 1.1E5 +128.04246520996094 1.1E4 +130.08588563287066 7.8E4 +131.07021037881472 1.7E6 +131.0791473388672 1.7E4 +133.08590866694206 4.0E7 +133.5879574482879 2.5E4 +134.08910264503518 1.2E6 +135.1014847851685 3.0E5 +135.4371795654297 4.4E4 +139.1472310348787 8.3E4 +141.02975463867188 4.5E4 +141.16302324476032 1.4E5 +142.09127807617188 1.2E4 +142.45306396484375 7.5E4 +145.1809539794922 2.5E5 +146.09335309679747 1.0E6 +146.5953826904297 6.7E3 +149.88922119140625 5.0E4 +151.0961710857887 2.0E5 +153.16270446777344 6.3E3 +155.08668518066406 4.4E3 +155.09881775457345 2.9E6 +155.17923347702688 8.8E4 +157.08619689941406 7.7E3 +159.1014424615206 3.1E4 +160.0112762451172 1.1E4 +161.11704548901483 9.4E5 +161.33563232421875 1.5E5 +164.10434156222743 4.7E4 +166.60690307617188 6.7E3 +166.68948364257812 1.9E5 +168.10688901020436 2.0E5 +169.19477242091082 2.4E4 +171.8499298095703 6.9E4 +175.09626310023845 1.1E6 +177.088623046875 7.1E4 +177.0953369140625 7.7E4 +177.11205758227064 1.5E7 +177.12802124023438 9.9E4 +177.6134934704318 2.1E5 +177.8236083984375 1.9E5 +178.115601239003 2.3E5 +178.99513244628906 7.1E4 +179.12736509140942 8.5E4 +179.76747131347656 1.0E4 +183.21128845214844 1.1E4 +185.6250135109598 1.4E5 +189.13368225097656 5.6E3 +189.1538543701172 5.6E3 +190.11949157714844 1.1E4 +194.13803100585938 9.3E3 +195.1228176487227 5.3E5 +197.7988739013672 4.6E4 +199.1248666274244 6.9E6 +199.62630747370272 2.7E5 +207.63864698697478 2.7E6 +208.13198852539062 9.3E3 +208.63458251953125 5.2E4 +215.36972045898438 2.0E5 +219.12271742140868 2.1E5 +221.1160888671875 2.0E4 +221.13816735650488 1.2E7 +221.16030451697907 1.9E5 +221.63964331827225 8.0E5 +222.14344787597656 6.8E3 +223.15440968892307 1.1E5 +225.25712968402155 4.8E5 +228.6436309814453 1.1E4 +229.651383676335 4.2E6 +229.6746368408203 7.6E3 +230.1532808054311 1.8E5 +239.14861814706597 6.6E5 +242.71754455566406 4.8E4 +243.15098674970156 4.7E6 +243.65252684221747 2.1E5 +244.5884552001953 1.5E4 +251.6643035470138 3.1E6 +252.1656986494269 3.7E5 +265.1382141113281 8.5E4 +265.1638898525611 3.2E6 +265.66729736328125 6.2E3 +267.17942961569565 1.1E5 +269.2836110077069 5.2E5 +273.5533752441406 6.2E4 +273.650634765625 1.0E4 +273.6774529883667 6.3E5 +274.1798095703125 7.2E3 +281.7222595214844 6.1E4 +283.174756798809 4.1E5 +287.1761803573094 1.3E5 +289.2365054856395 6.3E5 +295.68999668492074 3.3E5 +297.7494812011719 9.8E3 +309.1902564949718 1.9E6 +311.20641205383674 3.2E4 +311.24959640725393 4.9E5 +311.7528991699219 7.3E3 +313.31012092826126 3.8E6 +313.34454345703125 6.7E4 +314.31308227542206 1.6E5 +317.70359704545007 2.0E4 +318.19940185546875 1.2E4 +319.7629638428251 1.3E6 +320.2662731477423 9.4E4 +327.20026770064914 2.6E5 +328.5351257324219 1.2E4 +330.521728515625 4.6E4 +333.2640851382843 1.2E5 +340.2079162597656 1.4E4 +341.77614235802696 1.6E6 +342.2391052246094 5.5E3 +342.2782059467844 1.8E5 +353.21603461282285 1.7E6 +354.2222900390625 7.4E3 +355.0104064941406 7.4E4 +355.2342224121094 8.5E3 +357.3359141289104 2.4E6 +358.3404332627003 1.0E5 +362.2222689678068 8.2E5 +362.7232360839844 5.8E3 +362.8807373046875 2.5E5 +363.79004760987965 5.0E5 +364.28985595703125 5.5E4 +370.733642578125 6.0E3 +371.22552243003923 8.5E4 +375.34640555755254 1.6E5 +381.9954528808594 5.4E4 +382.7268981933594 7.2E4 +384.23553906647464 8.0E5 +384.7379486724474 2.9E4 +385.80289752298296 2.1E5 +392.74925205283523 2.8E4 +397.243041270454 8.3E5 +401.3613586425781 7.6E3 +406.19976806640625 4.9E4 +406.2480313717546 1.2E7 +406.74891848781095 2.7E6 +407.815673828125 1.4E4 +414.76099129275536 5.4E6 +414.8180847167969 8.1E3 +415.2619184833955 1.1E6 +419.3720881138154 3.1E5 +441.2703028357213 2.8E5 +447.11505126953125 1.5E5 +459.27663704661745 3.1E5 +461.8535461425781 5.9E3 +462.8170166015625 6.0E3 +463.4014817213983 2.1E5 +464.89910888671875 6.4E4 +476.3064880371094 1.2E4 +482.8610891154933 1.8E5 +483.36077880859375 6.1E3 +485.2926231800079 2.3E4 +503.30584639834103 1.2E6 +504.8746711996145 3.3E5 +505.3736629262787 9.3E4 +507.4276228001089 8.0E4 +518.3732531291841 2.1E6 +518.8751749166595 5.6E5 +520.3309543394363 2.7E5 +526.82421875 3.9E4 +526.8864611945565 1.4E7 +526.9647827148438 1.2E5 +527.31396484375 8.6E4 +527.3876369823386 5.5E6 +533.4325143865998 2.3E5 +535.3993974886979 1.7E6 +535.4716186523438 2.2E4 +535.9011212366596 3.4E5 +546.3611450195312 5.5E4 +547.3326596454106 1.5E6 +548.3386152692358 6.6E4 +564.3577770122791 8.4E5 +565.3566356734719 9.9E4 +577.4684550900587 3.0E4 +591.3577837218913 2.2E6 +592.366914576827 8.2E4 +595.47509765625 7.9E3 +608.3836970007435 1.9E6 +609.3876430795527 1.7E5 +611.622802734375 5.8E4 +621.4955835061538 7.2E4 +635.3835735008632 2.0E6 +636.3836434980822 8.0E4 +639.5070190429688 1.2E4 +652.4101780873468 2.1E6 +653.4148730885114 4.3E5 +679.4118421064076 1.6E6 +680.4140733831954 2.6E4 +688.61962890625 1.2E4 +696.4367431581286 2.0E6 +697.4424330258248 3.5E4 +700.5567042276274 3.4E5 +723.434638551043 9.0E5 +724.4431762695312 6.7E3 +727.5541381835938 1.2E4 +740.4594936071361 2.0E6 +741.4669762800439 1.0E5 +744.5792699723921 5.4E5 +745.5829467773438 9.2E3 +745.5994987140372 7.9E4 +764.08056640625 7.4E4 +767.4621251663692 2.1E5 +768.4713134765625 1.2E4 +771.5785966302084 2.3E5 +784.4891087815902 4.9E5 +785.4860636832225 7.7E4 +788.609053928669 8.1E5 +789.6100887478714 1.1E5 +791.0936889648438 2.4E5 +815.6087353095827 2.3E5 +832.6353977672323 1.4E6 +833.6353591776337 2.0E5 +859.6357497310318 4.3E4 +876.6621337682193 1.7E6 +877.6635329195981 2.5E5 +898.360595703125 1.3E4 +903.6527099609375 8.7E3 +920.682049722162 8.4E5 +921.6835272924646 8.4E4 +936.408203125 1.5E5 +1065.82275390625 1.3E4 +1067.5440673828125 5.8E4 +END IONS + +BEGIN IONS +FEATURE_ID=8 +PEPMASS=268.1039123535156 +CHARGE=1 +RTINSECONDS=60.54362400000001 +SPECTYPE=CORRELATED MS +MSLEVEL=1 +FILENAME=20221018_PMA_OA_AG_B135.mzML;20221018_PMA_OA_AG_B91.mzML;20221018_PMA_OA_AG_B5.mzML;20221018_PMA_OA_AG_V8.mzML +SCANS=-1 +268.1039123535156 6.3E8 +269.1072998046875 7.0E7 +270.1086120605469 8.3E6 +END IONS + +BEGIN IONS +FEATURE_ID=8 +PEPMASS=268.1039123535156 +CHARGE=1 +RTINSECONDS=60.54362400000001 +MSLEVEL=2 +FILENAME=20221018_PMA_OA_AG_V8.mzML +SCANS=8 +MERGED_SCANS=472 +MERGED_STATS=1 / 7 (6 removed due to low quality, 0 removed due to low cosine). +55.01865005493164 3.9E5 +57.0342903137207 6.4E5 +58.66444778442383 3.4E5 +78.20707702636719 3.5E5 +79.58879852294922 3.5E5 +84.48907470703125 3.9E5 +85.02881622314453 7.9E5 +106.10881805419922 3.9E5 +108.57849884033203 3.9E5 +115.0390396118164 5.5E5 +115.54519653320312 3.7E5 +119.03470611572266 5.1E5 +121.46626281738281 3.4E5 +133.04917907714844 4.3E5 +136.06153869628906 2.5E8 +136.0828094482422 1.3E6 +136.08901977539062 4.8E5 +215.68569946289062 4.2E5 +268.103271484375 1.9E7 +268.7911376953125 3.5E5 +END IONS + +BEGIN IONS +FEATURE_ID=9 +PEPMASS=309.08648681640625 +CHARGE=1 +RTINSECONDS=250.735482 +SPECTYPE=CORRELATED MS +MSLEVEL=1 +FILENAME=20221018_PMA_OA_AG_B135.mzML;20221018_PMA_OA_AG_B91.mzML;20221018_PMA_OA_AG_B5.mzML;20221018_PMA_OA_AG_V8.mzML +SCANS=-1 +309.08648681640625 6.1E8 +310.0898742675781 1.2E8 +311.09226989746094 1.5E7 +312.09486389160156 1.4E6 +END IONS + +BEGIN IONS +FEATURE_ID=9 +PEPMASS=309.08648681640625 +CHARGE=1 +RTINSECONDS=250.735482 +MSLEVEL=2 +FILENAME=20221018_PMA_OA_AG_B135.mzML;20221018_PMA_OA_AG_B91.mzML;20221018_PMA_OA_AG_B5.mzML;20221018_PMA_OA_AG_V8.mzML +SCANS=9 +MERGED_SCANS=2272,2302,2233,2285,2212 +MERGED_STATS=5 / 5 (0 removed due to low quality, 0 removed due to low cosine). +50.96147155761719 9.4E3 +51.5587158203125 6.4E4 +53.503692626953125 9.2E3 +54.38945770263672 4.3E4 +57.347930908203125 1.0E4 +58.066017150878906 5.4E3 +59.973331451416016 1.3E4 +62.21334457397461 1.3E4 +65.76956995564724 7.3E4 +67.05506455238495 4.3E4 +67.68319702148438 9.6E3 +69.070556640625 6.0E3 +71.04988148318441 3.3E4 +72.38601684570312 5.1E4 +72.38951873779297 2.6E4 +72.68909454345703 1.5E4 +72.93865966796875 5.6E3 +74.2210693359375 2.4E4 +75.0873031616211 5.0E4 +77.27052834611628 8.2E4 +77.2747802734375 4.5E3 +79.0547866821289 3.0E4 +81.0338747652948 6.2E4 +81.07034450136445 1.0E5 +83.049560546875 1.2E4 +84.06061553955078 2.6E4 +85.06516563958792 5.6E4 +90.9659652709961 2.6E4 +93.0702713504658 9.5E4 +95.04914093017578 2.4E4 +95.08573110356174 1.0E5 +96.87862315452934 3.5E5 +96.88407135009766 5.6E4 +97.06516541766317 2.2E4 +99.04427048694646 5.1E4 +99.08045959902203 2.4E4 +100.35990142822266 1.1E4 +101.05980682373047 1.6E4 +105.07016543484654 2.4E4 +107.0856475554628 1.1E5 +109.06476352107485 7.6E4 +109.10150771697981 8.3E4 +111.0806221550656 5.1E4 +111.91316223144531 5.1E4 +119.08560163405664 4.3E4 +121.10095050066202 6.2E4 +121.8470230102539 1.1E4 +122.32306671142578 1.3E4 +123.08058149783548 4.4E4 +123.11634063720703 6.9E3 +123.99116516113281 4.7E3 +125.09553189035896 6.6E4 +127.07519030719786 4.8E4 +127.11170196533203 1.3E4 +127.18811798095703 1.3E4 +133.10096874177646 2.1E4 +135.08016491218424 4.8E4 +137.09577002484866 9.9E4 +139.0757293701172 8.7E3 +145.1010088517867 6.0E4 +147.11650403971444 4.2E4 +149.09600452976187 2.3E4 +151.11198230420263 2.2E4 +157.10052490234375 6.4E3 +157.82504272460938 1.3E4 +159.11631774902344 1.8E4 +161.0962677001953 4.5E3 +163.11168227550442 2.9E4 +165.0908966064453 7.2E4 +166.43621826171875 5.9E3 +167.07235717773438 9.5E3 +173.13218688964844 1.7E4 +174.96934509277344 5.8E3 +174.98777770996094 5.0E3 +179.10536193847656 1.2E4 +179.72525024414062 4.6E4 +180.08042322153148 5.6E5 +185.0709991455078 7.3E3 +187.1121368408203 5.9E3 +190.06532287597656 1.6E4 +191.14216079832315 2.5E4 +192.06702298160596 4.3E4 +193.07493161557073 1.0E5 +197.1323699951172 1.5E4 +199.04953002929688 1.3E4 +205.07542186186888 5.9E5 +205.12184137393763 3.2E4 +206.05447387695312 1.8E4 +206.0691375732422 3.6E4 +206.08341384764032 4.6E6 +206.10350970055603 6.2E4 +207.07150877022875 2.0E4 +207.09130326786817 1.2E6 +207.1063995361328 3.5E3 +208.07495992212236 2.5E5 +208.2189483642578 5.4E3 +213.0662078857422 7.0E3 +217.07590583830572 3.8E5 +219.09230041503906 1.5E4 +219.59454345703125 5.7E3 +220.06065368652344 1.1E4 +221.0703348654059 5.1E5 +223.06188651179468 2.6E4 +232.062147194438 2.6E5 +233.0704229280906 2.0E5 +233.15155029296875 5.8E3 +234.07845451532123 5.9E5 +235.06199645996094 1.8E4 +235.06773718225202 1.5E4 +235.0861176114126 1.8E6 +235.11016845703125 1.4E4 +235.1529083251953 5.4E3 +236.06936223732242 9.6E4 +245.07033020711174 2.7E5 +249.14550259220164 3.0E4 +262.0713195800781 1.5E4 +262.99005126953125 8.9E3 +263.0052795410156 2.2E4 +263.0227913748522 1.5E5 +263.0350371334914 1.1E5 +263.08096988369823 4.7E7 +263.1276550292969 2.7E4 +263.1390080967163 3.0E5 +263.1556701660156 2.4E4 +273.18419780489205 1.2E5 +281.09145607126817 1.7E7 +281.1556396484375 1.4E4 +288.7059020996094 1.3E4 +288.9707946777344 5.8E3 +291.07555533111724 3.4E5 +291.1944807260778 1.5E5 +302.5604248046875 2.7E4 +308.99086278800263 4.5E4 +309.01251191862383 3.3E5 +309.0273964372139 1.6E5 +309.08632944125 5.7E7 +309.1471567783512 1.8E5 +309.1617851932138 5.7E5 +309.1826171875 1.7E4 +309.2041870327636 2.2E5 +311.0927429199219 1.2E4 +323.6383972167969 1.3E4 +326.756591796875 1.5E4 +END IONS + +BEGIN IONS +FEATURE_ID=10 +PEPMASS=520.3394165039062 +CHARGE=1 +RTINSECONDS=441.007332 +SPECTYPE=CORRELATED MS +MSLEVEL=1 +FILENAME=20221018_PMA_OA_AG_B91.mzML;20221018_PMA_OA_AG_B5.mzML;20221018_PMA_OA_AG_V8.mzML +SCANS=-1 +520.3394165039062 5.9E8 +521.343505859375 1.7E8 +522.344482421875 3.1E7 +523.3475341796875 4.9E6 +END IONS + +BEGIN IONS +FEATURE_ID=10 +PEPMASS=520.3394165039062 +CHARGE=1 +RTINSECONDS=441.007332 +MSLEVEL=2 +FILENAME=20221018_PMA_OA_AG_V8.mzML +SCANS=10 +MERGED_SCANS=4014 +MERGED_STATS=1 / 1 (0 removed due to low quality, 0 removed due to low cosine). +50.994598388671875 2.5E4 +51.5555305480957 3.1E4 +55.05515670776367 7.6E4 +55.81440353393555 4.1E4 +56.050384521484375 1.1E5 +57.0345344543457 2.8E4 +57.812644958496094 8.1E4 +57.815799713134766 2.7E5 +58.06326675415039 5.1E4 +58.0665283203125 1.7E5 +59.074092864990234 5.9E4 +60.0816764831543 3.5E6 +60.084835052490234 6.7E4 +67.05511474609375 1.8E5 +69.0707015991211 1.2E5 +70.06608581542969 2.5E4 +71.06974029541016 4.6E4 +71.07372283935547 8.5E5 +72.46218872070312 4.7E4 +77.64588165283203 2.5E4 +79.0549087524414 4.8E4 +81.07050323486328 1.6E5 +83.08609008789062 3.8E4 +86.09706115722656 1.7E7 +86.10552978515625 1.2E5 +86.10755920410156 8.7E4 +91.05423736572266 3.6E4 +93.07057189941406 3.3E4 +95.08612823486328 1.9E5 +96.87626647949219 6.3E4 +96.88300323486328 1.0E5 +98.98452758789062 4.6E5 +104.1002426147461 5.8E4 +104.10743713378906 1.8E6 +104.11477661132812 3.1E4 +107.08570098876953 4.0E4 +109.10177612304688 8.0E4 +111.8896255493164 2.7E4 +119.0855712890625 4.7E4 +125.00001525878906 9.5E6 +163.01528930664062 6.4E4 +183.99134826660156 4.2E4 +184.01983642578125 8.5E4 +184.02935791015625 2.2E5 +184.03927612304688 6.6E5 +184.0463409423828 5.3E5 +184.0734405517578 9.3E7 +184.10073852539062 5.4E5 +184.10740661621094 7.0E5 +184.1173858642578 2.4E5 +184.1265869140625 1.1E5 +184.1360626220703 8.7E4 +184.14601135253906 5.0E4 +258.11004638671875 1.3E5 +337.2734069824219 1.8E5 +502.3294677734375 9.1E5 +520.3399047851562 5.5E6 +END IONS \ No newline at end of file diff --git a/bindings/sirius-bindings/tests/test_sirius.rs b/bindings/sirius-bindings/tests/test_sirius.rs new file mode 100644 index 00000000..c4b62215 --- /dev/null +++ b/bindings/sirius-bindings/tests/test_sirius.rs @@ -0,0 +1,166 @@ +//! # Test for the Sirius bindings +use sirius_bindings::prelude::*; +use std::path::Path; + +#[test] +#[should_panic] +fn test_failing_sirius() { + let sirius = SiriusBuilder::default() + .maximal_mz(802.2) + .unwrap() + .formula_search_db(DBVector::from(vec![SearchDB::Bio])) + .unwrap() + .isotope_settings_filter(false) + .unwrap() + .structure_search_db(DBVector::from(vec![SearchDB::Coconut])) + .unwrap() + .timeout_seconds_per_tree_default() + .unwrap() + .number_of_candidates_per_ion(5) + .unwrap() + .adduct_settings_detectable_default() + .unwrap() + .timeout_seconds_per_instance_default() + .unwrap() + .formula_result_threshold_default() + .unwrap() + .inject_el_gordo_compounds_default() + .unwrap() + .median_noise_intensity(0.013) + .unwrap() + .ms1_absolute_intensity_error(-0.5) // should panic here ! + .unwrap() + .ms1_minimal_intensity_to_consider(0.4) + .unwrap() + .ms1_relative_intensity_error_default() + .unwrap() + .noise_threshold_settings_absolute_threshold_default() + .unwrap() + .noise_threshold_settings_intensity_threshold(0.2) + .unwrap() + .noise_threshold_settings_maximal_number_of_peaks(42) + .unwrap() + .number_of_structure_candidates(2) + .unwrap() + .enable_formula() + .unwrap() + .enable_zodiac() + .unwrap() + .enable_fingerprint() + .unwrap() + .enable_structure() + .unwrap() + .enable_canopus() + .unwrap() + .build(); + + let input_file_path = Path::new("tests/data/input_sirius.mgf"); + let output_file_path = Path::new("tests/data/output_sirius"); + + // Check if the path exists before attempting to remove it + if output_file_path.exists() { + let _ = std::fs::remove_dir_all(output_file_path); + } + + sirius.run(input_file_path, output_file_path).unwrap(); +} + +#[test] +fn test_run_sirius_default() -> Result<(), String> { + let sirius = SiriusBuilder::::default() + .maximal_mz_default()? + .enable_formula()? + .enable_zodiac()? + .enable_fingerprint()? + .enable_structure()? + .enable_canopus()? + .enable_write_summaries()? + .max_cpus_default()? + .build(); + let input_file_path = Path::new("tests/data/input_sirius.mgf"); + let output_file_path = Path::new("tests/data/output_sirius_default"); + + // Check if the path exists before attempting to remove it + if output_file_path.exists() { + let _ = std::fs::remove_dir_all(output_file_path); + } + sirius.run(input_file_path, output_file_path).unwrap(); + + Ok(()) +} + +#[test] +fn test_run_sirius_with_enpkg_params() -> Result<(), String> { + let sirius = SiriusBuilder::default() + .maximal_mz(800.0)? + .max_cpus_default()? + .isotope_settings_filter(true)? + .formula_search_db(DBVector::from(vec![SearchDB::Bio]))? + .timeout_seconds_per_tree(0)? + .formula_settings_enforced(AtomVector::from(vec![ + Atoms::H, + Atoms::C, + Atoms::N, + Atoms::O, + Atoms::P, + ]))? + .timeout_seconds_per_instance(0)? + .adduct_settings_detectable(AdductsVector::from(vec![ + Adducts::MplusHplus, + Adducts::MplusHminusTwoH2Oplus, + Adducts::MplusNaplus, + Adducts::MplusKplus, + Adducts::MplusH3NplusHplus, + Adducts::MplusHminusH2Oplus, + ]))? + .use_heuristic_mz_to_use_heuristic_only(650)? + .algorithm_profile(Instruments::Orbitrap)? + .isotope_ms2_settings(IsotopeMS2Settings::Ignore)? + .ms2_mass_deviation_allowed_mass_deviation(MassDeviation::Ppm(5.0))? + .number_of_candidates_per_ion(1)? + .use_heuristic_mz_to_use_heuristic(300)? + .formula_settings_detectable(AtomVector::from(vec![ + Atoms::B, + Atoms::Cl, + Atoms::Se, + Atoms::S, + ]))? + .number_of_candidates(10)? + .zodiac_number_of_considered_candidates_at_300_mz(10)? + .zodiac_run_in_two_steps(true)? + .zodiac_edge_filter_thresholds_min_local_connections(10)? + .zodiac_edge_filter_thresholds_threshold_filter(0.95)? + .zodiac_epochs_burn_in_period(2000)? + .zodiac_epochs_number_of_markov_chains(10)? + .zodiac_number_of_considered_candidates_at_800_mz(50)? + .zodiac_epochs_iterations(20000)? + .adduct_settings_enforced_default()? + .adduct_settings_fallback(AdductsVector::from(vec![ + Adducts::MplusHplus, + Adducts::MplusNaplus, + Adducts::MplusKplus, + ]))? + .formula_result_threshold(true)? + .inject_el_gordo_compounds(true)? + .structure_search_db(DBVector::from(vec![SearchDB::Bio]))? + .recompute_results(false)? + .enable_formula()? + .enable_zodiac()? + .enable_fingerprint()? + .enable_structure()? + .enable_canopus()? + .enable_write_summaries()? + .build(); + + let input_file_path = Path::new("tests/data/input_sirius.mgf"); + let output_file_path = Path::new("tests/data/output_sirius"); + + // Check if the path exists before attempting to remove it + if output_file_path.exists() { + let _ = std::fs::remove_dir_all(output_file_path); + } + // Start running sirius + sirius.run(input_file_path, output_file_path).unwrap(); + + Ok(()) +}