From e0bebfa23c928ab54e0b9c17adf3cea16af8a9bf Mon Sep 17 00:00:00 2001 From: Joshua Klein Date: Thu, 29 Feb 2024 22:58:40 -0500 Subject: [PATCH] checkpoint --- CHANGELOG.md | 19 ++ Cargo.lock | 530 ++++++++++++++++-------------- Cargo.toml | 4 +- examples/mzconvert.rs | 3 +- src/io/mgf.rs | 122 +++++-- src/io/mzml.rs | 2 +- src/io/mzml/reader.rs | 327 +++++++++++++++--- src/io/mzml/reading_shared.rs | 9 + src/io/mzmlb/reader.rs | 220 +++++++++++-- src/io/mzmlb/writer.rs | 2 +- src/io/offset_index.rs | 1 - src/meta/data_processing.rs | 48 ++- src/meta/traits.rs | 5 + src/params.rs | 97 +++++- src/spectrum/bindata/array.rs | 34 ++ src/spectrum/bindata/encodings.rs | 4 + src/spectrum/bindata/map.rs | 120 ++++--- src/spectrum/chromatogram.rs | 127 ++++++- src/spectrum/scan_properties.rs | 126 ++++++- src/spectrum/spectrum.rs | 163 ++++++++- 20 files changed, 1537 insertions(+), 426 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa7ac4f..82666c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,25 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog], and this project adheres to [Semantic Versioning]. +## [0.13.0] - Unreleased + +### Added +- `MGFReaderType` and `MGFWriterType` implement `MSDataFileMetadata` + +### Changed +- `MGFWriterType` now generates a spectrum title when one is absent, rather than defaulting to + the spectrum's native ID. +- `CURIE` can now be compared to `Param` + +### Deprecated + +### Removed + +### Fixed + +### Security + + ## [0.12.0] - 2024-01-29 ### Changed diff --git a/Cargo.lock b/Cargo.lock index 81ae956..6403844 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -49,9 +49,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.11" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" +checksum = "96b09b5178381e0874812a9b157f7fe84982617e48f71f4e3235482775e5b540" dependencies = [ "anstyle", "anstyle-parse", @@ -63,9 +63,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" [[package]] name = "anstyle-parse" @@ -97,9 +97,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.76" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59d2a3357dde987206219e78ecfbbb6e8dad06cbb65292758d3270e6254f7355" +checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" [[package]] name = "approx" @@ -160,9 +160,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.5" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64-simd" @@ -182,9 +182,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" [[package]] name = "block-buffer" @@ -206,15 +206,15 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.14.0" +version = "3.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b" [[package]] name = "bytemuck" -version = "1.14.0" +version = "1.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" +checksum = "a2ef034f05691a48569bd920a96c81b9d91bbad1ab5ac7c4616c1f6ef36cb79f" [[package]] name = "byteorder" @@ -257,12 +257,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.83" +version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "libc", -] +checksum = "3286b845d0fccbdd15af433f61c5970e711987036cb468f437ff6badd70f4e24" [[package]] name = "cfg-if" @@ -272,23 +269,23 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.31" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "wasm-bindgen", - "windows-targets 0.48.5", + "windows-targets 0.52.3", ] [[package]] name = "ciborium" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" dependencies = [ "ciborium-io", "ciborium-ll", @@ -297,15 +294,15 @@ dependencies = [ [[package]] name = "ciborium-io" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" [[package]] name = "ciborium-ll" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" dependencies = [ "ciborium-io", "half", @@ -313,9 +310,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.11" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2" +checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da" dependencies = [ "clap_builder", "clap_derive", @@ -323,33 +320,33 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.11" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb" +checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb" dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim", + "strsim 0.11.0", ] [[package]] name = "clap_derive" -version = "4.4.7" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.50", ] [[package]] name = "clap_lex" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "cmake" @@ -432,18 +429,18 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" dependencies = [ "cfg-if", ] @@ -486,34 +483,34 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" dependencies = [ - "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" -version = "0.9.17" +version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e3681d554572a651dda4186cd47240627c3d0114d45a95f6ad27f2f22e7548d" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ - "autocfg", - "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.18" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c" -dependencies = [ - "cfg-if", -] +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "crypto-common" @@ -545,7 +542,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim", + "strsim 0.10.0", "syn 1.0.109", ] @@ -674,15 +671,15 @@ dependencies = [ [[package]] name = "either" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" [[package]] name = "env_logger" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" dependencies = [ "humantime", "is-terminal", @@ -736,9 +733,9 @@ checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] name = "fdeflate" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7caf4086251adeba90011a7ff9bd1f6d7f7595be0871867daa4dbb0fcf2ca932" +checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" dependencies = [ "simd-adler32", ] @@ -839,9 +836,9 @@ dependencies = [ [[package]] name = "freetype" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee38378a9e3db1cc693b4f88d166ae375338a0ff75cb8263e1c601d51f35dc6" +checksum = "efc8599a3078adf8edeb86c71e9f8fa7d88af5ca31e806a867756081f90f5d83" dependencies = [ "freetype-sys", "libc", @@ -849,11 +846,11 @@ dependencies = [ [[package]] name = "freetype-sys" -version = "0.13.1" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a37d4011c0cc628dfa766fcc195454f4b068d7afdc2adfd28861191d866e731a" +checksum = "66ee28c39a43d89fbed8b4798fb4ba56722cfd2b5af81f9326c27614ba88ecd5" dependencies = [ - "cmake", + "cc", "libc", "pkg-config", ] @@ -870,9 +867,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if", "libc", @@ -909,9 +906,13 @@ checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "half" -version = "1.8.2" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" +checksum = "bc52e53916c08643f1b56ec082790d1e86a32e58dc5268f897f313fbae7b4872" +dependencies = [ + "cfg-if", + "crunchy", +] [[package]] name = "hashbrown" @@ -998,9 +999,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.3.3" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +checksum = "379dada1584ad501b383485dd706b8afb7a70fcbc7f4da7d780638a5a6124a60" [[package]] name = "humantime" @@ -1010,9 +1011,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "iana-time-zone" -version = "0.1.58" +version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -1049,24 +1050,23 @@ dependencies = [ [[package]] name = "image" -version = "0.24.7" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" dependencies = [ "bytemuck", "byteorder", "color_quant", "jpeg-decoder", - "num-rational", "num-traits", "png", ] [[package]] name = "indexmap" -version = "2.1.0" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" dependencies = [ "equivalent", "hashbrown", @@ -1106,13 +1106,13 @@ dependencies = [ [[package]] name = "is-terminal" -version = "0.4.9" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" dependencies = [ "hermit-abi", - "rustix", - "windows-sys 0.48.0", + "libc", + "windows-sys 0.52.0", ] [[package]] @@ -1132,15 +1132,15 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "jpeg-decoder" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" +checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" [[package]] name = "js-sys" -version = "0.3.66" +version = "0.3.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" dependencies = [ "wasm-bindgen", ] @@ -1189,9 +1189,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.151" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libloading" @@ -1219,16 +1219,16 @@ version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "libc", "redox_syscall 0.4.1", ] [[package]] name = "libz-sys" -version = "1.1.12" +version = "1.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b" +checksum = "037731f5d3aaa87a5675e895b63ddff1a87624bc29f77004ea829809654e48f6" dependencies = [ "cc", "cmake", @@ -1239,9 +1239,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "lock_api" @@ -1286,15 +1286,15 @@ checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" [[package]] name = "memchr" -version = "2.6.4" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", "simd-adler32", @@ -1336,15 +1336,15 @@ dependencies = [ [[package]] name = "mzpeaks" -version = "0.9.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d519a4d34d369ad9869553909c659de71e682119d58f00861d11be2978a25e67" +checksum = "7889b09ecc49ba7fb3efc2db7f3e09fb9e33dcc1e371a80d3e5f539a206094a4" [[package]] name = "mzsignal" -version = "0.10.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8aa6b2785466f3db02e8cd5d64105307fce6e04d92929b9e0069b505dc4ffe4" +checksum = "e231bf6cd72e4e12c2d1042722030981997a35fbde96510f4ad229873e23636b" dependencies = [ "cfg-if", "log", @@ -1360,9 +1360,9 @@ dependencies = [ [[package]] name = "nalgebra" -version = "0.32.3" +version = "0.32.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "307ed9b18cc2423f29e83f84fd23a8e73628727990181f18641a8b5dc2ab1caa" +checksum = "4541eb06dce09c0241ebbaab7102f0a01a0c8994afed2e5d0d66775016e25ac2" dependencies = [ "approx 0.5.1", "matrixmultiply", @@ -1446,9 +1446,9 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" +checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" dependencies = [ "num-traits", "rand", @@ -1457,11 +1457,10 @@ dependencies = [ [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", "num-traits", ] @@ -1478,9 +1477,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", ] @@ -1564,9 +1563,9 @@ checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "openblas-build" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eba42c395477605f400a8d79ee0b756cfb82abe3eb5618e35fa70d3a36010a7f" +checksum = "d4b6b44095098cafc71915cfac3427135b6dd2ea85820a7d94a5871cb0d1e169" dependencies = [ "anyhow", "flate2", @@ -1579,9 +1578,9 @@ dependencies = [ [[package]] name = "openblas-src" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38e5d8af0b707ac2fe1574daa88b4157da73b0de3dc7c39fe3e2c0bb64070501" +checksum = "aa4958649f766a1013db4254a852cdf2836764869b6654fa117316905f537363" dependencies = [ "dirs", "openblas-build", @@ -1590,11 +1589,11 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.62" +version = "0.10.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "cfg-if", "foreign-types", "libc", @@ -1611,7 +1610,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.50", ] [[package]] @@ -1622,9 +1621,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.98" +version = "0.9.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7" +checksum = "dda2b0f344e78efc2facf7d195d098df0dd72151b26ab98da807afc26c198dff" dependencies = [ "cc", "libc", @@ -1702,9 +1701,9 @@ checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pkg-config" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "plotters" @@ -1754,9 +1753,9 @@ dependencies = [ [[package]] name = "png" -version = "0.17.10" +version = "0.17.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd75bf2d8dd3702b9707cdbc56a5b9ef42cec752eb8b3bafc01234558442aa64" +checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" dependencies = [ "bitflags 1.3.2", "crc32fast", @@ -1797,9 +1796,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.71" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] @@ -1817,9 +1816,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.33" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -1862,9 +1861,9 @@ checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" [[package]] name = "rayon" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" +checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051" dependencies = [ "either", "rayon-core", @@ -1872,9 +1871,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ "crossbeam-deque", "crossbeam-utils", @@ -1911,9 +1910,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.2" +version = "1.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" dependencies = [ "aho-corasick", "memchr", @@ -1923,9 +1922,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" dependencies = [ "aho-corasick", "memchr", @@ -1940,16 +1939,17 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "ring" -version = "0.17.7" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", + "cfg-if", "getrandom", "libc", "spin", "untrusted", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -1969,11 +1969,11 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.28" +version = "0.38.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "errno 0.3.8", "libc", "linux-raw-sys", @@ -1982,52 +1982,63 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.10" +version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" +checksum = "e87c9956bd9807afa1f77e0f7594af32566e830e088a5576d27c5b6f30f49d41" dependencies = [ "log", "ring", + "rustls-pki-types", "rustls-webpki", - "sct", + "subtle", + "zeroize", ] [[package]] name = "rustls-native-certs" -version = "0.6.3" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +checksum = "8f1fb85efa936c42c6d5fc28d2629bb51e4b2f4b8a5211e297d599cc5a093792" dependencies = [ "openssl-probe", "rustls-pemfile", + "rustls-pki-types", "schannel", "security-framework", ] [[package]] name = "rustls-pemfile" -version = "1.0.4" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +checksum = "3c333bb734fcdedcea57de1602543590f545f127dc8b533324318fd492c5c70b" dependencies = [ - "base64 0.21.5", + "base64 0.21.7", + "rustls-pki-types", ] +[[package]] +name = "rustls-pki-types" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "048a63e5b3ac996d78d402940b5fa47973d2d080c6c6fffa1d0f19c4445310b7" + [[package]] name = "rustls-webpki" -version = "0.101.7" +version = "0.102.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" dependencies = [ "ring", + "rustls-pki-types", "untrusted", ] [[package]] name = "ryu" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] name = "safe_arch" @@ -2049,11 +2060,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -2062,16 +2073,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "sct" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "security-framework" version = "2.9.2" @@ -2097,35 +2098,35 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.20" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" [[package]] name = "serde" -version = "1.0.193" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.193" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.50", ] [[package]] name = "serde_json" -version = "1.0.108" +version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" dependencies = [ "itoa", "ryu", @@ -2164,9 +2165,9 @@ checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" [[package]] name = "smallvec" -version = "1.11.2" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" [[package]] name = "spin" @@ -2180,6 +2181,18 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strsim" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + [[package]] name = "syn" version = "1.0.109" @@ -2193,9 +2206,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.43" +version = "2.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee659fb5f3d355364e1f3e5bc10fb82068efbf824a1e9d1c9504244a6469ad53" +checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb" dependencies = [ "proc-macro2", "quote", @@ -2215,22 +2228,21 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.8.1" +version = "3.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" +checksum = "a365e8cd18e44762ef95d87f284f4b5cd04107fec2ff3052bd6a3e6069669e67" dependencies = [ "cfg-if", "fastrand", - "redox_syscall 0.4.1", "rustix", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "termcolor" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ "winapi-util", ] @@ -2253,27 +2265,27 @@ checksum = "7ba277e77219e9eea169e8508942db1bf5d8a41ff2db9b20aab5a5aadc9fa25d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.50", ] [[package]] name = "thiserror" -version = "1.0.52" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a48fd946b02c0a526b2e9481c8e2a17755e47039164a86c4070446e3a4614d" +checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.52" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7fbe9b594d6568a6a1443250a7e67d80b74e1e96f6d1715e1e21cc1888291d3" +checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.50", ] [[package]] @@ -2303,9 +2315,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.35.1" +version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" dependencies = [ "backtrace", "bytes", @@ -2322,7 +2334,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.50", ] [[package]] @@ -2348,9 +2360,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-bidi" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" @@ -2360,9 +2372,9 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] @@ -2375,17 +2387,18 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "ureq" -version = "2.9.1" +version = "2.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cdd25c339e200129fe4de81451814e5228c9b771d57378817d6117cc2b3f97" +checksum = "11f214ce18d8b2cbe84ed3aa6486ed3f5b285cf8d8fbdbce9f3f767a724adc35" dependencies = [ - "base64 0.21.5", + "base64 0.21.7", "flate2", "log", "native-tls", "once_cell", "rustls", "rustls-native-certs", + "rustls-pki-types", "rustls-webpki", "serde", "serde_json", @@ -2412,9 +2425,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" +checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" dependencies = [ "getrandom", ] @@ -2455,9 +2468,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.89" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2465,24 +2478,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.89" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.50", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.89" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2490,28 +2503,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.89" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.50", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.89" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" +checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" [[package]] name = "web-sys" -version = "0.3.66" +version = "0.3.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" +checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446" dependencies = [ "js-sys", "wasm-bindgen", @@ -2519,21 +2532,24 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.25.3" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" +checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009" +dependencies = [ + "rustls-pki-types", +] [[package]] name = "weezl" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" [[package]] name = "wide" -version = "0.7.13" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c68938b57b33da363195412cfc5fc37c9ed49aa9cfe2156fde64b8d2c9498242" +checksum = "89beec544f246e679fc25490e3f8e08003bc4bf612068f325120dad4cea02c1c" dependencies = [ "bytemuck", "safe_arch", @@ -2572,11 +2588,11 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-core" -version = "0.51.1" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.48.5", + "windows-targets 0.52.3", ] [[package]] @@ -2594,7 +2610,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.3", ] [[package]] @@ -2614,17 +2630,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "d380ba1dc7187569a8a9e91ed34b8ccfc33123bbacb8c0aed2d1ad7f3ef2dc5f" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm 0.52.3", + "windows_aarch64_msvc 0.52.3", + "windows_i686_gnu 0.52.3", + "windows_i686_msvc 0.52.3", + "windows_x86_64_gnu 0.52.3", + "windows_x86_64_gnullvm 0.52.3", + "windows_x86_64_msvc 0.52.3", ] [[package]] @@ -2635,9 +2651,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "68e5dcfb9413f53afd9c8f86e56a7b4d86d9a2fa26090ea2dc9e40fba56c6ec6" [[package]] name = "windows_aarch64_msvc" @@ -2647,9 +2663,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "8dab469ebbc45798319e69eebf92308e541ce46760b49b18c6b3fe5e8965b30f" [[package]] name = "windows_i686_gnu" @@ -2659,9 +2675,9 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "2a4e9b6a7cac734a8b4138a4e1044eac3404d8326b6c0f939276560687a033fb" [[package]] name = "windows_i686_msvc" @@ -2671,9 +2687,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "28b0ec9c422ca95ff34a78755cfa6ad4a51371da2a5ace67500cf7ca5f232c58" [[package]] name = "windows_x86_64_gnu" @@ -2683,9 +2699,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "704131571ba93e89d7cd43482277d6632589b18ecf4468f591fbae0a8b101614" [[package]] name = "windows_x86_64_gnullvm" @@ -2695,9 +2711,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "42079295511643151e98d61c38c0acc444e52dd42ab456f7ccfd5152e8ecf21c" [[package]] name = "windows_x86_64_msvc" @@ -2707,9 +2723,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "0770833d60a970638e989b3fa9fd2bb1aaadcf88963d1659fd7d9990196ed2d6" [[package]] name = "winreg" @@ -2732,9 +2748,9 @@ dependencies = [ [[package]] name = "xattr" -version = "1.1.3" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7dae5072fe1f8db8f8d29059189ac175196e410e40ba42d5d4684ae2f750995" +checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" dependencies = [ "libc", "linux-raw-sys", @@ -2752,3 +2768,9 @@ dependencies = [ "once_cell", "pkg-config", ] + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/Cargo.toml b/Cargo.toml index 826bdce..88b9afe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -93,9 +93,9 @@ flate2 = {version = "1.0.20"} num-traits = "0.2" indexmap = { version = "2.0.0", features = [ "serde" ] } log = "0.4.20" -mzpeaks = { version = "0.9.0" } +mzpeaks = { version = ">=0.11.0,<1.0.0" } rayon = { version = "1.7.0", optional = true } -mzsignal = { version = "0.10.0", default-features = false, optional = true} +mzsignal = { version = "0.12.0", default-features = false, optional = true} md5 = "0.7.0" tokio = {version = "1.32.0", optional = true, features = ["macros", "rt", "fs", "rt-multi-thread"]} diff --git a/examples/mzconvert.rs b/examples/mzconvert.rs index 05035d9..30f9e92 100644 --- a/examples/mzconvert.rs +++ b/examples/mzconvert.rs @@ -77,7 +77,8 @@ impl MZConvert { ) -> io::Result<()> { match infer_from_path(&self.outpath).0 { MassSpectrometryFormat::MGF => { - let writer = MGFWriter::new(io::BufWriter::new(fs::File::create(&self.outpath)?)); + let mut writer = MGFWriter::new(io::BufWriter::new(fs::File::create(&self.outpath)?)); + writer.copy_metadata_from(&reader); self.task(reader, writer)?; } MassSpectrometryFormat::MzML => { diff --git a/src/io/mgf.rs b/src/io/mgf.rs index e813797..bf9fb19 100644 --- a/src/io/mgf.rs +++ b/src/io/mgf.rs @@ -32,19 +32,19 @@ use super::traits::{ use super::utils::DetailLevel; use crate::meta::{ DataProcessing, FileDescription, InstrumentConfiguration, MSDataFileMetadata, Software, + MassSpectrometryRun }; -use crate::params::{ControlledVocabulary, Param, ParamDescribed, ParamLike}; -use crate::spectrum::bindata::{ - vec_as_bytes, ArrayType, BinaryArrayMap, BinaryDataArrayType, BuildArrayMapFrom, - BuildFromArrayMap, DataArray, -}; -use crate::spectrum::spectrum::{ - CentroidPeakAdapting, CentroidSpectrumType, DeconvolutedPeakAdapting, MultiLayerSpectrum, -}; -use crate::spectrum::PeakDataLevel; -use crate::spectrum::SignalContinuity; +use crate::params::{ControlledVocabulary, Param, ParamDescribed, ParamLike, CURIE}; use crate::spectrum::{ - Precursor, PrecursorSelection, SelectedIon, SpectrumDescription, SpectrumLike, + IonProperties, PeakDataLevel, Precursor, PrecursorSelection, SelectedIon, SignalContinuity, + SpectrumDescription, SpectrumLike, + spectrum::{ + CentroidPeakAdapting, CentroidSpectrumType, DeconvolutedPeakAdapting, MultiLayerSpectrum, + }, + bindata::{ + vec_as_bytes, ArrayType, BinaryArrayMap, BinaryDataArrayType, BuildArrayMapFrom, + BuildFromArrayMap, DataArray, + } }; use crate::utils::neutral_mass; @@ -205,6 +205,7 @@ pub struct MGFReaderType< instrument_configurations: HashMap, softwares: Vec, data_processings: Vec, + pub run: MassSpectrometryRun, pub detail_level: DetailLevel, centroid_type: PhantomData, deconvoluted_type: PhantomData, @@ -291,12 +292,14 @@ impl< parts.next().map(|v| v.parse().unwrap()).unwrap_or_default(); let charge: Option = parts.next().map(|c| c.parse().unwrap()); builder.description.precursor = Some(Precursor { - ion: SelectedIon { - mz, - intensity, - charge, - ..Default::default() - }, + ions: vec![ + SelectedIon { + mz, + intensity, + charge, + ..Default::default() + }, + ], ..Default::default() }); } @@ -460,6 +463,7 @@ impl< softwares: Vec::new(), file_description: Self::default_file_description(), detail_level: DetailLevel::Full, + run: MassSpectrometryRun::default() } } } @@ -683,6 +687,14 @@ impl< None } } + + fn run_description(&self) -> Option<&MassSpectrometryRun> { + Some(&self.run) + } + + fn run_description_mut(&mut self) -> Option<&mut MassSpectrometryRun> { + Some(&mut self.run) + } } pub type MGFReader = MGFReaderType; @@ -696,6 +708,10 @@ pub(crate) fn is_mgf(buf: &[u8]) -> bool { ) } +const TITLE_CV: CURIE = ControlledVocabulary::MS.curie(1000796); +const MS_LEVEL_CV: CURIE = ControlledVocabulary::MS.curie(1000511); +const MSN_SPECTRUM_CV: CURIE = ControlledVocabulary::MS.curie(1000580); + /// An MGF writer type pub struct MGFWriterType< W: io::Write, @@ -706,6 +722,11 @@ pub struct MGFWriterType< pub offset: usize, centroid_type: PhantomData, deconvoluted_type: PhantomData, + file_description: FileDescription, + instrument_configurations: HashMap, + softwares: Vec, + data_processings: Vec, + run: MassSpectrometryRun, } impl< @@ -721,9 +742,35 @@ impl< offset: 0, centroid_type: PhantomData, deconvoluted_type: PhantomData, + file_description: Default::default(), + instrument_configurations: Default::default(), + softwares: Default::default(), + data_processings: Default::default(), + run: Default::default() } } + pub fn make_title>( + &self, + spectrum: &S, + ) -> String { + let idx = spectrum.index(); + let charge = spectrum + .precursor() + .and_then(|prec| prec.ion().charge()) + .unwrap_or_default(); + let id = spectrum.id(); + let run_id = self.run_description().and_then(|d| d.id.as_ref()); + let source_file = self.source_file_name(); + let title = match (run_id, source_file) { + (None, None) => format!("run.{idx}.{idx}.{charge} NativeID:\"{id}\""), + (None, Some(source_name)) => format!("run.{idx}.{idx}.{charge} SourceFile:\"{source_name}\""), + (Some(run_id), None) => format!("{run_id}.{idx}.{idx}.{charge} NativeID:\"{id}\""), + (Some(run_id), Some(source_name)) => format!("{run_id}.{idx}.{idx}.{charge} SourceFile:\"{source_name}\", NativeID:\"{id}\""), + }; + title + } + pub fn into_inner(self) -> BufWriter { self.handle } @@ -737,6 +784,14 @@ impl< Ok(()) } + fn write_kv(&mut self, key: &str, value: &str) -> io::Result<()> { + self.handle.write_all(key.as_bytes())?; + self.handle.write_all(b"=")?; + self.handle.write_all(value.as_bytes())?; + self.handle.write_all(b"\n")?; + Ok(()) + } + fn write_header>(&mut self, spectrum: &T) -> io::Result<()> { let desc = spectrum.description(); if desc.ms_level == 1 { @@ -746,14 +801,20 @@ impl< ); return Ok(()); } + // spectrum self.handle.write_all( br#"BEGIN IONS TITLE="#, )?; - self.handle.write_all(desc.id.as_bytes())?; + let (title, _had_title) = desc + .get_param_by_curie(&TITLE_CV) + .and_then(|p| Some((p.value.clone(), true))) + .unwrap_or_else(|| (self.make_title(spectrum), false)); + self.handle.write_all(title.as_bytes())?; + self.write_kv("NATIVEID", spectrum.id())?; self.handle.write_all(b"\nRTINSECONDS=")?; self.handle - .write_all(spectrum.start_time().to_string().as_bytes())?; + .write_all((spectrum.start_time() * 60.0).to_string().as_bytes())?; self.handle.write_all(b"\n")?; match &desc.precursor { Some(precursor) => { @@ -785,7 +846,11 @@ TITLE="#, } None => {} } - for param in desc.params() { + for param in desc + .params() + .iter() + .filter(|p| TITLE_CV != **p && MSN_SPECTRUM_CV != **p && MS_LEVEL_CV != **p) + { self.write_param(param)?; } @@ -874,6 +939,23 @@ TITLE="#, } } +impl< + W: io::Write, + C: CentroidPeakAdapting + From, + D: DeconvolutedPeakAdapting + From, + > MSDataFileMetadata for MGFWriterType +{ + crate::impl_metadata_trait!(); + + fn run_description(&self) -> Option<&MassSpectrometryRun> { + Some(&self.run) + } + + fn run_description_mut(&mut self) -> Option<&mut MassSpectrometryRun> { + Some(&mut self.run) + } +} + impl< 'a, W: io::Write, diff --git a/src/io/mzml.rs b/src/io/mzml.rs index c299f2a..e96889f 100644 --- a/src/io/mzml.rs +++ b/src/io/mzml.rs @@ -25,7 +25,7 @@ mod r#async; pub use reading_shared::{ CVParamParse, MzMLParserError, MzMLParserState, MzMLSAX, XMLParseBase, - FileMetadataBuilder + FileMetadataBuilder, EntryType }; #[allow(unused)] diff --git a/src/io/mzml/reader.rs b/src/io/mzml/reader.rs index b65abed..65f1e53 100644 --- a/src/io/mzml/reader.rs +++ b/src/io/mzml/reader.rs @@ -20,6 +20,7 @@ use super::super::offset_index::OffsetIndex; use super::super::traits::{ MZFileReader, RandomAccessSpectrumIterator, ScanSource, SeekRead, SpectrumAccessError, }; +use super::reading_shared::EntryType; use mzpeaks::{CentroidPeak, DeconvolutedPeak}; @@ -33,6 +34,7 @@ use crate::spectrum::bindata::{ ArrayType, BinaryArrayMap, BinaryCompressionType, BinaryDataArrayType, BuildArrayMapFrom, BuildFromArrayMap, DataArray, }; +use crate::spectrum::chromatogram::{Chromatogram, ChromatogramLike}; use crate::spectrum::scan_properties::*; use crate::spectrum::spectrum::{ CentroidPeakAdapting, CentroidSpectrumType, DeconvolutedPeakAdapting, MultiLayerSpectrum, @@ -62,10 +64,13 @@ pub trait SpectrumBuilding< /// Get the last scan window being constructed. fn scan_window_mut(&mut self) -> &mut ScanWindow; fn selected_ion_mut(&mut self) -> &mut SelectedIon; + fn new_selected_ion(&mut self) -> &mut SelectedIon; fn current_array_mut(&mut self) -> &mut DataArray; /// Move all the data into the provided `spectrum` reference fn into_spectrum(self, spectrum: &mut S); + fn into_chromatogram(self, chromatogram: &mut Chromatogram); + fn fill_spectrum>(&mut self, param: P); fn fill_binary_data_array>(&mut self, param: P) { @@ -128,6 +133,16 @@ pub trait SpectrumBuilding< self.current_array_mut().name = ArrayType::NonStandardDataArray { name: Box::new(param.value().to_string()), }; + }, + 1000595 => { + self.current_array_mut().name = ArrayType::TimeArray; + let unit = param.unit(); + match unit { + Unit::Minute | Unit::Second | Unit::Millisecond => self.current_array_mut().unit = unit, + _ => { + warn!("Invalid unit {} found for time array", unit) + } + } } 1002477 => { self.current_array_mut().name = ArrayType::MeanIonMobilityArray; @@ -296,13 +311,14 @@ pub struct MzMLSpectrumBuilder< pub current_array: DataArray, pub index: usize, - pub scan_id: String, + pub entry_id: String, pub ms_level: u8, pub polarity: ScanPolarity, pub signal_continuity: SignalContinuity, pub has_precursor: bool, pub detail_level: DetailLevel, pub instrument_id_map: Option<&'a mut IncrementingIdMap>, + entry_type: EntryType, centroid_type: PhantomData, deconvoluted_type: PhantomData, } @@ -362,7 +378,7 @@ impl< fn into_spectrum(self, spectrum: &mut MultiLayerSpectrum) { let description = &mut spectrum.description; - description.id = self.scan_id; + description.id = self.entry_id; description.index = self.index; description.signal_continuity = self.signal_continuity; description.ms_level = self.ms_level; @@ -409,6 +425,47 @@ impl< self.instrument_id_map = Some(instrument_configurations); self } + + fn new_selected_ion(&mut self) -> &mut SelectedIon { + self.precursor.add_ion(SelectedIon::default()); + self.precursor.last_ion_mut() + } + + fn into_chromatogram(self, chromatogram: &mut Chromatogram) { + let description = chromatogram.description_mut(); + description.id = self.entry_id; + description.index = self.index; + description.polarity = self.polarity; + description.ms_level = Some(self.ms_level); + + let mut params = Vec::with_capacity(self.params.len()); + for param in self.params.into_iter() { + if param.is_controlled() { + if param.is_ms() { + if let Some(chrom_type) = + ChromatogramType::from_accession(param.accession.unwrap()) + { + description.chromatogram_type = chrom_type; + } else { + params.push(param) + } + } else { + params.push(param) + } + } else { + params.push(param) + } + } + + description.params = params; + if self.has_precursor { + description.precursor = Some(self.precursor); + } else { + description.precursor = None; + } + + chromatogram.arrays = self.arrays; + } } impl< @@ -434,7 +491,7 @@ impl< self.acquisition = Acquisition::default(); self.arrays.clear(); self.current_array.clear(); - self.scan_id.clear(); + self.entry_id.clear(); self.precursor = Precursor::default(); self.index = 0; @@ -443,24 +500,20 @@ impl< self.polarity = ScanPolarity::Unknown; } - pub fn _to_spectrum(&self, spectrum: &mut MultiLayerSpectrum) { - let description = &mut spectrum.description; + pub fn set_entry_type(&mut self, entry_type: EntryType) { + self.entry_type = entry_type; + } - description.id = self.scan_id.clone(); - description.index = self.index; - description.signal_continuity = self.signal_continuity; - description.ms_level = self.ms_level; - description.polarity = self.polarity; + pub fn entry_type(&self) -> EntryType { + self.entry_type + } - description.params = self.params.clone(); - description.acquisition = self.acquisition.clone(); - if self.has_precursor { - description.precursor = Some(self.precursor.clone()); - } else { - description.precursor = None; - } + pub fn is_spectrum_entry(&self) -> bool { + matches!(self.entry_type, EntryType::Spectrum) + } - spectrum.arrays = Some(self.arrays.clone()); + pub fn is_chromatogram_entry(&self) -> bool { + matches!(self.entry_type, EntryType::Chromatogram) } pub fn fill_param_into(&mut self, param: Param, state: MzMLParserState) { @@ -549,18 +602,23 @@ impl< } } -impl<'inner, 'outer: 'inner, C: CentroidLike + Default, D: DeconvolutedPeakAdapting> MzMLSAX - for MzMLSpectrumBuilder<'inner, C, D> +impl< + 'inner, + 'outer: 'inner, + C: CentroidLike + Default + BuildFromArrayMap, + D: DeconvolutedPeakAdapting + BuildFromArrayMap, + > MzMLSAX for MzMLSpectrumBuilder<'inner, C, D> { fn start_element(&mut self, event: &BytesStart, state: MzMLParserState) -> ParserResult { let elt_name = event.name(); match elt_name.as_ref() { b"spectrum" => { + self.set_entry_type(EntryType::Spectrum); for attr_parsed in event.attributes() { match attr_parsed { Ok(attr) => match attr.key.as_ref() { b"id" => { - self.scan_id = attr + self.entry_id = attr .unescape_value() .expect("Error decoding id") .to_string(); @@ -663,6 +721,32 @@ impl<'inner, 'outer: 'inner, C: CentroidLike + Default, D: DeconvolutedPeakAdapt b"binary" => { return Ok(MzMLParserState::Binary); } + b"chromatogramList" => return Ok(MzMLParserState::ChromatogramList), + b"chromatogram" => { + self.set_entry_type(EntryType::Chromatogram); + for attr_parsed in event.attributes() { + match attr_parsed { + Ok(attr) => match attr.key.as_ref() { + b"id" => { + self.entry_id = attr + .unescape_value() + .expect("Error decoding id") + .to_string(); + } + b"index" => { + self.index = String::from_utf8_lossy(&attr.value) + .parse::() + .expect("Failed to parse index"); + } + _ => {} + }, + Err(msg) => { + return Err(self.handle_xml_error(msg.into(), state)); + } + } + } + return Ok(MzMLParserState::Chromatogram); + } _ => {} }; Ok(state) @@ -686,7 +770,9 @@ impl<'inner, 'outer: 'inner, C: CentroidLike + Default, D: DeconvolutedPeakAdapt b"cvParam" | b"userParam" => { match Self::handle_param_borrowed(event, reader_position, state) { Ok(param) => match state { - MzMLParserState::Spectrum => self.fill_spectrum(param), + MzMLParserState::Spectrum | MzMLParserState::Chromatogram => { + self.fill_spectrum(param) + } MzMLParserState::ScanList => { if param.is_controlled() { if let Some(comb) = ScanCombination::from_accession( @@ -781,6 +867,7 @@ impl<'inner, 'outer: 'inner, C: CentroidLike + Default, D: DeconvolutedPeakAdapt let elt_name = event.name(); match elt_name.as_ref() { b"spectrum" => return Ok(MzMLParserState::SpectrumDone), + b"chromatogram" => return Ok(MzMLParserState::ChromatogramDone), b"scanList" => return Ok(MzMLParserState::Spectrum), b"scan" => return Ok(MzMLParserState::ScanList), b"scanWindow" => return Ok(MzMLParserState::ScanWindowList), @@ -801,11 +888,13 @@ impl<'inner, 'outer: 'inner, C: CentroidLike + Default, D: DeconvolutedPeakAdapt .decode_and_store() .expect("Error during decoding and storing of array data"); } + debug!("Adding {}", array.name); self.arrays.add(array); return Ok(MzMLParserState::BinaryDataArrayList); } b"binary" => return Ok(MzMLParserState::BinaryDataArray), b"spectrumList" => return Ok(MzMLParserState::SpectrumListDone), + b"chromatogramList" => return Ok(MzMLParserState::ChromatogramListDone), _ => {} }; Ok(state) @@ -873,7 +962,8 @@ pub struct MzMLReaderType< /// A place to store the last error the parser encountered error: Option, /// A spectrum ID to byte offset for fast random access - pub index: OffsetIndex, + pub spectrum_index: OffsetIndex, + pub chromatogram_index: Box, /// The description of the file's contents and the previous data files that were /// consumed to produce it. pub(crate) file_description: FileDescription, @@ -890,7 +980,6 @@ pub struct MzMLReaderType< pub reference_param_groups: HashMap>, pub detail_level: DetailLevel, - // SpectrumList attributes pub run: MassSpectrometryRun, num_spectra: Option, @@ -929,7 +1018,8 @@ impl< state: MzMLParserState::Start, error: None, buffer: Bytes::new(), - index: OffsetIndex::new("spectrum".to_owned()), + spectrum_index: OffsetIndex::new("spectrum".to_owned()), + chromatogram_index: Box::new(OffsetIndex::new("chromatogram".to_owned())), file_description: FileDescription::default(), instrument_configurations: HashMap::new(), @@ -1070,7 +1160,7 @@ impl< self.num_spectra = accumulator.num_spectra; match self.state { - MzMLParserState::SpectrumDone => Ok(()), + MzMLParserState::SpectrumDone | MzMLParserState::ChromatogramDone => Ok(()), MzMLParserState::ParserError => { let mut error = None; mem::swap(&mut error, &mut self.error); @@ -1192,14 +1282,18 @@ impl< offset += self.buffer.len(); self.buffer.clear(); match self.state { - MzMLParserState::SpectrumDone | MzMLParserState::ParserError => { + MzMLParserState::SpectrumDone + | MzMLParserState::ChromatogramDone + | MzMLParserState::ParserError => { break; } _ => {} }; } match self.state { - MzMLParserState::SpectrumDone => Ok((accumulator, offset)), + MzMLParserState::SpectrumDone | MzMLParserState::ChromatogramDone => { + Ok((accumulator, offset)) + } MzMLParserState::ParserError if self.error.is_some() => { let mut error = None; mem::swap(&mut error, &mut self.error); @@ -1262,6 +1356,41 @@ impl< } } } + + fn _read_next_chromatogram(&mut self) -> Result { + let accumulator = MzMLSpectrumBuilder::::with_detail_level(self.detail_level); + + match self.state { + MzMLParserState::ChromatogramDone => { + self.state = MzMLParserState::Resume; + } + MzMLParserState::ParserError => { + eprintln!("Starting parsing from error: {:?}", self.error); + } + state + if state > MzMLParserState::ChromatogramDone + && state < MzMLParserState::Chromatogram => + { + eprintln!( + "Attempting to start parsing a spectrum in state {}", + self.state + ); + } + _ => {} + } + match self._parse_into(accumulator) { + Ok((accumulator, _sz)) => { + if accumulator.is_chromatogram_entry() { + let mut chrom = Chromatogram::default(); + accumulator.into_chromatogram(&mut chrom); + return Ok(chrom); + } else { + return Err(MzMLParserError::UnknownError(self.state)); + } + } + Err(err) => Err(err), + } + } } /// When the underlying stream supports random access, this type can read the index at the end of @@ -1334,6 +1463,58 @@ impl< } Ok(matched_tag) } + + pub fn get_chromatogram_by_id(&mut self, id: &str) -> Option { + let offset_ref = self.chromatogram_index.get(id); + let offset = offset_ref.expect("Failed to retrieve offset"); + let start = self + .handle + .stream_position() + .expect("Failed to save checkpoint"); + self.seek(SeekFrom::Start(offset)) + .expect("Failed to move seek to offset"); + debug_assert!( + self.check_stream("chromatogram").unwrap(), + "The next XML tag was not `chromatogram`" + ); + self.state = MzMLParserState::Resume; + let result = self._read_next_chromatogram(); + self.seek(SeekFrom::Start(start)) + .expect("Failed to restore offset"); + if let Ok(chrom) = result { + Some(chrom) + } else { + None + } + } + + pub fn get_chromatogram_by_index(&mut self, index: usize) -> Option { + let offset_ref = self.chromatogram_index.get_index(index); + let (_key, offset) = offset_ref.expect("Failed to retrieve offset"); + let start = self + .handle + .stream_position() + .expect("Failed to save checkpoint"); + self.seek(SeekFrom::Start(offset)) + .expect("Failed to move seek to offset"); + debug_assert!( + self.check_stream("chromatogram").unwrap(), + "The next XML tag was not `chromatogram`" + ); + self.state = MzMLParserState::Resume; + let result = self._read_next_chromatogram(); + self.seek(SeekFrom::Start(start)) + .expect("Failed to restore offset"); + if let Ok(chrom) = result { + Some(chrom) + } else { + None + } + } + + pub fn iter_chromatograms(&mut self) -> ChromatogramIter { + ChromatogramIter::new(self) + } } /// [`MzMLReaderType`] instances are [`Iterator`]s over [`Spectrum`] @@ -1360,7 +1541,7 @@ impl< { /// Retrieve a spectrum by it's native ID fn get_spectrum_by_id(&mut self, id: &str) -> Option> { - let offset_ref = self.index.get(id); + let offset_ref = self.spectrum_index.get(id); let offset = offset_ref.expect("Failed to retrieve offset"); let start = self .handle @@ -1381,7 +1562,7 @@ impl< /// Retrieve a spectrum by it's integer index fn get_spectrum_by_index(&mut self, index: usize) -> Option> { - let (_id, offset) = self.index.get_index(index)?; + let (_id, offset) = self.spectrum_index.get_index(index)?; let byte_offset = offset; let start = self .handle @@ -1407,14 +1588,14 @@ impl< } fn get_index(&self) -> &OffsetIndex { - if !self.index.init { + if !self.spectrum_index.init { warn!("Attempting to use an uninitialized offset index on MzMLReaderType") } - &self.index + &self.spectrum_index } fn set_index(&mut self, index: OffsetIndex) { - self.index = index + self.spectrum_index = index } } @@ -1552,7 +1733,7 @@ impl< Ok(state) => { indexer_state = state; if matches!(indexer_state, IndexParserState::Done) { - break + break; } } Err(err) => return Err(err), @@ -1582,10 +1763,12 @@ impl< } } self.buffer.clear(); - self.index = indexer.spectrum_index; - self.index.init = true; + self.spectrum_index = indexer.spectrum_index; + self.spectrum_index.init = true; + *self.chromatogram_index = indexer.chromatogram_index; + self.chromatogram_index.init = true; self.handle.seek(SeekFrom::Start(current_position)).unwrap(); - Ok(self.index.len() as u64) + Ok(self.spectrum_index.len() as u64) } /// Builds an offset index to each `` XML element @@ -1616,7 +1799,7 @@ impl< .expect("Error decoding id") .to_string(); // This count is off by 2 because somehow the < and > bytes are removed? - self.index.insert( + self.spectrum_index.insert( scan_id, (reader.buffer_position() - e.len() - 2) as u64, ); @@ -1647,8 +1830,8 @@ impl< self.handle .seek(SeekFrom::Start(start)) .expect("Failed to restore location"); - self.index.init = true; - if self.index.is_empty() { + self.spectrum_index.init = true; + if self.spectrum_index.is_empty() { warn!("An index was built but no entries were found") } offset @@ -1715,6 +1898,48 @@ pub(crate) fn is_mzml(buf: &[u8]) -> bool { } } +pub struct ChromatogramIter< + 'a, + R: SeekRead, + C: CentroidPeakAdapting + BuildFromArrayMap, + D: DeconvolutedPeakAdapting + BuildFromArrayMap, +> { + reader: &'a mut MzMLReaderType, + index: usize, +} + +impl< + 'a, + R: SeekRead, + C: CentroidPeakAdapting + BuildFromArrayMap, + D: DeconvolutedPeakAdapting + BuildFromArrayMap, + > ChromatogramIter<'a, R, C, D> +{ + pub fn new(reader: &'a mut MzMLReaderType) -> Self { + Self { reader, index: 0 } + } +} + +impl< + 'a, + R: SeekRead, + C: CentroidPeakAdapting + BuildFromArrayMap, + D: DeconvolutedPeakAdapting + BuildFromArrayMap, + > Iterator for ChromatogramIter<'a, R, C, D> +{ + type Item = Chromatogram; + + fn next(&mut self) -> Option { + if self.reader.chromatogram_index.len() <= self.index { + let result = self.reader.get_chromatogram_by_index(self.index); + self.index += 1; + result + } else { + None + } + } +} + #[cfg(test)] mod test { use super::*; @@ -1887,7 +2112,7 @@ mod test { panic!("Failed to parse out index {:?}", err); } }; - assert!(!reader.index.is_empty()); + assert!(!reader.spectrum_index.is_empty()); Ok(()) } @@ -2067,6 +2292,26 @@ mod test { Ok(()) } + #[test_log::test] + fn test_get_chromatogram() -> io::Result<()> { + let path = path::Path::new("./test/data/batching_test.mzML"); + let mut reader = MzMLReader::open_path(path)?; + let chrom = reader.get_chromatogram_by_id("TIC").unwrap(); + assert_eq!(chrom.id(), "TIC"); + assert_eq!(chrom.index(), 0); + assert_eq!(chrom.arrays.len(), 3); + + let key = ArrayType::NonStandardDataArray { + name: Box::new("ms level".to_string()), + }; + let arr = chrom.arrays.get(&key).unwrap(); + let view = arr.to_i64().unwrap(); + assert_eq!(view.len(), 73368); + assert_eq!(chrom.time().unwrap().len(), 73368); + + Ok(()) + } + #[cfg(feature = "mzsignal")] #[test] fn test_averaging() -> io::Result<()> { diff --git a/src/io/mzml/reading_shared.rs b/src/io/mzml/reading_shared.rs index 46b76b5..a82a616 100644 --- a/src/io/mzml/reading_shared.rs +++ b/src/io/mzml/reading_shared.rs @@ -81,8 +81,10 @@ pub enum MzMLParserState { SpectrumDone, SpectrumListDone, + ChromatogramList, Chromatogram, ChromatogramDone, + ChromatogramListDone, ParserError, EOF @@ -94,6 +96,13 @@ impl Display for MzMLParserState { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub enum EntryType { + #[default] + Spectrum, + Chromatogram +} + /** All the ways that mzML parsing can go wrong */ diff --git a/src/io/mzmlb/reader.rs b/src/io/mzmlb/reader.rs index 0b0487f..096840c 100644 --- a/src/io/mzmlb/reader.rs +++ b/src/io/mzmlb/reader.rs @@ -15,8 +15,7 @@ use thiserror::Error; use mzpeaks::{CentroidPeak, DeconvolutedPeak}; use crate::io::mzml::{ - CVParamParse, IncrementingIdMap, MzMLParserError, MzMLParserState, MzMLReaderType, MzMLSAX, - MzMLSpectrumBuilder, ParserResult, SpectrumBuilding, + CVParamParse, EntryType, IncrementingIdMap, MzMLParserError, MzMLParserState, MzMLReaderType, MzMLSAX, MzMLSpectrumBuilder, ParserResult, SpectrumBuilding }; use crate::io::traits::MZFileReader; use crate::io::utils::DetailLevel; @@ -38,7 +37,7 @@ use numpress::numpress_decompress; use crate::spectrum::spectrum::{ CentroidPeakAdapting, DeconvolutedPeakAdapting, MultiLayerSpectrum, }; -use crate::spectrum::{IsolationWindow, ScanWindow, SelectedIon}; +use crate::spectrum::{Chromatogram, IsolationWindow, ScanWindow, SelectedIon}; #[derive(Debug, Error)] pub enum MzMLbError { @@ -466,6 +465,22 @@ impl<'a, C: CentroidPeakAdapting + BuildFromArrayMap, D: DeconvolutedPeakAdaptin pub fn with_detail_level(detail_level: DetailLevel) -> Self { Self { inner: MzMLSpectrumBuilder::with_detail_level(detail_level), ..Default::default() } } + + pub fn is_chromatogram_entry(&self) -> bool { + self.inner.is_chromatogram_entry() + } + + pub fn is_spectrum_entry(&self) -> bool { + self.inner.is_spectrum_entry() + } + + pub fn entry_type(&self) -> EntryType { + self.inner.entry_type() + } + + pub fn set_entry_type(&mut self, entry_type: EntryType) { + self.inner.set_entry_type(entry_type) + } } impl<'a, C: CentroidPeakAdapting + BuildFromArrayMap, D: DeconvolutedPeakAdapting + BuildFromArrayMap> MzMLSAX @@ -657,6 +672,14 @@ impl<'a, C: CentroidPeakAdapting + BuildFromArrayMap, D: DeconvolutedPeakAdaptin .borrow_instrument_configuration(instrument_configurations); self } + + fn new_selected_ion(&mut self) -> &mut SelectedIon { + self.inner.new_selected_ion() + } + + fn into_chromatogram(self, chromatogram: &mut crate::spectrum::Chromatogram) { + self.inner.into_chromatogram(chromatogram) + } } pub struct MzMLbReaderType< @@ -665,7 +688,8 @@ pub struct MzMLbReaderType< > { pub handle: hdf5::File, /// A spectrum ID to byte offset for fast random access - pub index: OffsetIndex, + pub spectrum_index: OffsetIndex, + pub chromatogram_index: Box, /// The description of the file's contents and the previous data files that were /// consumed to produce it. pub file_description: FileDescription, @@ -701,7 +725,7 @@ impl<'a, 'b: 'a, C: CentroidPeakAdapting + BuildFromArrayMap, D: DeconvolutedPea Err(e) => Err(io::Error::new(io::ErrorKind::Other, e))?, }; - let index = Self::parse_spectrum_index(&handle)?; + let (spectrum_index, chromatogram_index) = Self::parse_spectrum_index(&handle)?; let mzml_ds = match handle.dataset("mzML") { Ok(ds) => ds, @@ -720,7 +744,8 @@ impl<'a, 'b: 'a, C: CentroidPeakAdapting + BuildFromArrayMap, D: DeconvolutedPea let inst = Self { handle, - index, + spectrum_index, + chromatogram_index: Box::new(chromatogram_index), file_description: mzml_parser.file_description.clone(), instrument_configurations: mzml_parser.instrument_configurations.clone(), softwares: mzml_parser.softwares.clone(), @@ -779,8 +804,8 @@ impl<'a, 'b: 'a, C: CentroidPeakAdapting + BuildFromArrayMap, D: DeconvolutedPea } /// Parses the regular spectrum index if it is present. - fn parse_spectrum_index(handle: &hdf5::File) -> io::Result { - let mut index = OffsetIndex::new("spectrum".to_string()); + fn parse_spectrum_index(handle: &hdf5::File) -> io::Result<(OffsetIndex, OffsetIndex)> { + let mut spectrum_index = OffsetIndex::new("spectrum".to_string()); let mut spectrum_index_ids_ds: ByteReader = match handle.dataset("mzML_spectrumIndex_idRef") { Ok(ds) => ByteReader::from(ds), @@ -804,13 +829,43 @@ impl<'a, 'b: 'a, C: CentroidPeakAdapting + BuildFromArrayMap, D: DeconvolutedPea if id.is_empty() || off == 0 { return; } - index.insert( + spectrum_index.insert( String::from_utf8(id.to_vec()).expect("Faild to decode spectrum id as UTF8"), off, ); }); - index.init = true; - Ok(index) + spectrum_index.init = true; + + let mut chromatogram_index = OffsetIndex::new("chromatogram".to_string()); + let mut chromatogram_index_ids_ds: ByteReader = match handle.dataset("mzML_chromatogramIndex_idRef") + { + Ok(ds) => ByteReader::from(ds), + Err(e) => Err(io::Error::new(io::ErrorKind::Other, e))?, + }; + + let chromatogram_index_offsets_ds = match handle.dataset("mzML_chromatogramIndex") { + Ok(ds) => ds, + Err(e) => Err(io::Error::new(io::ErrorKind::Other, e))?, + }; + idx_buffer.clear(); + chromatogram_index_ids_ds.read_to_end(&mut idx_buffer)?; + let idx_splits = idx_buffer.split(|b| *b == b'\x00'); + let offsets = match chromatogram_index_offsets_ds.read_1d::() { + Ok(series) => series, + Err(e) => Err(io::Error::new(io::ErrorKind::Other, e))?, + }; + + idx_splits.zip(offsets).for_each(|(id, off)| { + if id.is_empty() || off == 0 { + return; + } + chromatogram_index.insert( + String::from_utf8(id.to_vec()).expect("Faild to decode spectrum id as UTF8"), + off, + ); + }); + chromatogram_index.init = true; + Ok((spectrum_index, chromatogram_index)) } fn _parse_into< @@ -829,6 +884,38 @@ impl<'a, 'b: 'a, C: CentroidPeakAdapting + BuildFromArrayMap, D: DeconvolutedPea } } + fn _read_next_chromatogram(&mut self) -> Result { + let accumulator = MzMLbSpectrumBuilder::::with_detail_level(self.detail_level); + + match self.mzml_parser.state { + MzMLParserState::ChromatogramDone => { + self.mzml_parser.state = MzMLParserState::Resume; + } + MzMLParserState::ParserError => { + eprintln!("Starting parsing from error"); + } + state if state > MzMLParserState::ChromatogramDone && state < MzMLParserState::Chromatogram => { + eprintln!( + "Attempting to start parsing a spectrum in state {}", + self.mzml_parser.state + ); + } + _ => {} + } + match self._parse_into(accumulator) { + Ok((accumulator, _sz)) => { + if accumulator.is_chromatogram_entry() { + let mut chrom = Chromatogram::default(); + accumulator.into_chromatogram(&mut chrom); + return Ok(chrom) + } else { + return Err(MzMLParserError::UnknownError(self.mzml_parser.state).into()) + } + } + Err(err) => Err(err), + } + } + /// Populate a new [`MultiLayerSpectrum`] in-place on the next available spectrum data. /// This allocates memory to build the spectrum's attributes but then moves it /// into `spectrum` rather than copying it. @@ -869,6 +956,62 @@ impl<'a, 'b: 'a, C: CentroidPeakAdapting + BuildFromArrayMap, D: DeconvolutedPea pub fn set_blosc_nthreads(num_threads: u8) -> u8 { filters::blosc_set_nthreads(num_threads) } + + pub fn get_chromatogram_by_id(&mut self, id: &str) -> Option { + let offset_ref = self.chromatogram_index.get(id); + let offset = offset_ref.expect("Failed to retrieve offset"); + let start = self + .mzml_parser + .stream_position() + .expect("Failed to save checkpoint"); + self.mzml_parser + .seek(SeekFrom::Start(offset)) + .expect("Failed to seek to offset"); + debug_assert!( + self.mzml_parser.check_stream("chromatogram").unwrap(), + "The next XML tag was not `chromatogram`" + ); + self.mzml_parser.state = MzMLParserState::Resume; + let result = self._read_next_chromatogram(); + self.mzml_parser + .seek(SeekFrom::Start(start)) + .expect("Failed to restore offset"); + if let Ok(chrom) = result { + Some(chrom) + } else { + None + } + } + + pub fn get_chromatogram_by_index(&mut self, index: usize) -> Option { + let offset_ref = self.chromatogram_index.get_index(index); + let (_key, offset) = offset_ref.expect("Failed to retrieve offset"); + let start = self + .mzml_parser + .stream_position() + .expect("Failed to save checkpoint"); + self.mzml_parser + .seek(SeekFrom::Start(offset)) + .expect("Failed to seek to offset"); + debug_assert!( + self.mzml_parser.check_stream("chromatogram").unwrap(), + "The next XML tag was not `chromatogram`" + ); + self.mzml_parser.state = MzMLParserState::Resume; + let result = self._read_next_chromatogram(); + self.mzml_parser + .seek(SeekFrom::Start(start)) + .expect("Failed to restore offset"); + if let Ok(chrom) = result { + Some(chrom) + } else { + None + } + } + + pub fn iter_chromatograms(&mut self) -> ChromatogramIter<'_, C, D> { + ChromatogramIter::new(self) + } } /// [`MzMLbReaderType`] instances are [`Iterator`]s over [`MultiLayerSpectrum`], like all @@ -889,7 +1032,7 @@ impl Option> { - let offset_ref = self.index.get(id); + let offset_ref = self.spectrum_index.get(id); let offset = offset_ref.expect("Failed to retrieve offset"); let start = self .mzml_parser @@ -912,7 +1055,7 @@ impl Option> { - let (_id, offset) = self.index.get_index(index)?; + let (_id, offset) = self.spectrum_index.get_index(index)?; let byte_offset = offset; let start = self .mzml_parser @@ -942,14 +1085,14 @@ impl &OffsetIndex { - if !self.index.init { + if !self.spectrum_index.init { warn!("Attempting to use an uninitialized offset index on MzMLbReaderType") } - &self.index + &self.spectrum_index } fn set_index(&mut self, index: OffsetIndex) { - self.index = index + self.spectrum_index = index } } @@ -1009,7 +1152,7 @@ impl u64 { - self.index.len() as u64 + self.spectrum_index.len() as u64 } fn open_path

