From f1d812ddb85428b234d1f8612140359cc6ea228e Mon Sep 17 00:00:00 2001 From: N08I40K Date: Fri, 21 Mar 2025 07:25:42 +0400 Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B2=D1=8B=D0=B9=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=BC=D0=BC=D0=B8=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 + .idea/.gitignore | 8 + .idea/material_theme_project_new.xml | 17 + .idea/modules.xml | 8 + .idea/schedule-parser-rusted.iml | 13 + .idea/vcs.xml | 6 + Cargo.lock | 2142 +++++++++++++++++++++++++ Cargo.toml | 15 + lib/schedule_parser/Cargo.toml | 23 + lib/schedule_parser/benches/parse.rs | 12 + lib/schedule_parser/src/lib/lib.rs | 627 ++++++++ lib/schedule_parser/src/lib/schema.rs | 97 ++ schedule.xls | Bin 0 -> 121344 bytes src/main.rs | 15 + 14 files changed, 2986 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/.gitignore create mode 100644 .idea/material_theme_project_new.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/schedule-parser-rusted.iml create mode 100644 .idea/vcs.xml create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 lib/schedule_parser/Cargo.toml create mode 100644 lib/schedule_parser/benches/parse.rs create mode 100644 lib/schedule_parser/src/lib/lib.rs create mode 100644 lib/schedule_parser/src/lib/schema.rs create mode 100644 schedule.xls create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..56536bf --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +.~*.xls +schedule.json \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/material_theme_project_new.xml b/.idea/material_theme_project_new.xml new file mode 100644 index 0000000..3da2041 --- /dev/null +++ b/.idea/material_theme_project_new.xml @@ -0,0 +1,17 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..c1f582f --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/schedule-parser-rusted.iml b/.idea/schedule-parser-rusted.iml new file mode 100644 index 0000000..38f6e7d --- /dev/null +++ b/.idea/schedule-parser-rusted.iml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..6741a9f --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,2142 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "actix-codec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" +dependencies = [ + "bitflags", + "bytes", + "futures-core", + "futures-sink", + "memchr", + "pin-project-lite", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "actix-http" +version = "3.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa882656b67966045e4152c634051e70346939fced7117d5f0b52146a7c74c9" +dependencies = [ + "actix-codec", + "actix-rt", + "actix-service", + "actix-utils", + "base64", + "bitflags", + "brotli", + "bytes", + "bytestring", + "derive_more", + "encoding_rs", + "flate2", + "foldhash", + "futures-core", + "h2", + "http", + "httparse", + "httpdate", + "itoa", + "language-tags", + "local-channel", + "mime", + "percent-encoding", + "pin-project-lite", + "rand", + "sha1", + "smallvec", + "tokio", + "tokio-util", + "tracing", + "zstd", +] + +[[package]] +name = "actix-macros" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "actix-router" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13d324164c51f63867b57e73ba5936ea151b8a41a1d23d1031eeb9f70d0236f8" +dependencies = [ + "bytestring", + "cfg-if", + "http", + "regex", + "regex-lite", + "serde", + "tracing", +] + +[[package]] +name = "actix-rt" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eda4e2a6e042aa4e55ac438a2ae052d3b5da0ecf83d7411e1a368946925208" +dependencies = [ + "futures-core", + "tokio", +] + +[[package]] +name = "actix-server" +version = "2.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6398974fd4284f4768af07965701efbbb5fdc0616bff20cade1bb14b77675e24" +dependencies = [ + "actix-rt", + "actix-service", + "actix-utils", + "futures-core", + "futures-util", + "mio", + "socket2", + "tokio", + "tracing", +] + +[[package]] +name = "actix-service" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e46f36bf0e5af44bdc4bdb36fbbd421aa98c79a9bce724e1edeb3894e10dc7f" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "actix-utils" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8" +dependencies = [ + "local-waker", + "pin-project-lite", +] + +[[package]] +name = "actix-web" +version = "4.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2e3b15b3dc6c6ed996e4032389e9849d4ab002b1e92fbfe85b5f307d1479b4d" +dependencies = [ + "actix-codec", + "actix-http", + "actix-macros", + "actix-router", + "actix-rt", + "actix-server", + "actix-service", + "actix-utils", + "actix-web-codegen", + "bytes", + "bytestring", + "cfg-if", + "cookie", + "derive_more", + "encoding_rs", + "foldhash", + "futures-core", + "futures-util", + "impl-more", + "itoa", + "language-tags", + "log", + "mime", + "once_cell", + "pin-project-lite", + "regex", + "regex-lite", + "serde", + "serde_json", + "serde_urlencoded", + "smallvec", + "socket2", + "time", + "tracing", + "url", +] + +[[package]] +name = "actix-web-codegen" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f591380e2e68490b5dfaf1dd1aa0ebe78d84ba7067078512b4ea6e4492d622b8" +dependencies = [ + "actix-router", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "brotli" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74fa05ad7d803d413eb8380983b092cbbaf9a85f151b871360e7b00cd7060b37" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bumpalo" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "bytestring" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e465647ae23b2823b0753f50decb2d5a86d2bb2cac04788fafd1f80e45378e5f" +dependencies = [ + "bytes", +] + +[[package]] +name = "calamine" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138646b9af2c5d7f1804ea4bf93afc597737d2bd4f7341d67c48b03316976eb1" +dependencies = [ + "byteorder", + "codepage", + "encoding_rs", + "log", + "quick-xml", + "serde", + "zip", +] + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cc" +version = "1.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" +dependencies = [ + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "4.5.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + +[[package]] +name = "codepage" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48f68d061bc2828ae826206326e61251aca94c1e4a5305cf52d9138639c918b4" +dependencies = [ + "encoding_rs", +] + +[[package]] +name = "cookie" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "is-terminal", + "itertools", + "num-traits", + "once_cell", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "deranged" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "derive_arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "derive_more" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "flate2" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "fuzzy-matcher" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54614a3312934d066701a80f20f15fa3b56d67ac7722b39eea5b4c9dd1d66c94" +dependencies = [ + "thread_local", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "half" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7db2ff139bba50379da6aa0766b52fdcb62cb5b263009b09ed58ba604e14bbd1" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + +[[package]] +name = "hermit-abi" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbd780fe5cc30f81464441920d82ac8740e2e46b29a6fad543ddd075229ce37e" + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "impl-more" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a5a9a0ff0086c7a148acb942baaabeadf9504d10400b5a05645853729b9cd2" + +[[package]] +name = "indexmap" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "is-terminal" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "jobserver" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "language-tags" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" + +[[package]] +name = "libc" +version = "0.2.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" + +[[package]] +name = "litemap" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" + +[[package]] +name = "local-channel" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6cbc85e69b8df4b8bb8b89ec634e7189099cea8927a276b7384ce5488e53ec8" +dependencies = [ + "futures-core", + "futures-sink", + "local-waker", +] + +[[package]] +name = "local-waker" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "lockfree-object-pool" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e" + +[[package]] +name = "log" +version = "0.4.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.52.0", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc" + +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quick-xml" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" +dependencies = [ + "encoding_rs", + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + +[[package]] +name = "rand" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +dependencies = [ + "rand_chacha", + "rand_core", + "zerocopy", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-lite" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustversion" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schedule-parser-rusted" +version = "0.1.0" +dependencies = [ + "actix-web", + "schedule_parser", + "serde", + "serde_json", + "serde_repr", +] + +[[package]] +name = "schedule_parser" +version = "0.1.0" +dependencies = [ + "calamine", + "chrono", + "criterion", + "fuzzy-matcher", + "regex", + "serde", + "serde_repr", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" + +[[package]] +name = "socket2" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "syn" +version = "2.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "time" +version = "0.3.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d9c75b47bdff86fa3334a3db91356b8d7d86a9b839dab7d0bdc5c3d3a077618" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" + +[[package]] +name = "time-macros" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29aa485584182073ed57fd5004aa09c371f021325014694e432313345865fd04" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "tokio" +version = "1.44.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-util" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", +] + +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-link" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags", +] + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd97444d05a4328b90e75e503a34bad781f14e28a823ad3557f0750df1ebcbc6" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6352c01d0edd5db859a63e2605f4ea3183ddbd15e2c4a9e7d32184df75e4f154" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zip" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabe6324e908f85a1c52063ce7aa26b68dcb7eb6dbc83a2d148403c9bc3eba50" +dependencies = [ + "arbitrary", + "crc32fast", + "crossbeam-utils", + "displaydoc", + "flate2", + "indexmap", + "memchr", + "thiserror", + "zopfli", +] + +[[package]] +name = "zopfli" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5019f391bac5cf252e93bbcc53d039ffd62c7bfb7c150414d61369afe57e946" +dependencies = [ + "bumpalo", + "crc32fast", + "lockfree-object-pool", + "log", + "once_cell", + "simd-adler32", +] + +[[package]] +name = "zstd" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.15+zstd.1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..69583a2 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,15 @@ +[workspace] +members = ["lib/schedule_parser"] + +[package] +name = "schedule-parser-rusted" +version = "0.1.0" +edition = "2024" +publish = false + +[dependencies] +actix-web = "4.10.2" +serde = { version = "1.0.219", features = ["derive"] } +serde_repr = "0.1.20" +serde_json = "1.0.140" +schedule_parser = { path = "./lib/schedule_parser" } \ No newline at end of file diff --git a/lib/schedule_parser/Cargo.toml b/lib/schedule_parser/Cargo.toml new file mode 100644 index 0000000..5c129e3 --- /dev/null +++ b/lib/schedule_parser/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "schedule_parser" +version = "0.1.0" +edition = "2024" + +[lib] +name = "schedule_parser" +path = "src/lib/lib.rs" + +[dependencies] +serde = { version = "1.0.219", features = ["derive"] } +serde_repr = "0.1.20" +chrono = { version = "0.4.40", features = ["serde"] } +calamine = "0.26.1" +regex = "1.11.1" +fuzzy-matcher = "0.3.7" + +[dev-dependencies] +criterion = "0.5.1" + +[[bench]] +name = "parse" +harness = false diff --git a/lib/schedule_parser/benches/parse.rs b/lib/schedule_parser/benches/parse.rs new file mode 100644 index 0000000..e907edb --- /dev/null +++ b/lib/schedule_parser/benches/parse.rs @@ -0,0 +1,12 @@ +use criterion::{Criterion, criterion_group, criterion_main}; +use schedule_parser::parse_xls; +use std::path::Path; + +pub fn bench_parse_xls(c: &mut Criterion) { + c.bench_function("parse_xls", |b| { + b.iter(|| parse_xls(Path::new("../../schedule.xls"))) + }); +} + +criterion_group!(benches, bench_parse_xls); +criterion_main!(benches); diff --git a/lib/schedule_parser/src/lib/lib.rs b/lib/schedule_parser/src/lib/lib.rs new file mode 100644 index 0000000..7745866 --- /dev/null +++ b/lib/schedule_parser/src/lib/lib.rs @@ -0,0 +1,627 @@ +use crate::LessonParseResult::{Lessons, Street}; +use crate::schema::LessonType::Break; +use crate::schema::{Day, Group, Lesson, LessonSubGroup, LessonTime, LessonType}; +use calamine::{Reader, Xls, open_workbook}; +use chrono::{Duration, NaiveDateTime}; +use fuzzy_matcher::FuzzyMatcher; +use fuzzy_matcher::skim::SkimMatcherV2; +use regex::Regex; +use std::collections::HashMap; +use std::path::Path; +use std::sync::LazyLock; + +mod schema; + +struct InternalId { + /** + * Индекс строки + */ + row: u32, + + /** + * Индекс столбца + */ + column: u32, + + /** + * Текст в ячейке + */ + name: String, +} + +struct InternalTime { + /** + * Временной отрезок проведения пары + */ + time_range: LessonTime, + + /** + * Тип пары + */ + lesson_type: LessonType, + + /** + * Индекс пары + */ + default_index: Option, + + /** + * Рамка ячейки + */ + xls_range: ((u32, u32), (u32, u32)), +} + +type WorkSheet = calamine::Range; + +fn get_string_from_cell(worksheet: &WorkSheet, row: u32, col: u32) -> Option { + let cell_data = if let Some(data) = worksheet.get((row as usize, col as usize)) { + data.to_string() + } else { + return None; + }; + + if cell_data.trim().is_empty() { + return None; + } + + static NL_RE: LazyLock Regex> = + LazyLock::new(|| Regex::new(r"[\n\r]+").unwrap()); + static SP_RE: LazyLock Regex> = LazyLock::new(|| Regex::new(r"\s+").unwrap()); + + let trimmed_data = SP_RE + .replace_all(&NL_RE.replace_all(&cell_data, " "), " ") + .trim() + .to_string(); + + if trimmed_data.is_empty() { + None + } else { + Some(trimmed_data) + } +} + +fn get_merge_from_start(worksheet: &WorkSheet, row: u32, column: u32) -> ((u32, u32), (u32, u32)) { + let worksheet_end = worksheet.end().unwrap(); + + let row_end: u32 = { + let mut r: u32 = 0; + + for _r in (row + 1)..worksheet_end.0 { + r = _r; + + if let Some(_) = worksheet.get((_r as usize, column as usize)) { + break; + } + } + + r + }; + + let column_end: u32 = { + let mut c: u32 = 0; + + for _c in (column + 1)..worksheet_end.1 { + c = _c; + + if let Some(_) = worksheet.get((row as usize, _c as usize)) { + break; + } + } + + c + }; + + ((row, column), (row_end, column_end)) +} + +fn parse_skeleton(worksheet: &WorkSheet) -> (Vec, Vec) { + let range = &worksheet; + + let mut is_parsed = false; + + let mut groups: Vec = Vec::new(); + let mut days: Vec = Vec::new(); + + let start = range.start().expect("Could not find start"); + let end = range.end().expect("Could not find end"); + + let mut row = start.0; + while row < end.0 { + row += 1; + + let day_name_opt = get_string_from_cell(&worksheet, row, 0); + if day_name_opt.is_none() { + continue; + } + + let day_name = day_name_opt.unwrap(); + + if !is_parsed { + is_parsed = true; + + row -= 1; + + for column in (start.1 + 2)..=end.1 { + let group_name = get_string_from_cell(&worksheet, row, column); + if group_name.is_none() { + continue; + } + + groups.push(InternalId { + row, + column, + name: group_name.unwrap(), + }); + } + + row += 1; + } + + days.push(InternalId { + row, + column: 0, + name: day_name.clone(), + }); + + if days.len() > 2 && day_name.starts_with("Суббота") { + break; + } + } + + (days, groups) +} + +enum LessonParseResult { + Lessons(Vec), + Street(String), +} + +trait StringInnerSlice { + fn inner_slice(&self, from: usize, to: usize) -> Self; +} + +impl StringInnerSlice for String { + fn inner_slice(&self, from: usize, to: usize) -> Self { + self.chars() + .take(from) + .chain(self.chars().skip(to)) + .collect() + } +} + +fn guess_lesson_type(name: &String) -> Option<(String, LessonType)> { + let map: HashMap = HashMap::from([ + ("(консультация)".to_string(), LessonType::Consultation), + ( + "самостоятельная работа".to_string(), + LessonType::IndependentWork, + ), + ("зачет".to_string(), LessonType::Exam), + ("зачет с оценкой".to_string(), LessonType::ExamWithGrade), + ("экзамен".to_string(), LessonType::ExamDefault), + ]); + + let matcher = SkimMatcherV2::default(); + let name_lower = name.to_lowercase(); + + type SearchResult<'a> = (&'a LessonType, i64, Vec); + + let mut search_results: Vec = map + .iter() + .map(|entry| -> SearchResult { + if let Some((score, indices)) = matcher.fuzzy_indices(&*name_lower, entry.0) { + return (entry.1, score, indices); + } + + (entry.1, 0, Vec::new()) + }) + .collect(); + search_results.sort_by(|a, b| b.1.cmp(&a.1)); + + let guessed_type = search_results.first().unwrap(); + + if guessed_type.1 > 80 { + Some(( + name.inner_slice(guessed_type.2[0], guessed_type.2[guessed_type.2.len() - 1]), + guessed_type.0.clone(), + )) + } else { + None + } +} + +fn parse_lesson( + worksheet: &WorkSheet, + day: &mut Day, + day_times: &Vec, + time: &InternalTime, + column: u32, +) -> LessonParseResult { + let row = time.xls_range.0.0; + + let (name, lesson_type) = { + let raw_name_opt = get_string_from_cell(&worksheet, row, column); + if raw_name_opt.is_none() { + return Lessons(Vec::new()); + } + + let raw_name = raw_name_opt.unwrap(); + + static OTHER_STREET_RE: LazyLock Regex> = + LazyLock::new(|| Regex::new(r"^[А-Я][а-я]+,?\s?[0-9]+$").unwrap()); + + if OTHER_STREET_RE.is_match(&raw_name) { + return Street(raw_name); + } + + if let Some(guess) = guess_lesson_type(&raw_name) { + guess + } else { + (raw_name, time.lesson_type.clone()) + } + }; + + let (default_range, lesson_time): (Option<[u8; 2]>, LessonTime) = { + // check if multi-lesson + let cell_range = get_merge_from_start(worksheet, row, column); + + let end_time_arr = day_times + .iter() + .filter(|time| time.xls_range.1.0 == cell_range.1.0) + .collect::>(); + + let end_time = end_time_arr.first().expect("Unable to find lesson time!"); + + let range: Option<[u8; 2]> = if time.default_index != None { + let default = time.default_index.unwrap() as u8; + Some([default, end_time.default_index.unwrap() as u8]) + } else { + None + }; + + let time = LessonTime { + start: time.time_range.start, + end: end_time.time_range.end, + }; + + (range, time) + }; + + let (name, mut subgroups) = parse_name_and_subgroups(&name); + + { + let cabinets: Vec = parse_cabinets(worksheet, row, column + 1); + + // Если количество кабинетов равно 1, назначаем этот кабинет всем подгруппам + if cabinets.len() == 1 { + for subgroup in &mut subgroups { + subgroup.cabinet = Some(cabinets.get(0).or(Some(&String::new())).unwrap().clone()) + } + } + // Если количество кабинетов совпадает с количеством подгрупп, назначаем кабинеты по порядку + else if cabinets.len() == subgroups.len() { + for subgroup in &mut subgroups { + subgroup.cabinet = Some( + cabinets + .get((subgroup.number - 1) as usize) + .unwrap() + .clone(), + ); + } + } + // Если количество кабинетов больше количества подгрупп, делаем ещё одну подгруппу. + else if cabinets.len() > subgroups.len() { + for index in 0..subgroups.len() { + subgroups[index].cabinet = Some(cabinets[index].clone()); + } + + while cabinets.len() > subgroups.len() { + subgroups.push(LessonSubGroup { + number: (subgroups.len() + 1) as u8, + cabinet: Some(cabinets[subgroups.len()].clone()), + teacher: "Ошибка в расписании".to_string(), + }); + } + } + // Если кабинетов нет, но есть подгруппы, назначаем им значение "??" + else { + for subgroup in &mut subgroups { + subgroup.cabinet = Some("??".to_string()); + } + } + + cabinets + }; + + let lesson = Lesson { + lesson_type, + default_range, + name: Some(name), + time: lesson_time, + subgroups: Some(subgroups), + group: None, + }; + + let prev_lesson = if day.lessons.len() == 0 { + return Lessons(Vec::from([lesson])); + } else { + &day.lessons[day.lessons.len() - 1] + }; + + Lessons(Vec::from([ + Lesson { + lesson_type: Break, + default_range: None, + name: None, + time: LessonTime { + start: prev_lesson.time.end, + end: lesson.time.start, + }, + subgroups: Some(Vec::new()), + group: None, + }, + lesson, + ])) +} + +fn parse_cabinets(worksheet: &WorkSheet, row: u32, column: u32) -> Vec { + let mut cabinets: Vec = Vec::new(); + + if let Some(raw) = get_string_from_cell(&worksheet, row, column) { + let clean = raw.replace("\n", " "); + let parts: Vec<&str> = clean.split(" ").collect(); + + for part in parts { + let clean_part = part.to_string().trim().to_string(); + + cabinets.push(clean_part); + } + } + + cabinets +} + +fn parse_name_and_subgroups(name: &String) -> (String, Vec) { + static LESSON_RE: LazyLock Regex> = + LazyLock::new(|| Regex::new(r"(?:[А-Я][а-я]+[А-Я]{2}(?:\([0-9][а-я]+\))?)+$").unwrap()); + static TEACHER_RE: LazyLock Regex> = + LazyLock::new(|| Regex::new(r"([А-Я][а-я]+)([А-Я])([А-Я])(?:\(([0-9])[а-я]+\))?").unwrap()); + static CLEAN_RE: LazyLock Regex> = + LazyLock::new(|| Regex::new(r"[\s.,]+").unwrap()); + static NAME_CLEAN_RE: LazyLock Regex> = + LazyLock::new(|| Regex::new(r"\.\s+$").unwrap()); + + let (teachers, lesson_name) = { + let clean_name = CLEAN_RE.replace_all(&name, "").to_string(); + + if let Some(captures) = LESSON_RE.captures(&clean_name) { + let capture = captures.get(0).unwrap(); + let capture_str = capture.as_str().to_string(); + let capture_name: String = capture_str.chars().take(5).collect(); + + ( + NAME_CLEAN_RE.replace(&capture_str, "").to_string(), + name[0..name.find(&*capture_name).unwrap()].to_string(), + ) + } else { + return (NAME_CLEAN_RE.replace(&name, "").to_string(), Vec::new()); + } + }; + + let mut subgroups: Vec = Vec::new(); + + let teacher_it = TEACHER_RE.captures_iter(&teachers); + + for captures in teacher_it { + subgroups.push(LessonSubGroup { + number: if let Some(capture) = captures.get(4) { + capture + .as_str() + .to_string() + .parse::() + .expect("Unable to read subgroup index!") + } else { + 0 + }, + cabinet: None, + teacher: format!( + "{} {}.{}.", + captures.get(1).unwrap().as_str().to_string(), + captures.get(2).unwrap().as_str().to_string(), + captures.get(3).unwrap().as_str().to_string() + ), + }) + } + + // фикс, если у кого-то отсутствует индекс подгруппы + + if subgroups.len() == 1 { + let index = subgroups[0].number; + + if index == 0 { + subgroups[0].number = 1u8; + } else { + subgroups.push(LessonSubGroup { + number: if index == 1 { 2 } else { 1 }, + cabinet: None, + teacher: "Только у другой".to_string(), + }); + } + } else if subgroups.len() == 2 { + // если индексы отсутствуют у обоих, ставим поочерёдно + if subgroups[0].number == 0 && subgroups[1].number == 0 { + subgroups[0].number = 1; + subgroups[1].number = 2; + } + // если индекс отсутствует у первого, ставим 2, если у второго индекс 1 и наоборот + else if subgroups[0].number == 0 { + subgroups[0].number = if subgroups[1].number == 1 { 2 } else { 1 }; + } + // если индекс отсутствует у второго, ставим 2, если у первого индекс 1 и наоборот + else if subgroups[1].number == 0 { + subgroups[1].number = if subgroups[0].number == 1 { 2 } else { 1 }; + } + } + + if subgroups.len() == 2 && subgroups[0].number == 2 && subgroups[1].number == 1 { + subgroups.reverse() + } + + (lesson_name, subgroups) +} + +pub fn parse_xls(path: &Path) -> HashMap { + let mut workbook: Xls<_> = open_workbook(path).expect("Can't open workbook"); + + let worksheet: WorkSheet = workbook + .worksheets() + .first() + .expect("No worksheet found") + .1 + .to_owned(); + + let (days_markup, groups_markup) = parse_skeleton(&worksheet); + + let mut groups: HashMap = HashMap::new(); + let mut days_times: Vec> = Vec::new(); + + let saturday_end_row = worksheet.end().unwrap().0; + + for group_markup in groups_markup { + let mut group = Group { + name: group_markup.name, + days: Vec::new(), + }; + + for day_index in 0..(&days_markup).len() { + let day_markup = &days_markup[day_index]; + + let mut day = { + let space_index = day_markup.name.find(' ').unwrap(); + + let name = day_markup.name[..space_index].to_string(); + + let date_raw = day_markup.name[space_index + 1..].to_string(); + let date_add = format!("{} 00:00:00", date_raw); + + let date = NaiveDateTime::parse_from_str(&*date_add, "%d.%m.%Y %H:%M:%S"); + + Day { + name, + street: None, + date: date.unwrap().and_utc(), + lessons: Vec::new(), + } + }; + + let lesson_time_column = days_markup[0].column + 1; + + let row_distance = if day_index != days_markup.len() - 1 { + days_markup[day_index + 1].row + } else { + saturday_end_row + } - day_markup.row; + + if days_times.len() != 6 { + let mut day_times: Vec = Vec::new(); + + for row in day_markup.row..(day_markup.row + row_distance) { + // time + let time_opt = get_string_from_cell(&worksheet, row, lesson_time_column); + if time_opt.is_none() { + continue; + } + + let time = time_opt.unwrap(); + + // type + let lesson_type = if time.contains("пара") { + LessonType::Default + } else { + LessonType::Additional + }; + + // lesson index + let default_index = if lesson_type == LessonType::Default { + Some( + time.chars() + .next() + .unwrap() + .to_string() + .parse::() + .unwrap(), + ) + } else { + None + }; + + // time + let time_range = { + static TIME_RE: LazyLock Regex> = + LazyLock::new(|| Regex::new(r"(\d+\.\d+)-(\d+\.\d+)").unwrap()); + + let parse_res = TIME_RE + .captures(&time) + .expect("Unable to obtain lesson start and end!"); + + let start_match = parse_res.get(1).unwrap().as_str(); + let start_parts: Vec<&str> = start_match.split(".").collect(); + + let end_match = parse_res.get(2).unwrap().as_str(); + let end_parts: Vec<&str> = end_match.split(".").collect(); + + LessonTime { + start: day.date.clone() + + Duration::hours(start_parts[0].parse().unwrap()) + + Duration::minutes(start_parts[1].parse().unwrap()), + end: day.date.clone() + + Duration::hours(end_parts[0].parse().unwrap()) + + Duration::minutes(end_parts[1].parse().unwrap()), + } + }; + + day_times.push(InternalTime { + time_range, + lesson_type, + default_index, + xls_range: get_merge_from_start(&worksheet, row, lesson_time_column), + }); + } + + days_times.push(day_times); + } + + let day_times = &days_times[day_index]; + + for time in day_times { + match &mut parse_lesson( + &worksheet, + &mut day, + &day_times, + &time, + group_markup.column, + ) { + Lessons(l) => day.lessons.append(l), + Street(s) => day.street = Some(s.to_owned()), + } + } + + group.days.push(day); + } + + groups.insert(group.name.clone(), group); + } + + groups +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = parse_xls(Path::new("../../schedule.xls")); + assert_ne!(result.len(), 0); + } +} diff --git a/lib/schedule_parser/src/lib/schema.rs b/lib/schedule_parser/src/lib/schema.rs new file mode 100644 index 0000000..8687ad5 --- /dev/null +++ b/lib/schedule_parser/src/lib/schema.rs @@ -0,0 +1,97 @@ +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use serde_repr::{Deserialize_repr, Serialize_repr}; + +#[derive(Serialize, Deserialize, Debug)] +pub struct LessonTime { + pub start: DateTime, + pub end: DateTime, +} + +#[derive(Serialize_repr, Deserialize_repr, Debug, PartialEq, Clone)] +#[repr(u8)] +pub enum LessonType { + Default = 0, // Обычная + Additional, // Допы + Break, // Перемена + Consultation, // Консультация + IndependentWork, // Самостоятельная работа + Exam, // Зачёт + ExamWithGrade, // Зачет с оценкой + ExamDefault, // Экзамен +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct LessonSubGroup { + pub number: u8, + + pub cabinet: Option, + + pub teacher: String, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct Lesson { + /** + * Тип занятия + */ + #[serde(rename = "type")] + pub lesson_type: LessonType, + + /** + * Индексы пар, если присутствуют + */ + #[serde(rename = "defaultRange")] + pub default_range: Option<[u8; 2]>, + + /** + * Название занятия + */ + pub name: Option, + + /** + * Начало и конец занятия + */ + pub time: LessonTime, + + /** + * Подгруппы + */ + #[serde(rename = "subGroups")] + pub subgroups: Option>, + + /** + * Группа (только для расписания преподавателей) + */ + pub group: Option, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct Day { + pub name: String, + + pub street: Option, + + pub date: DateTime, + + pub lessons: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct Group { + pub name: String, + + pub days: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct Schedule { + #[serde(rename = "updatedAt")] + pub updated_at: DateTime, + + pub groups: HashMap, + + #[serde(rename = "updatedGroups")] + pub updated_groups: Vec>, +} diff --git a/schedule.xls b/schedule.xls new file mode 100644 index 0000000000000000000000000000000000000000..d7ec04c07cecc294643e5984861af29bdaba06d8 GIT binary patch literal 121344 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