(path: P) -> io::Result @@ -1026,7 +1169,7 @@ impl MSDataFileMetadata crate::impl_metadata_trait!(); fn spectrum_count_hint(&self) -> Option { - Some(self.index.len() as u64) + Some(self.spectrum_index.len() as u64) } fn run_description(&self) -> Option<&MassSpectrometryRun> { @@ -1040,6 +1183,45 @@ impl MSDataFileMetadata pub type MzMLbReader = MzMLbReaderType; +pub struct ChromatogramIter< + 'a, + C: CentroidPeakAdapting + BuildFromArrayMap, + D: DeconvolutedPeakAdapting + BuildFromArrayMap, +> { + reader: &'a mut MzMLbReaderType, + index: usize, +} + +impl< + 'a, + C: CentroidPeakAdapting + BuildFromArrayMap, + D: DeconvolutedPeakAdapting + BuildFromArrayMap, + > ChromatogramIter<'a, C, D> +{ + pub fn new(reader: &'a mut MzMLbReaderType) -> Self { + Self { reader, index: 0 } + } +} + +impl< + 'a, + C: CentroidPeakAdapting + BuildFromArrayMap, + D: DeconvolutedPeakAdapting + BuildFromArrayMap, + > Iterator for ChromatogramIter<'a,C, D> +{ + type Item = Chromatogram; + + fn next(&mut self) -> Option { + if self.reader.chromatogram_index.len() <= self.index { + let result = self.reader.get_chromatogram_by_index(self.index); + self.index += 1; + result + } else { + None + } + } +} + #[cfg(test)] mod test { use crate::{MzMLReader, prelude::*}; @@ -1050,7 +1232,7 @@ mod test { fn test_open() -> io::Result<()> { let mut reader = MzMLbReader::new(&"test/data/small.mzMLb")?; let mut ref_reader = MzMLReader::open_path("test/data/small.mzML")?; - assert_eq!(reader.index.len(), 48); + assert_eq!(reader.spectrum_index.len(), 48); assert_eq!(reader.softwares.len(), 4); let scan = reader.next().unwrap(); let ref_scan = ref_reader.next().unwrap(); diff --git a/src/io/mzmlb/writer.rs b/src/io/mzmlb/writer.rs index d76d0d8..87f0825 100644 --- a/src/io/mzmlb/writer.rs +++ b/src/io/mzmlb/writer.rs @@ -27,7 +27,7 @@ use crate::io::traits::ScanWriter; use crate::meta::{ DataProcessing, FileDescription, InstrumentConfiguration, MassSpectrometryRun, Software, }; -use crate::params::ControlledVocabulary; +use crate::params::{ControlledVocabulary, ParamDescribed}; use crate::prelude::{MSDataFileMetadata, SpectrumLike}; use crate::spectrum::bindata::{ ArrayRetrievalError, BinaryDataArrayType, BuildArrayMapFrom, ByteArrayView, DataArray, diff --git a/src/io/offset_index.rs b/src/io/offset_index.rs index 3036895..041b7c3 100644 --- a/src/io/offset_index.rs +++ b/src/io/offset_index.rs @@ -19,7 +19,6 @@ pub struct OffsetIndex { /// The mapping from ID to byte offset, ordered by occurrence // If using serde_json to save this, use - // https://docs.rs/indexmap/2.0.0/indexmap/serde_seq/index.html #[serde(with = "indexmap::map::serde_seq")] pub offsets: IndexMap, diff --git a/src/meta/data_processing.rs b/src/meta/data_processing.rs index 411cc80..e1c1317 100644 --- a/src/meta/data_processing.rs +++ b/src/meta/data_processing.rs @@ -2,7 +2,7 @@ use std::fmt::Display; use crate::impl_param_described; -use crate::params::{ParamList, Param, ParamCow, ControlledVocabulary}; +use crate::params::{ControlledVocabulary, Param, ParamCow, ParamList}; #[derive(Debug, Clone, Default, PartialEq, Eq)] pub struct ProcessingMethod { @@ -37,12 +37,11 @@ impl DataProcessing { } } - #[derive(Debug, Clone, PartialEq)] pub enum DataTransformationAction { FormatConversion(FormatConversion), DataProcessingAction(DataProcessingAction), - Other(Param) + Other(Param), } impl Display for DataTransformationAction { @@ -79,21 +78,35 @@ impl DataProcessingAction { match self { DataProcessingAction::Deisotoping => CV.const_param_ident("deisotoping", 1000033), - DataProcessingAction::ChargeDeconvolution => CV.const_param_ident("charge deconvolution", 1000034), + DataProcessingAction::ChargeDeconvolution => { + CV.const_param_ident("charge deconvolution", 1000034) + } DataProcessingAction::PeakPicking => CV.const_param_ident("peak picking", 1000035), DataProcessingAction::Smoothing => CV.const_param_ident("smoothing", 1000592), - DataProcessingAction::BaselineReduction => CV.const_param_ident("baseline reduction", 1000593), - DataProcessingAction::ChargeStateCalculation => CV.const_param_ident("charge state calculation", 1000778), - DataProcessingAction::PrecursorRecalculation => CV.const_param_ident("precursor recalculation", 1000780), - DataProcessingAction::IntensityNormalization => CV.const_param_ident("intensity normalization", 1001484), + DataProcessingAction::BaselineReduction => { + CV.const_param_ident("baseline reduction", 1000593) + } + DataProcessingAction::ChargeStateCalculation => { + CV.const_param_ident("charge state calculation", 1000778) + } + DataProcessingAction::PrecursorRecalculation => { + CV.const_param_ident("precursor recalculation", 1000780) + } + DataProcessingAction::IntensityNormalization => { + CV.const_param_ident("intensity normalization", 1001484) + } DataProcessingAction::MZCalibration => CV.const_param_ident("m/z calibration", 1001485), DataProcessingAction::DataFiltering => CV.const_param_ident("data filtering", 1001486), - DataProcessingAction::AdductDeconvolution => CV.const_param_ident("adduct deconvolution", 1003220), - DataProcessingAction::IonMobilityDeconvolution => CV.const_param_ident("ion mobility deconvolution", 1003222), + DataProcessingAction::AdductDeconvolution => { + CV.const_param_ident("adduct deconvolution", 1003220) + } + DataProcessingAction::IonMobilityDeconvolution => { + CV.const_param_ident("ion mobility deconvolution", 1003222) + } } } - pub const fn from_accession(accession: u32) -> Option { + pub const fn from_accession(accession: u32) -> Option { match accession { 1000033 => Some(DataProcessingAction::Deisotoping), 1000034 => Some(DataProcessingAction::ChargeDeconvolution), @@ -107,7 +120,7 @@ impl DataProcessingAction { 1001486 => Some(DataProcessingAction::DataFiltering), 1003220 => Some(DataProcessingAction::AdductDeconvolution), 1003222 => Some(DataProcessingAction::IonMobilityDeconvolution), - _ => None + _ => None, } } } @@ -129,8 +142,12 @@ impl FormatConversion { const CV: ControlledVocabulary = ControlledVocabulary::MS; match self { - FormatConversion::ConversionToMzML => CV.const_param_ident("Conversion to mzML", 1000544), - FormatConversion::ConversionToMzMLb => CV.const_param_ident("Conversion to mzMLb", 1002839), + FormatConversion::ConversionToMzML => { + CV.const_param_ident("Conversion to mzML", 1000544) + } + FormatConversion::ConversionToMzMLb => { + CV.const_param_ident("Conversion to mzMLb", 1002839) + } } } @@ -138,8 +155,7 @@ impl FormatConversion { match accession { 1000544 => Some(Self::ConversionToMzML), 1002839 => Some(Self::ConversionToMzMLb), - _ => None + _ => None, } } } - diff --git a/src/meta/traits.rs b/src/meta/traits.rs index e45528a..a26bf40 100644 --- a/src/meta/traits.rs +++ b/src/meta/traits.rs @@ -65,6 +65,11 @@ pub trait MSDataFileMetadata { fn run_description_mut(&mut self) -> Option<&mut MassSpectrometryRun> { None } + + /// Get the name of the primary source file, if available + fn source_file_name(&self) -> Option<&str> { + self.file_description().source_files.first().map(|s| s.name.as_str()) + } } diff --git a/src/params.rs b/src/params.rs index b40896f..f2527c1 100644 --- a/src/params.rs +++ b/src/params.rs @@ -9,15 +9,6 @@ use std::str::{self, FromStr}; use thiserror::Error; -#[doc(hidden)] -#[derive(Debug, Clone, PartialEq, PartialOrd)] -pub enum ValueType { - String(Box), - Integer(i64), - Float(f64), - Other(Box>), -} - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct CURIE { controlled_vocabulary: ControlledVocabulary, @@ -31,12 +22,32 @@ impl CURIE { accession, } } + + pub fn as_param(&self) -> Param { + let mut param = Param::new(); + param.controlled_vocabulary = Some(self.controlled_vocabulary); + param.accession = Some(self.accession); + param + } +} + +impl Display for CURIE { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}:{}", + self.controlled_vocabulary.prefix(), + self.accession + ) + } } impl PartialEq for CURIE { fn eq(&self, other: &T) -> bool { - if !other.is_controlled() || other.controlled_vocabulary().unwrap() != self.controlled_vocabulary { - return false + if !other.is_controlled() + || other.controlled_vocabulary().unwrap() != self.controlled_vocabulary + { + return false; } else { other.accession().unwrap() == self.accession } @@ -218,6 +229,12 @@ impl<'a> From> for Param { } } +impl<'a> PartialEq for ParamCow<'a> { + fn eq(&self, other: &CURIE) -> bool { + other.eq(self) + } +} + /// A controlled vocabulary or user parameter #[derive(Debug, Clone, Default, PartialEq, Eq)] pub struct Param { @@ -255,10 +272,10 @@ impl Param { } } - pub fn new_key_value(name: String, value: String) -> Param { + pub fn new_key_value, V: Into>(name: K, value: V) -> Param { let mut inst = Self::new(); - inst.name = name; - inst.value = value; + inst.name = name.into(); + inst.value = value.into(); inst } @@ -317,6 +334,12 @@ impl ParamLike for Param { } } +impl PartialEq for Param { + fn eq(&self, other: &CURIE) -> bool { + other.eq(self) + } +} + /// Controlled vocabularies used in mass spectrometry data files #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] pub enum ControlledVocabulary { @@ -524,6 +547,8 @@ macro_rules! impl_param_described_deferred { /// Units that a term's value might have #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum Unit { + Unknown, + // Mass MZ, Mass, @@ -547,8 +572,6 @@ pub enum Unit { Electronvolt, PercentElectronVolt, Volt, - - Unknown, } impl Unit { @@ -616,6 +639,48 @@ impl Unit { } } + pub const fn from_curie(acc: &CURIE) -> Unit { + match acc { + CURIE { controlled_vocabulary: ControlledVocabulary::UO, accession: 28 } => Self::Millisecond, + CURIE { controlled_vocabulary: ControlledVocabulary::UO, accession: 10 } => Self::Second, + CURIE { controlled_vocabulary: ControlledVocabulary::UO, accession: 31 } => Self::Minute, + + CURIE { controlled_vocabulary: ControlledVocabulary::MS, accession: 1000040 } => Self::MZ, + CURIE { controlled_vocabulary: ControlledVocabulary::UO, accession: 221 } => Self::Mass, + + CURIE { controlled_vocabulary: ControlledVocabulary::MS, accession: 1000131 } => Self::DetectorCounts, + CURIE { controlled_vocabulary: ControlledVocabulary::MS, accession: 1000132 } => Self::PercentBasePeak, + CURIE { controlled_vocabulary: ControlledVocabulary::UO, accession: 269 } => Self::AbsorbanceUnit, + CURIE { controlled_vocabulary: ControlledVocabulary::MS, accession: 1000814 } => Self::CountsPerSecond, + + CURIE { controlled_vocabulary: ControlledVocabulary::UO, accession: 266 } => Self::Electronvolt, + CURIE { controlled_vocabulary: ControlledVocabulary::UO, accession: 187 } => Self::PercentElectronVolt, + + _ => Unit::Unknown + } + } + + pub const fn to_curie(&self) -> Option { + match self { + Self::Millisecond => Some(CURIE { controlled_vocabulary: ControlledVocabulary::UO, accession: 28 }), + Self::Second => Some(CURIE { controlled_vocabulary: ControlledVocabulary::UO, accession: 10 }), + Self::Minute => Some(CURIE { controlled_vocabulary: ControlledVocabulary::UO, accession: 31 }), + + Self::MZ => Some(CURIE { controlled_vocabulary: ControlledVocabulary::MS, accession: 1000040 }), + Self::Mass => Some(CURIE { controlled_vocabulary: ControlledVocabulary::UO, accession: 221 }), + + Self::DetectorCounts => Some(CURIE { controlled_vocabulary: ControlledVocabulary::MS, accession: 1000131 }), + Self::PercentBasePeak => Some(CURIE { controlled_vocabulary: ControlledVocabulary::MS, accession: 1000132 }), + Self::AbsorbanceUnit => Some(CURIE { controlled_vocabulary: ControlledVocabulary::UO, accession: 269 }), + Self::CountsPerSecond => Some(CURIE { controlled_vocabulary: ControlledVocabulary::MS, accession: 1000814 }), + + Self::Electronvolt => Some(CURIE { controlled_vocabulary: ControlledVocabulary::UO, accession: 266 }), + Self::PercentElectronVolt => Some(CURIE { controlled_vocabulary: ControlledVocabulary::UO, accession: 187 }), + + _ => None + } + } + pub const fn from_param(param: &Param) -> Unit { param.unit } diff --git a/src/spectrum/bindata/array.rs b/src/spectrum/bindata/array.rs index 9cde9ab..fac8698 100644 --- a/src/spectrum/bindata/array.rs +++ b/src/spectrum/bindata/array.rs @@ -138,6 +138,32 @@ impl<'transient, 'lifespan: 'transient> DataArray { } } + pub fn push(&mut self, value: T) -> Result<(), ArrayRetrievalError> { + if !matches!(self.compression, BinaryCompressionType::Decoded) { + self.decode_and_store()?; + }; + if self.dtype.size_of() != mem::size_of::() { + Err(ArrayRetrievalError::DataTypeSizeMismatch) + } else { + let data = bytemuck::bytes_of(&value); + self.data.extend(data.iter()); + Ok(()) + } + } + + pub fn extend(&mut self, values: &[T]) -> Result<(), ArrayRetrievalError> { + if !matches!(self.compression, BinaryCompressionType::Decoded) { + self.decode_and_store()?; + }; + if self.dtype.size_of() != mem::size_of::() { + Err(ArrayRetrievalError::DataTypeSizeMismatch) + } else { + let data = bytemuck::cast_slice(values); + self.data.extend(data.iter()); + Ok(()) + } + } + pub fn encode_bytestring(&self, compression: BinaryCompressionType) -> Bytes { let bytestring = match self.compression { BinaryCompressionType::Decoded => Cow::Borrowed(self.data.as_slice()), @@ -364,6 +390,10 @@ impl<'transient, 'lifespan: 'transient> DataArray { self.dtype = dtype; result } + + pub const fn is_ion_mobility(&self) -> bool { + self.name.is_ion_mobility() + } } impl<'transient, 'lifespan: 'transient> ByteArrayView<'transient, 'lifespan> for DataArray { @@ -402,6 +432,10 @@ impl<'a> DataArraySlice<'a> { pub fn decode(&'a self) -> Result, ArrayRetrievalError> { self.source.decoded_slice(self.start, self.end) } + + pub const fn is_ion_mobility(&self) -> bool { + self.source.is_ion_mobility() + } } impl<'transient, 'lifespan: 'transient> ByteArrayView<'transient, 'lifespan> diff --git a/src/spectrum/bindata/encodings.rs b/src/spectrum/bindata/encodings.rs index f860caa..8e021ea 100644 --- a/src/spectrum/bindata/encodings.rs +++ b/src/spectrum/bindata/encodings.rs @@ -78,6 +78,10 @@ impl ArrayType { } } + pub const fn is_ion_mobility(&self) -> bool { + matches!(self, Self::IonMobilityArray | Self::MeanIonMobilityArray | Self::RawIonMobilityArray | Self::DeconvolutedIonMobilityArray) + } + pub const fn as_param_with_unit_const(&self, unit: Unit) -> ParamCow<'static> { const CV: ControlledVocabulary = ControlledVocabulary::MS; match self { diff --git a/src/spectrum/bindata/map.rs b/src/spectrum/bindata/map.rs index f6a61bf..6330de7 100644 --- a/src/spectrum/bindata/map.rs +++ b/src/spectrum/bindata/map.rs @@ -1,7 +1,6 @@ - use std::borrow::Cow; -use std::collections::HashMap; use std::collections::hash_map::{Iter, IterMut}; +use std::collections::HashMap; #[cfg(feature = "parallelism")] use rayon::prelude::*; @@ -9,10 +8,9 @@ use rayon::prelude::*; use mzpeaks::Tolerance; use super::array::DataArray; -use super::encodings::{ArrayType, ArrayRetrievalError, BinaryCompressionType}; +use super::encodings::{ArrayRetrievalError, ArrayType, BinaryCompressionType}; use super::traits::{ByteArrayView, ByteArrayViewMut}; - #[derive(Debug, Default, Clone)] pub struct BinaryArrayMap { pub byte_buffer_map: HashMap, @@ -25,6 +23,7 @@ impl BinaryArrayMap { } } + /// Get the number of arrays in the map pub fn len(&self) -> usize { self.byte_buffer_map.len() } @@ -33,14 +32,24 @@ impl BinaryArrayMap { self.byte_buffer_map.is_empty() } + /// Check if there is an ion mobility array present + pub fn has_ion_mobility(&self) -> bool { + self.byte_buffer_map.keys().any(|a| a.is_ion_mobility()) + } + + /// Iterate over references to the key-value pairs of this map pub fn iter(&self) -> Iter { self.byte_buffer_map.iter() } + /// Iterate over mutable references to the key-value pairs of this map pub fn iter_mut(&mut self) -> IterMut { self.byte_buffer_map.iter_mut() } + /// Decode all [`DataArray`] in this map. If there are many arrays and the + /// `parallelism` feature is enabled, each array will be decoded on a separate + /// thread. pub fn decode_all_arrays(&mut self) -> Result<(), ArrayRetrievalError> { #[cfg(not(feature = "parallelism"))] { @@ -69,19 +78,26 @@ impl BinaryArrayMap { } #[cfg(feature = "parallelism")] - fn _decode_all_arrays_parallel(&mut self) -> Result<(), ArrayRetrievalError> { - let res: Result<(), ArrayRetrievalError> = self.iter_mut().par_bridge().map(|(_key, value)| { - match value.compression { - BinaryCompressionType::Decoded => {} - _ => { - value.decode_and_store()?; + fn _decode_all_arrays_parallel(&mut self) -> Result<(), ArrayRetrievalError> { + let res: Result<(), ArrayRetrievalError> = self + .iter_mut() + .par_bridge() + .map(|(_key, value)| { + match value.compression { + BinaryCompressionType::Decoded => {} + _ => { + value.decode_and_store()?; + } } - } - Ok(()) - }).collect::>(); + Ok(()) + }) + .collect::>(); res } + /// Decode a specific [`DataArray`] if it is present. + /// + /// This method may fail if decoding fails or if the array type is missing. pub fn decode_array(&mut self, array_type: &ArrayType) -> Result<(), ArrayRetrievalError> { if let Some(array) = self.get_mut(array_type) { array.decode_and_store()?; @@ -91,54 +107,64 @@ impl BinaryArrayMap { } } + /// Add a [`DataArray`] to the map by its [`ArrayType`] name pub fn add(&mut self, array: DataArray) { self.byte_buffer_map.insert(array.name.clone(), array); } + /// Get a reference to a specific [`DataArray`] if present pub fn get(&self, array_type: &ArrayType) -> Option<&DataArray> { self.byte_buffer_map.get(array_type) } + /// Get a mutable reference to a specific [`DataArray`] if present pub fn get_mut(&mut self, array_type: &ArrayType) -> Option<&mut DataArray> { self.byte_buffer_map.get_mut(array_type) } + /// Check whether a specific [`ArrayType`] is present pub fn has_array(&self, array_type: &ArrayType) -> bool { self.byte_buffer_map.contains_key(array_type) } + /// Clear the map, discarding any array data pub fn clear(&mut self) { self.byte_buffer_map.clear(); } + /// Search for a specific m/z pub fn search(&self, query: f64, error_tolerance: Tolerance) -> Option { - let mzs = self.mzs().unwrap(); - let (lower, _upper) = error_tolerance.bounds(query); - match mzs[..].binary_search_by(|m| m.partial_cmp(&lower).unwrap()) { - Ok(i) => { - let mut best_error = error_tolerance.call(query, mzs[i]).abs(); - let mut best_index = i; - let mut index = i + 1; - while index < mzs.len() { - let error = error_tolerance.call(query, mzs[index]).abs(); - if error < best_error { - best_index = index; - best_error = error; + if let Ok(mzs) = self.mzs() { + let (lower, _upper) = error_tolerance.bounds(query); + match mzs[..].binary_search_by(|m| m.partial_cmp(&lower).unwrap()) { + Ok(i) => { + let mut best_error = error_tolerance.call(query, mzs[i]).abs(); + let mut best_index = i; + let mut index = i + 1; + while index < mzs.len() { + let error = error_tolerance.call(query, mzs[index]).abs(); + if error < best_error { + best_index = index; + best_error = error; + } + index += 1; } - index += 1; - } - if best_error < error_tolerance.tol() { - return Some(best_index); + if best_error < error_tolerance.tol() { + return Some(best_index); + } + None } - None + Err(_err) => None, } - Err(_err) => None, + } else { + None } } pub fn mzs(&'_ self) -> Result, ArrayRetrievalError> { let mz_array = self - .get(&ArrayType::MZArray).ok_or(ArrayRetrievalError::NotFound(ArrayType::MZArray))? + .get(&ArrayType::MZArray) + .ok_or(ArrayRetrievalError::NotFound(ArrayType::MZArray))? .to_f64()?; Ok(mz_array) } @@ -155,7 +181,8 @@ impl BinaryArrayMap { pub fn intensities(&'_ self) -> Result, ArrayRetrievalError> { let intensities = self - .get(&ArrayType::IntensityArray).ok_or(ArrayRetrievalError::NotFound(ArrayType::IntensityArray))? + .get(&ArrayType::IntensityArray) + .ok_or(ArrayRetrievalError::NotFound(ArrayType::IntensityArray))? .to_f32()?; Ok(intensities) } @@ -187,16 +214,27 @@ impl BinaryArrayMap { } } + pub fn ion_mobility(&self) -> Result<(Cow<'_, [f32]>, ArrayType), ArrayRetrievalError> { + if let Some((array_type, data_array)) = self + .byte_buffer_map + .iter() + .filter(|(a, _)| a.is_ion_mobility()) + .next() + { + Ok((data_array.to_f32()?, array_type.clone())) + } else { + Err(ArrayRetrievalError::NotFound(ArrayType::IonMobilityArray)) + } + } } - #[cfg(test)] mod test { use crate::spectrum::BinaryDataArrayType; use super::*; - use std::io::{self, prelude::*}; use std::fs; + use std::io::{self, prelude::*}; fn make_array_from_file() -> io::Result { let mut fh = fs::File::open("./test/data/mz_f64_zlib_bas64.txt")?; @@ -223,9 +261,15 @@ mod test { let da = make_array_from_file()?; let mut map = BinaryArrayMap::new(); map.add(da); - assert_eq!(map.get(&ArrayType::MZArray).unwrap().compression, BinaryCompressionType::Zlib); + assert_eq!( + map.get(&ArrayType::MZArray).unwrap().compression, + BinaryCompressionType::Zlib + ); map.decode_all_arrays()?; - assert_eq!(map.get(&ArrayType::MZArray).unwrap().compression, BinaryCompressionType::Decoded); + assert_eq!( + map.get(&ArrayType::MZArray).unwrap().compression, + BinaryCompressionType::Decoded + ); Ok(()) } -} \ No newline at end of file +} diff --git a/src/spectrum/chromatogram.rs b/src/spectrum/chromatogram.rs index 3f259b9..7702370 100644 --- a/src/spectrum/chromatogram.rs +++ b/src/spectrum/chromatogram.rs @@ -1,11 +1,12 @@ -use std::borrow::Cow; +use std::borrow::{Borrow, Cow}; -use crate::params::Param; +use crate::params::{Param, ParamDescribed}; use crate::spectrum::scan_properties::{ Precursor, ScanPolarity, ChromatogramType, ChromatogramDescription, }; use super::bindata::{ArrayType, BinaryArrayMap, ArrayRetrievalError, ByteArrayView}; - +use mzpeaks::coordinate::{Time, MZ}; +use mzpeaks::feature::{FeatureView, SimpleFeature, TimeInterval}; #[derive(Debug, Default, Clone)] @@ -14,11 +15,78 @@ pub struct Chromatogram { pub arrays: BinaryArrayMap } +const EMPTY: &[f64] = &[0.0]; + +macro_rules! as_feature_view { + ($chromatogram:ident, $then:expr) => { + if let Ok(t) = $chromatogram.time() { + if let Ok(i) = $chromatogram.intensity() { + let view = FeatureView::::new(t.borrow(), t.borrow(), i.borrow()); + Some($then(view)) + } else { + None + } + } else { + None + } + }; +} + + +#[allow(unused)] +pub(crate) fn as_simple_feature(chromatogram: &Chromatogram) -> Option> { + if let Ok(t) = chromatogram.time() { + if let Ok(i) = chromatogram.intensity() { + let mut f = SimpleFeature::::empty(0.0); + f.extend(t.iter().zip(i.iter()).map(|(y, z)| (0.0f64, *y, *z))); + return Some(f) + } + } + None +} + +impl TimeInterval