diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..6879f8b --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,3 @@ +[target.x86_64-unknown-linux-gnu] +linker = "clang" +rustflags = ["-C", "link-arg=-fuse-ld=/home/golds/mold-2.3.3-x86_64-linux/bin/mold"] \ No newline at end of file diff --git a/.gitignore b/.gitignore index 8a53831..edf2892 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,9 @@ __pycache__/ # C extensions *.so +# Test DB +testy.db + # Distribution / packaging .Python .venv/ @@ -72,4 +75,13 @@ docs/_build/ .python-version out.txt requirements.txt -ptest \ No newline at end of file +ptest + +# Pyscan +.pyscan/ +.pyscan/ +.pyscan/ +.pyscan/ +.pyscan/ +.pyscan/ +.pyscan/ \ No newline at end of file diff --git a/.sqlx/query-1143eed8e081ab8548c45cac31fb8f82e4dbd0c15f8f4c79e647e9b6195eb4a1.json b/.sqlx/query-1143eed8e081ab8548c45cac31fb8f82e4dbd0c15f8f4c79e647e9b6195eb4a1.json new file mode 100644 index 0000000..12ccd0c --- /dev/null +++ b/.sqlx/query-1143eed8e081ab8548c45cac31fb8f82e4dbd0c15f8f4c79e647e9b6195eb4a1.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "\n UPDATE ProjectInfo SET path = ?\n WHERE project_id = ?\n ", + "describe": { + "columns": [], + "parameters": { + "Right": 2 + }, + "nullable": [] + }, + "hash": "1143eed8e081ab8548c45cac31fb8f82e4dbd0c15f8f4c79e647e9b6195eb4a1" +} diff --git a/.sqlx/query-2021d6a744f4f134e1380f38f3b03295c54a5f92122c5a0c2a020f6088ca1b5b.json b/.sqlx/query-2021d6a744f4f134e1380f38f3b03295c54a5f92122c5a0c2a020f6088ca1b5b.json new file mode 100644 index 0000000..4b43023 --- /dev/null +++ b/.sqlx/query-2021d6a744f4f134e1380f38f3b03295c54a5f92122c5a0c2a020f6088ca1b5b.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "\n CREATE TABLE IF NOT EXISTS ProjectInfo (\n id INTEGER PRIMARY_KEY AUTO_INCREMENT,\n project_id TEXT NOT NULL,\n path TEXT NOT NULL,\n added TEXT NOT NULL\n ); \n ", + "describe": { + "columns": [], + "parameters": { + "Right": 0 + }, + "nullable": [] + }, + "hash": "2021d6a744f4f134e1380f38f3b03295c54a5f92122c5a0c2a020f6088ca1b5b" +} diff --git a/.sqlx/query-2b189cea9b80ec6b40a3e580079efc81213d13ccdb0f60f48a9490efe44047ac.json b/.sqlx/query-2b189cea9b80ec6b40a3e580079efc81213d13ccdb0f60f48a9490efe44047ac.json new file mode 100644 index 0000000..4b6abbe --- /dev/null +++ b/.sqlx/query-2b189cea9b80ec6b40a3e580079efc81213d13ccdb0f60f48a9490efe44047ac.json @@ -0,0 +1,20 @@ +{ + "db_name": "SQLite", + "query": "\n SELECT hash from DependencyChanges \n WHERE hash = ?;\n ", + "describe": { + "columns": [ + { + "name": "hash", + "ordinal": 0, + "type_info": "Text" + } + ], + "parameters": { + "Right": 1 + }, + "nullable": [ + false + ] + }, + "hash": "2b189cea9b80ec6b40a3e580079efc81213d13ccdb0f60f48a9490efe44047ac" +} diff --git a/.sqlx/query-2b4b1116ae1a9edeae070ed2ca233d44eafdcad589034ed13ba0bdf738c0fc4f.json b/.sqlx/query-2b4b1116ae1a9edeae070ed2ca233d44eafdcad589034ed13ba0bdf738c0fc4f.json new file mode 100644 index 0000000..c18055b --- /dev/null +++ b/.sqlx/query-2b4b1116ae1a9edeae070ed2ca233d44eafdcad589034ed13ba0bdf738c0fc4f.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "\n INSERT INTO Vulnerability (cve, name)\n VALUES (?,?);\n ", + "describe": { + "columns": [], + "parameters": { + "Right": 2 + }, + "nullable": [] + }, + "hash": "2b4b1116ae1a9edeae070ed2ca233d44eafdcad589034ed13ba0bdf738c0fc4f" +} diff --git a/.sqlx/query-476ee627b3c8d37fabe9af81e56d5808fff59f2d643914ff90643ea05170cf0f.json b/.sqlx/query-476ee627b3c8d37fabe9af81e56d5808fff59f2d643914ff90643ea05170cf0f.json new file mode 100644 index 0000000..29e71b7 --- /dev/null +++ b/.sqlx/query-476ee627b3c8d37fabe9af81e56d5808fff59f2d643914ff90643ea05170cf0f.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "\n INSERT INTO Dependency (name, version, added, updated)\n VALUES (?,?,?,?);\n ", + "describe": { + "columns": [], + "parameters": { + "Right": 4 + }, + "nullable": [] + }, + "hash": "476ee627b3c8d37fabe9af81e56d5808fff59f2d643914ff90643ea05170cf0f" +} diff --git a/.sqlx/query-4d8756d5416f1b10aa78ee233276e1cdf656926408208aee8cbdbaf4a03f500c.json b/.sqlx/query-4d8756d5416f1b10aa78ee233276e1cdf656926408208aee8cbdbaf4a03f500c.json new file mode 100644 index 0000000..9beccbf --- /dev/null +++ b/.sqlx/query-4d8756d5416f1b10aa78ee233276e1cdf656926408208aee8cbdbaf4a03f500c.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "CREATE TABLE IF NOT EXISTS pipcache (\n name TEXT NOT NULL,\n version TEXT NOT NULL)", + "describe": { + "columns": [], + "parameters": { + "Right": 0 + }, + "nullable": [] + }, + "hash": "4d8756d5416f1b10aa78ee233276e1cdf656926408208aee8cbdbaf4a03f500c" +} diff --git a/.sqlx/query-7771f0e39a33e95603e67c8204dd21ee7456826574118c0d6c2637281925dedb.json b/.sqlx/query-7771f0e39a33e95603e67c8204dd21ee7456826574118c0d6c2637281925dedb.json new file mode 100644 index 0000000..5b589d0 --- /dev/null +++ b/.sqlx/query-7771f0e39a33e95603e67c8204dd21ee7456826574118c0d6c2637281925dedb.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "\n CREATE TABLE IF NOT EXISTS VulnerabilityDependency (\n vulnerability_cve TEXT NOT NULL,\n dependency_name TEXT NOT NULL,\n FOREIGN KEY (vulnerability_cve) REFERENCES Vulnerability(cve) ON DELETE CASCADE,\n FOREIGN KEY (dependency_name) REFERENCES Dependency(name) ON DELETE CASCADE,\n PRIMARY KEY (vulnerability_cve, dependency_name)\n );\n ", + "describe": { + "columns": [], + "parameters": { + "Right": 0 + }, + "nullable": [] + }, + "hash": "7771f0e39a33e95603e67c8204dd21ee7456826574118c0d6c2637281925dedb" +} diff --git a/.sqlx/query-7cdbc2f0e6add5ee02740df272d992f492fcf2f05af4579cfad4711736b76c95.json b/.sqlx/query-7cdbc2f0e6add5ee02740df272d992f492fcf2f05af4579cfad4711736b76c95.json new file mode 100644 index 0000000..4cac8f2 --- /dev/null +++ b/.sqlx/query-7cdbc2f0e6add5ee02740df272d992f492fcf2f05af4579cfad4711736b76c95.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "\n INSERT INTO ProjectInfo (project_id, path, added)\n VALUES (?,?,?)\n ", + "describe": { + "columns": [], + "parameters": { + "Right": 3 + }, + "nullable": [] + }, + "hash": "7cdbc2f0e6add5ee02740df272d992f492fcf2f05af4579cfad4711736b76c95" +} diff --git a/.sqlx/query-8b33c9cf8336f4b6e8bc642846f261a42f0b7a5264a534ff8b1c5c85fa236482.json b/.sqlx/query-8b33c9cf8336f4b6e8bc642846f261a42f0b7a5264a534ff8b1c5c85fa236482.json new file mode 100644 index 0000000..e5c5534 --- /dev/null +++ b/.sqlx/query-8b33c9cf8336f4b6e8bc642846f261a42f0b7a5264a534ff8b1c5c85fa236482.json @@ -0,0 +1,26 @@ +{ + "db_name": "SQLite", + "query": "\n SELECT key,value FROM Settings;\n ", + "describe": { + "columns": [ + { + "name": "key", + "ordinal": 0, + "type_info": "Text" + }, + { + "name": "value", + "ordinal": 1, + "type_info": "Text" + } + ], + "parameters": { + "Right": 0 + }, + "nullable": [ + false, + false + ] + }, + "hash": "8b33c9cf8336f4b6e8bc642846f261a42f0b7a5264a534ff8b1c5c85fa236482" +} diff --git a/.sqlx/query-999850da2250924c4d747405c0f97ebf294102c39f6bbd6b6166fb0c6a52a5a0.json b/.sqlx/query-999850da2250924c4d747405c0f97ebf294102c39f6bbd6b6166fb0c6a52a5a0.json new file mode 100644 index 0000000..7310f77 --- /dev/null +++ b/.sqlx/query-999850da2250924c4d747405c0f97ebf294102c39f6bbd6b6166fb0c6a52a5a0.json @@ -0,0 +1,32 @@ +{ + "db_name": "SQLite", + "query": "\n SELECT project_id as identifier, path, added FROM ProjectInfo;\n ", + "describe": { + "columns": [ + { + "name": "identifier", + "ordinal": 0, + "type_info": "Text" + }, + { + "name": "path", + "ordinal": 1, + "type_info": "Text" + }, + { + "name": "added", + "ordinal": 2, + "type_info": "Text" + } + ], + "parameters": { + "Right": 0 + }, + "nullable": [ + false, + false, + false + ] + }, + "hash": "999850da2250924c4d747405c0f97ebf294102c39f6bbd6b6166fb0c6a52a5a0" +} diff --git a/.sqlx/query-a88738dacb155b6e1bd2e5089a90292901401f843c266892618bb261080b887a.json b/.sqlx/query-a88738dacb155b6e1bd2e5089a90292901401f843c266892618bb261080b887a.json new file mode 100644 index 0000000..24a74ff --- /dev/null +++ b/.sqlx/query-a88738dacb155b6e1bd2e5089a90292901401f843c266892618bb261080b887a.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "\n CREATE TABLE IF NOT EXISTS DependencyChanges (\n hash TEXT NOT NULL,\n name TEXT NOT NULL,\n change CHAR(1) NOT NULL,\n timestamp INTEGER NOT NULL\n );\n ", + "describe": { + "columns": [], + "parameters": { + "Right": 0 + }, + "nullable": [] + }, + "hash": "a88738dacb155b6e1bd2e5089a90292901401f843c266892618bb261080b887a" +} diff --git a/.sqlx/query-a9456623d7d27b076db135743f5517b45beb0173d3181e62b912edabdf778c11.json b/.sqlx/query-a9456623d7d27b076db135743f5517b45beb0173d3181e62b912edabdf778c11.json new file mode 100644 index 0000000..9a49943 --- /dev/null +++ b/.sqlx/query-a9456623d7d27b076db135743f5517b45beb0173d3181e62b912edabdf778c11.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "\n CREATE TABLE IF NOT EXISTS Vulnerability (\n cve TEXT PRIMARY KEY,\n name TEXT NOT NULL\n ); \n ", + "describe": { + "columns": [], + "parameters": { + "Right": 0 + }, + "nullable": [] + }, + "hash": "a9456623d7d27b076db135743f5517b45beb0173d3181e62b912edabdf778c11" +} diff --git a/.sqlx/query-ad897b700f9bea8d672f9353c45c20135bd9e32b67dbe15a73e2c5a430dd93b4.json b/.sqlx/query-ad897b700f9bea8d672f9353c45c20135bd9e32b67dbe15a73e2c5a430dd93b4.json new file mode 100644 index 0000000..e9f7433 --- /dev/null +++ b/.sqlx/query-ad897b700f9bea8d672f9353c45c20135bd9e32b67dbe15a73e2c5a430dd93b4.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "\n UPDATE Dependency SET name = ?, version = ?, added = ?, updated = ?\n WHERE name = ?;\n ", + "describe": { + "columns": [], + "parameters": { + "Right": 5 + }, + "nullable": [] + }, + "hash": "ad897b700f9bea8d672f9353c45c20135bd9e32b67dbe15a73e2c5a430dd93b4" +} diff --git a/.sqlx/query-b27b0c2a270b3f55a59416f9d806da76349d98ed75eba7def35ecefef1b1c05d.json b/.sqlx/query-b27b0c2a270b3f55a59416f9d806da76349d98ed75eba7def35ecefef1b1c05d.json new file mode 100644 index 0000000..12e1f5c --- /dev/null +++ b/.sqlx/query-b27b0c2a270b3f55a59416f9d806da76349d98ed75eba7def35ecefef1b1c05d.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "UPDATE pipcache SET name = ?, version = ?", + "describe": { + "columns": [], + "parameters": { + "Right": 2 + }, + "nullable": [] + }, + "hash": "b27b0c2a270b3f55a59416f9d806da76349d98ed75eba7def35ecefef1b1c05d" +} diff --git a/.sqlx/query-b41442dba317483596c926d276354b33aaa3751840042f93718533277977158e.json b/.sqlx/query-b41442dba317483596c926d276354b33aaa3751840042f93718533277977158e.json new file mode 100644 index 0000000..1028787 --- /dev/null +++ b/.sqlx/query-b41442dba317483596c926d276354b33aaa3751840042f93718533277977158e.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "\n CREATE TABLE IF NOT EXISTS Dependency (\n name TEXT PRIMARY KEY,\n version TEXT NOT NULL,\n added TEXT NOT NULL,\n updated TEXT NOT NULL\n ); \n ", + "describe": { + "columns": [], + "parameters": { + "Right": 0 + }, + "nullable": [] + }, + "hash": "b41442dba317483596c926d276354b33aaa3751840042f93718533277977158e" +} diff --git a/.sqlx/query-bc812d92b2320efb123ecaa67caf53b3f669e668a7a3c1ff5748b4cacf6d8976.json b/.sqlx/query-bc812d92b2320efb123ecaa67caf53b3f669e668a7a3c1ff5748b4cacf6d8976.json new file mode 100644 index 0000000..2502344 --- /dev/null +++ b/.sqlx/query-bc812d92b2320efb123ecaa67caf53b3f669e668a7a3c1ff5748b4cacf6d8976.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "INSERT INTO pipcache (name, version) VALUES (?, ?)", + "describe": { + "columns": [], + "parameters": { + "Right": 2 + }, + "nullable": [] + }, + "hash": "bc812d92b2320efb123ecaa67caf53b3f669e668a7a3c1ff5748b4cacf6d8976" +} diff --git a/.sqlx/query-d0e102115b330f41819727bf826f6ab9241d4926706174e52c87dde259c4b71d.json b/.sqlx/query-d0e102115b330f41819727bf826f6ab9241d4926706174e52c87dde259c4b71d.json new file mode 100644 index 0000000..1f3b4e9 --- /dev/null +++ b/.sqlx/query-d0e102115b330f41819727bf826f6ab9241d4926706174e52c87dde259c4b71d.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "\n INSERT OR IGNORE INTO Settings (key,value) VALUES (?,?);\n UPDATE Settings SET key = ?, value = ?\n WHERE key = ?;\n ", + "describe": { + "columns": [], + "parameters": { + "Right": 5 + }, + "nullable": [] + }, + "hash": "d0e102115b330f41819727bf826f6ab9241d4926706174e52c87dde259c4b71d" +} diff --git a/.sqlx/query-d67357dd230e09ecaee19dc47c55b0ba46b450baa3796b488af423be6d41dfd6.json b/.sqlx/query-d67357dd230e09ecaee19dc47c55b0ba46b450baa3796b488af423be6d41dfd6.json new file mode 100644 index 0000000..b9460d2 --- /dev/null +++ b/.sqlx/query-d67357dd230e09ecaee19dc47c55b0ba46b450baa3796b488af423be6d41dfd6.json @@ -0,0 +1,32 @@ +{ + "db_name": "SQLite", + "query": "\n SELECT project_id as identifier, path, added FROM ProjectInfo\n WHERE project_id = ?;\n ", + "describe": { + "columns": [ + { + "name": "identifier", + "ordinal": 0, + "type_info": "Text" + }, + { + "name": "path", + "ordinal": 1, + "type_info": "Text" + }, + { + "name": "added", + "ordinal": 2, + "type_info": "Text" + } + ], + "parameters": { + "Right": 1 + }, + "nullable": [ + false, + false, + false + ] + }, + "hash": "d67357dd230e09ecaee19dc47c55b0ba46b450baa3796b488af423be6d41dfd6" +} diff --git a/.sqlx/query-ee7767c6132c0b4781867d39fe62590c14883fd7536407f6c70df0677801e934.json b/.sqlx/query-ee7767c6132c0b4781867d39fe62590c14883fd7536407f6c70df0677801e934.json new file mode 100644 index 0000000..98ef27a --- /dev/null +++ b/.sqlx/query-ee7767c6132c0b4781867d39fe62590c14883fd7536407f6c70df0677801e934.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "\n CREATE TABLE IF NOT EXISTS Settings (\n key TEXT UNIQUE NOT NULL,\n value TEXT NOT NULL\n ); \n ", + "describe": { + "columns": [], + "parameters": { + "Right": 0 + }, + "nullable": [] + }, + "hash": "ee7767c6132c0b4781867d39fe62590c14883fd7536407f6c70df0677801e934" +} diff --git a/.sqlx/query-ef096d4ac618bb69b7485e8cdcc193d0b66a0414440ad3e420a2167d12685f31.json b/.sqlx/query-ef096d4ac618bb69b7485e8cdcc193d0b66a0414440ad3e420a2167d12685f31.json new file mode 100644 index 0000000..11a203e --- /dev/null +++ b/.sqlx/query-ef096d4ac618bb69b7485e8cdcc193d0b66a0414440ad3e420a2167d12685f31.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "DELETE FROM pipcache WHERE name = ?;", + "describe": { + "columns": [], + "parameters": { + "Right": 1 + }, + "nullable": [] + }, + "hash": "ef096d4ac618bb69b7485e8cdcc193d0b66a0414440ad3e420a2167d12685f31" +} diff --git a/Cargo.lock b/Cargo.lock index bbd1e3f..0a0ad3d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "ahash" version = "0.8.3" @@ -9,19 +24,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" dependencies = [ "cfg-if", + "getrandom", "once_cell", "version_check", ] [[package]] name = "aho-corasick" -version = "1.0.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + +[[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" @@ -33,30 +61,29 @@ dependencies = [ [[package]] name = "anstream" -version = "0.3.2" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", - "is-terminal", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" [[package]] name = "anstyle-parse" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" +checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" dependencies = [ "utf8parse", ] @@ -72,25 +99,72 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "1.0.1" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" dependencies = [ "anstyle", "windows-sys 0.48.0", ] +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + +[[package]] +name = "async-trait" +version = "0.1.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + +[[package]] +name = "atoi" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits", +] + [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base64" -version = "0.21.0" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" + +[[package]] +name = "base64ct" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "bitflags" @@ -98,23 +172,50 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +dependencies = [ + "serde", +] + +[[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 = "bumpalo" -version = "3.12.2" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "byteorder" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c6ed94e98ecff0c12dd1b04c15ec0d7d9458ca8fe806cea6f12954efe74c63b" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "cc" -version = "1.0.79" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] [[package]] name = "cfg-if" @@ -124,24 +225,23 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.24" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" dependencies = [ + "android-tzdata", "iana-time-zone", "js-sys", - "num-integer", "num-traits", - "time", "wasm-bindgen", - "winapi", + "windows-targets 0.48.5", ] [[package]] name = "chumsky" -version = "1.0.0-alpha.4" +version = "1.0.0-alpha.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc3172a80699de358070dd99f80ea8badc6cdf8ac2417cb5a96e6d81bf5fe06d" +checksum = "379cdc19530b72a1e76d94a350676eaea1455375533eb38f18dfa712f9996902" dependencies = [ "hashbrown 0.13.2", "stacker", @@ -149,45 +249,43 @@ dependencies = [ [[package]] name = "clap" -version = "4.2.7" +version = "4.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34d21f9bf1b425d2968943631ec91202fe5e837264063503708b83013f8fc938" +checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956" dependencies = [ "clap_builder", "clap_derive", - "once_cell", ] [[package]] name = "clap_builder" -version = "4.2.7" +version = "4.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "914c8c79fb560f238ef6429439a30023c862f7a28e688c58f7203f12b29970bd" +checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45" dependencies = [ "anstream", "anstyle", - "bitflags", "clap_lex", "strsim", ] [[package]] name = "clap_derive" -version = "4.2.0" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" +checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.38", ] [[package]] name = "clap_lex" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" +checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" [[package]] name = "colorchoice" @@ -197,17 +295,23 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "console" -version = "0.15.5" +version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d79fbe8970a77e3e34151cc13d3b3e248aa0faaecb9f6091fa07ebefe5ad60" +checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" dependencies = [ "encode_unicode", "lazy_static", "libc", "unicode-width", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] +[[package]] +name = "const-oid" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" + [[package]] name = "core-foundation" version = "0.9.3" @@ -224,6 +328,118 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +[[package]] +name = "cpufeatures" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484" + +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + +[[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 = "der" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +dependencies = [ + "serde", +] + [[package]] name = "encode_unicode" version = "0.3.6" @@ -232,41 +448,67 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "encoding_rs" -version = "0.8.32" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" dependencies = [ "cfg-if", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "errno" -version = "0.3.1" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" dependencies = [ - "errno-dragonfly", "libc", "windows-sys 0.48.0", ] [[package]] -name = "errno-dragonfly" -version = "0.1.2" +name = "etcetera" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" dependencies = [ - "cc", - "libc", + "cfg-if", + "home", + "windows-sys 0.48.0", ] +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + [[package]] name = "fastrand" -version = "1.9.0" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[package]] +name = "finl_unicode" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6" + +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" dependencies = [ - "instant", + "futures-core", + "futures-sink", + "spin 0.9.8", ] [[package]] @@ -292,9 +534,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] @@ -341,6 +583,17 @@ dependencies = [ "futures-util", ] +[[package]] +name = "futures-intrusive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot", +] + [[package]] name = "futures-io" version = "0.3.28" @@ -355,7 +608,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.38", ] [[package]] @@ -388,11 +641,38 @@ dependencies = [ "slab", ] +[[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.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" + [[package]] name = "h2" -version = "0.3.19" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d357c7ae988e7d2182f7d7871d0b963962420b0678b0997ce7de72001aeab782" +checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" dependencies = [ "bytes", "fnv", @@ -400,7 +680,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 1.9.3", "slab", "tokio", "tokio-util", @@ -422,26 +702,72 @@ dependencies = [ "ahash", ] +[[package]] +name = "hashbrown" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "hashlink" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" +dependencies = [ + "hashbrown 0.14.1", +] + [[package]] name = "heck" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +dependencies = [ + "unicode-segmentation", +] [[package]] name = "hermit-abi" -version = "0.2.6" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" dependencies = [ - "libc", + "hmac", ] [[package]] -name = "hermit-abi" -version = "0.3.1" +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +dependencies = [ + "windows-sys 0.48.0", +] [[package]] name = "http" @@ -473,15 +799,15 @@ checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "0.14.26" +version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", @@ -494,7 +820,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2", + "socket2 0.4.9", "tokio", "tower-service", "tracing", @@ -516,16 +842,16 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.56" +version = "0.1.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" +checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows", + "windows-core", ] [[package]] @@ -539,9 +865,9 @@ dependencies = [ [[package]] name = "idna" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -558,54 +884,41 @@ dependencies = [ ] [[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.10" +name = "indexmap" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897" dependencies = [ - "hermit-abi 0.3.1", - "libc", - "windows-sys 0.48.0", + "equivalent", + "hashbrown 0.14.1", ] [[package]] name = "ipnet" -version = "2.7.2" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" [[package]] -name = "is-terminal" -version = "0.4.7" +name = "itertools" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" dependencies = [ - "hermit-abi 0.3.1", - "io-lifetimes", - "rustix", - "windows-sys 0.48.0", + "either", ] [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "js-sys" -version = "0.3.62" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68c16e1bfd491478ab155fd8b4896b86f9ede344949b641e61501e07c2b8b4d5" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] @@ -615,6 +928,9 @@ name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin 0.5.2", +] [[package]] name = "lenient_semver" @@ -659,47 +975,95 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.144" +version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] -name = "linux-raw-sys" -version = "0.3.7" +name = "libm" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] -name = "log" -version = "0.4.17" +name = "libsqlite3-sys" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +checksum = "afc22eff61b133b115c6e8c74e818c628d6d5e7a502afea6f64dee076dd94326" dependencies = [ - "cfg-if", + "cc", + "pkg-config", + "vcpkg", ] [[package]] -name = "memchr" -version = "2.5.0" +name = "linux-raw-sys" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" [[package]] -name = "mime" -version = "0.3.17" +name = "lock_api" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] [[package]] name = "mio" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", - "log", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.45.0", + "wasi", + "windows-sys 0.48.0", ] [[package]] @@ -720,6 +1084,33 @@ dependencies = [ "tempfile", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand", + "smallvec", + "zeroize", +] + [[package]] name = "num-integer" version = "0.1.45" @@ -730,25 +1121,46 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", + "libm", ] [[package]] name = "num_cpus" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.2.6", + "hermit-abi", "libc", ] +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.18.0" @@ -757,11 +1169,11 @@ checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "openssl" -version = "0.10.52" +version = "0.10.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01b8574602df80f7b85fdfc5392fa884a4e3b3f4f35402c070ab34c3d3f78d56" +checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c" dependencies = [ - "bitflags", + "bitflags 2.4.1", "cfg-if", "foreign-types", "libc", @@ -778,7 +1190,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.38", ] [[package]] @@ -789,9 +1201,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.87" +version = "0.9.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e17f59264b2809d77ae94f0e1ebabc434773f370d6ca667bd223ea10e06cc7e" +checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d" dependencies = [ "cc", "libc", @@ -800,25 +1212,60 @@ dependencies = [ ] [[package]] -name = "pep-508" -version = "0.3.0" +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "parking_lot" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daec7940032badfd65fe9a11705ecbea49c77269ca81934060f33952af010da3" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ - "chumsky", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.4.1", + "smallvec", + "windows-targets 0.48.5", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", ] [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -826,17 +1273,44 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + [[package]] name = "pkg-config" version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + [[package]] name = "proc-macro2" -version = "1.0.57" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4ec6d5fe0b140acb27c9a0444118cf55bfbb4e0b259739429abb4521dd67c16" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] @@ -854,46 +1328,122 @@ dependencies = [ name = "pyscan" version = "0.1.6" dependencies = [ + "anyhow", + "async-trait", "chrono", + "chumsky", "clap", "console", + "dirs", "futures", "lazy_static", "lenient_semver", "once_cell", - "pep-508", "regex", "reqwest", "semver", "serde", "serde_json", + "sqlx", "tokio", "toml", + "xxhash-rust", ] [[package]] name = "quote" -version = "1.0.27" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "redox_syscall" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom", + "redox_syscall 0.2.16", + "thiserror", ] [[package]] name = "regex" -version = "1.8.1" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" dependencies = [ "aho-corasick", "memchr", @@ -902,15 +1452,15 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.7.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "reqwest" -version = "0.11.17" +version = "0.11.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13293b639a097af28fc8a90f22add145a9c954e49d77da06263d58cf44d5fb91" +checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" dependencies = [ "base64", "bytes", @@ -933,6 +1483,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", + "system-configuration", "tokio", "tokio-native-tls", "tower-service", @@ -943,15 +1494,42 @@ dependencies = [ "winreg", ] +[[package]] +name = "rsa" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ab43bb47d23c1a631b4b680199a45255dce26fa9ab2fa902581f624ff13e6a8" +dependencies = [ + "byteorder", + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-iter", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core", + "signature", + "spki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + [[package]] name = "rustix" -version = "0.37.19" +version = "0.38.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" +checksum = "745ecfa778e66b2b63c88a61cb36e0eea109e803b0b86bf9879fbc77c70e86ed" dependencies = [ - "bitflags", + "bitflags 2.4.1", "errno", - "io-lifetimes", "libc", "linux-raw-sys", "windows-sys 0.48.0", @@ -959,26 +1537,32 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "schannel" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "windows-sys 0.42.0", + "windows-sys 0.48.0", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "security-framework" -version = "2.9.0" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2855b3715770894e67cbfa3df957790aa0c9edc3bf06efa1a84d77fa0839d1" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -987,9 +1571,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" dependencies = [ "core-foundation-sys", "libc", @@ -997,35 +1581,35 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.17" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" [[package]] name = "serde" -version = "1.0.163" +version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" +checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.163" +version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" +checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.38", ] [[package]] name = "serde_json" -version = "1.0.96" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" dependencies = [ "itoa", "ryu", @@ -1034,9 +1618,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.1" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4" +checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" dependencies = [ "serde", ] @@ -1053,15 +1637,53 @@ dependencies = [ "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 = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "signature" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" +dependencies = [ + "digest", + "rand_core", +] + [[package]] name = "slab" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] +[[package]] +name = "smallvec" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" + [[package]] name = "socket2" version = "0.4.9" @@ -1072,6 +1694,251 @@ dependencies = [ "winapi", ] +[[package]] +name = "socket2" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spki" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "sqlformat" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b7b278788e7be4d0d29c0f39497a0eef3fba6bbc8e70d8bf7fde46edeaa9e85" +dependencies = [ + "itertools", + "nom", + "unicode_categories", +] + +[[package]] +name = "sqlx" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e50c216e3624ec8e7ecd14c6a6a6370aad6ee5d8cfc3ab30b5162eeeef2ed33" +dependencies = [ + "sqlx-core", + "sqlx-macros", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", +] + +[[package]] +name = "sqlx-core" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d6753e460c998bbd4cd8c6f0ed9a64346fcca0723d6e75e52fdc351c5d2169d" +dependencies = [ + "ahash", + "atoi", + "byteorder", + "bytes", + "chrono", + "crc", + "crossbeam-queue", + "dotenvy", + "either", + "event-listener", + "futures-channel", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashlink", + "hex", + "indexmap 2.0.2", + "log", + "memchr", + "once_cell", + "paste", + "percent-encoding", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlformat", + "thiserror", + "tokio", + "tokio-stream", + "tracing", + "url", +] + +[[package]] +name = "sqlx-macros" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a793bb3ba331ec8359c1853bd39eed32cdd7baaf22c35ccf5c92a7e8d1189ec" +dependencies = [ + "proc-macro2", + "quote", + "sqlx-core", + "sqlx-macros-core", + "syn 1.0.109", +] + +[[package]] +name = "sqlx-macros-core" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a4ee1e104e00dedb6aa5ffdd1343107b0a4702e862a84320ee7cc74782d96fc" +dependencies = [ + "dotenvy", + "either", + "heck", + "hex", + "once_cell", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2", + "sqlx-core", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", + "syn 1.0.109", + "tempfile", + "tokio", + "url", +] + +[[package]] +name = "sqlx-mysql" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "864b869fdf56263f4c95c45483191ea0af340f9f3e3e7b4d57a61c7c87a970db" +dependencies = [ + "atoi", + "base64", + "bitflags 2.4.1", + "byteorder", + "bytes", + "chrono", + "crc", + "digest", + "dotenvy", + "either", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "generic-array", + "hex", + "hkdf", + "hmac", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "percent-encoding", + "rand", + "rsa", + "serde", + "sha1", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "tracing", + "whoami", +] + +[[package]] +name = "sqlx-postgres" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb7ae0e6a97fb3ba33b23ac2671a5ce6e3cabe003f451abd5a56e7951d975624" +dependencies = [ + "atoi", + "base64", + "bitflags 2.4.1", + "byteorder", + "chrono", + "crc", + "dotenvy", + "etcetera", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "hex", + "hkdf", + "hmac", + "home", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "rand", + "serde", + "serde_json", + "sha1", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "tracing", + "whoami", +] + +[[package]] +name = "sqlx-sqlite" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59dc83cf45d89c555a577694534fcd1b55c545a816c816ce51f20bbe56a4f3f" +dependencies = [ + "atoi", + "chrono", + "flume", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log", + "percent-encoding", + "serde", + "sqlx-core", + "tracing", + "url", +] + [[package]] name = "stacker" version = "0.1.15" @@ -1085,45 +1952,103 @@ dependencies = [ "winapi", ] +[[package]] +name = "stringprep" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb41d74e231a107a1b4ee36bd1214b11285b77768d2e3824aedafa988fd36ee6" +dependencies = [ + "finl_unicode", + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "strsim" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + [[package]] name = "syn" -version = "2.0.16" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "syn" +version = "2.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tempfile" -version = "3.5.0" +version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" dependencies = [ "cfg-if", "fastrand", - "redox_syscall", + "redox_syscall 0.3.5", "rustix", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] -name = "time" -version = "0.1.45" +name = "thiserror" +version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", ] [[package]] @@ -1143,17 +2068,17 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.28.1" +version = "1.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aa32867d44e6f2ce3385e89dceb990188b8bb0fb25b0cf576647a6f98ac5105" +checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" dependencies = [ - "autocfg", + "backtrace", "bytes", "libc", "mio", "num_cpus", "pin-project-lite", - "socket2", + "socket2 0.5.4", "tokio-macros", "windows-sys 0.48.0", ] @@ -1166,7 +2091,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.38", ] [[package]] @@ -1179,11 +2104,22 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-stream" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + [[package]] name = "tokio-util" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +checksum = "1d68074620f57a0b21594d9735eb2e98ab38b17f80d3fcb189fca266771ca60d" dependencies = [ "bytes", "futures-core", @@ -1195,9 +2131,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.7.3" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b403acf6f2bb0859c93c7f0d967cb4a75a7ac552100f9322faf64dc047669b21" +checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" dependencies = [ "serde", "serde_spanned", @@ -1207,20 +2143,20 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.1" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.19.8" +version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap", + "indexmap 2.0.2", "serde", "serde_spanned", "toml_datetime", @@ -1235,20 +2171,32 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "ee2ef2af84856a50c1d430afce2fdded0a4ec7eda868db86409b4543df0797f9" dependencies = [ - "cfg-if", + "log", "pin-project-lite", + "tracing-attributes", "tracing-core", ] +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + [[package]] name = "tracing-core" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", ] @@ -1259,6 +2207,12 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + [[package]] name = "unicode-bidi" version = "0.3.13" @@ -1267,9 +2221,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" @@ -1280,17 +2234,29 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + [[package]] name = "unicode-width" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" + +[[package]] +name = "unicode_categories" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" [[package]] name = "url" -version = "2.3.1" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" dependencies = [ "form_urlencoded", "idna", @@ -1317,20 +2283,13 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1339,9 +2298,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.85" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b6cb788c4e39112fbe1822277ef6fb3c55cd86b95cb3d3c4c1c9597e4ac74b4" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1349,24 +2308,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.85" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e522ed4105a9d626d885b35d62501b30d9666283a5c8be12c14a8bdafe7822" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.38", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.35" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "083abe15c5d88556b77bdf7aef403625be9e327ad37c62c4e4129af740168163" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" dependencies = [ "cfg-if", "js-sys", @@ -1376,9 +2335,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.85" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "358a79a0cb89d21db8120cbfb91392335913e4890665b1a7981d9e956903b434" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1386,33 +2345,39 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.85" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4783ce29f09b9d93134d41297aded3a712b7b979e9c6f28c32cb88c973a94869" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.38", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.85" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a901d592cafaa4d711bc324edfaff879ac700b19c3dfd60058d2b445be2691eb" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "web-sys" -version = "0.3.62" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b5f940c7edfdc6d12126d98c9ef4d1b3d470011c47c76a6581df47ad9ba721" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", ] +[[package]] +name = "whoami" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22fc3756b8a9133049b26c7f61ab35416c130e8c09b660f5b3958b446f52cc50" + [[package]] name = "winapi" version = "0.3.9" @@ -1436,27 +2401,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" -dependencies = [ - "windows-targets 0.48.0", -] - -[[package]] -name = "windows-sys" -version = "0.42.0" +name = "windows-core" +version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-targets 0.48.5", ] [[package]] @@ -1474,7 +2424,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.0", + "windows-targets 0.48.5", ] [[package]] @@ -1494,17 +2444,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -1515,9 +2465,9 @@ checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_msvc" @@ -1527,9 +2477,9 @@ checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_aarch64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_i686_gnu" @@ -1539,9 +2489,9 @@ checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_msvc" @@ -1551,9 +2501,9 @@ checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_i686_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_x86_64_gnu" @@ -1563,9 +2513,9 @@ checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnullvm" @@ -1575,9 +2525,9 @@ checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_msvc" @@ -1587,24 +2537,37 @@ checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "windows_x86_64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "winnow" -version = "0.4.6" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699" +checksum = "a3b801d0e0a6726477cc207f60162da452f3a95adb368399bef20a946e06f65c" dependencies = [ "memchr", ] [[package]] name = "winreg" -version = "0.10.1" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "winapi", + "cfg-if", + "windows-sys 0.48.0", ] + +[[package]] +name = "xxhash-rust" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9828b178da53440fa9c766a3d2f73f7cf5d0ac1fe3980c1e5018d899fd19e07b" + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" diff --git a/Cargo.toml b/Cargo.toml index d8e2ffc..0208b36 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,12 +13,11 @@ categories = ["command-line-utilities"] [dependencies] -chrono = "0.4.24" +chrono = "0.4.31" clap = {version="4.2.1", features=["derive"]} console = "0.15.5" lazy_static = "1.4.0" once_cell = "1.18.0" -pep-508 = "0.3.0" regex = "1.7.3" reqwest = {version="0.11.16"} serde = {version="1.0.160", features=["derive", "serde_derive"]} @@ -28,3 +27,12 @@ lenient_semver = { version = "0.4.2", features = [ "version_semver"] } semver = "1.0.17" tokio = { version = "1", features = ["macros", "rt-multi-thread"] } futures = "0.3.28" +sqlx = { version = "0.7", features = [ "runtime-tokio", "sqlite", "chrono" ] } +dirs = "5.0.1" +anyhow = "1.0.75" +chumsky = "=1.0.0-alpha.3" +async-trait = "0.1.74" + +[dependencies.xxhash-rust] +version = "0.8.5" +features = ["xxh3"] \ No newline at end of file diff --git a/README.md b/README.md index 2490fd7..799e1ed 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ - + diff --git a/assets/2pyscan-repository.png b/assets/2pyscan-repository.png new file mode 100644 index 0000000..29d859e Binary files /dev/null and b/assets/2pyscan-repository.png differ diff --git a/assets/pyscan-repository.png b/assets/pyscan-repository.png deleted file mode 100644 index 28bf623..0000000 Binary files a/assets/pyscan-repository.png and /dev/null differ diff --git a/src/display/mod.rs b/src/display/mod.rs index d9686bc..ea9ee1a 100644 --- a/src/display/mod.rs +++ b/src/display/mod.rs @@ -79,7 +79,7 @@ pub fn display_queried( .as_str(), ); } // display the safe deps - let _ = display_summary(&collected); + let _ = display_summary(collected); } pub fn display_summary(collected: &Vec) -> io::Result<()> { diff --git a/src/main.rs b/src/main.rs index e5dcb32..07e3889 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,19 +9,23 @@ mod parser; mod scanner; mod docker; mod display; +mod map; +mod store; +mod pep_508; use std::env; use tokio::task; -use crate::{utils::get_version, parser::structs::{Dependency, VersionStatus}}; +use crate::{utils::get_version, parser::structs::{Dependency, VersionStatus}, store::{queries::PyscanData, paths::{populate_data_dir, pyscan_init_jobs}}}; +use store::paths::{PYSCAN_ROOT, populate_project_dir}; #[derive(Parser, Debug)] -#[command(author="aswinnnn",version="0.1.6",about="python dependency vulnerability scanner.\n\ndo 'pyscan [subcommand] --help' for specific help.")] +#[command(author="aswinnnn",version="0.1.6",about="python dependency vulnerability scanner.\nrun 'pyscan' to do a scan.\n\ndo 'pyscan [subcommand] --help' for specific help.")] struct Cli { /// path to source. (default: current directory) #[arg(long,short,default_value=None,value_name="DIRECTORY")] dir: Option, - /// export the result to a desired format. [json] + /// export the result to a desired format. [only json for now] #[arg(long,short, required=false, value_name="FILENAME")] output: Option, @@ -50,7 +54,7 @@ struct Cli { #[arg(long, required=false,action=clap::ArgAction::SetTrue)] pypi: bool, - /// turns off the caching of pip packages at the starting of execution. + /// turns off the caching of pip packages at starting. #[arg(long="cache-off", required=false,action=clap::ArgAction::SetTrue)] cache_off: bool, @@ -58,6 +62,10 @@ struct Cli { #[derive(Subcommand, Debug, Clone)] enum SubCommand { + + /// initialize a pyscan data store and enable persistent analysis. [required for advanced features] + Init, + /// query for a single python package Package { /// name of the package @@ -68,6 +76,7 @@ enum SubCommand { #[arg(long, short, default_value=None)] version: Option }, + /// scan inside a docker image Docker { @@ -122,6 +131,24 @@ async fn main() { docker::list_files_in_docker_image(name, path.to_path_buf()).await .expect("Error in scanning files from Docker image."); exit(0) + }, + Some(SubCommand::Init) => { + // unwrapping the static forces an initialization since its lazy. + // if unwrapping fails it tells the user it could not initialize. + + let r = PYSCAN_ROOT.clone().unwrap_or_else( |()|{ + let cwd = env::current_dir(); + if let Ok(path) = cwd { + eprintln!("Could not initialize a .pyscan directory at {}", path.display()); exit(1) + } + else { + eprintln!("Pyscan encountered a problem identifying your current working directory.\n Report this at github.com/aswinnnn/pyscan/issues"); exit(1); + } + }); + let _ = pyscan_init_jobs(&r).await.map_err(|e| {eprintln!("error: {}", e)}); + + println!("Initialized persistent vigilance at {}\nYou can now run 'pyscan' or 'pyscan map' to start gathering data. See 'pyscan help' for more info.", r.display()); exit(0) + } None => () } diff --git a/src/map/mod.rs b/src/map/mod.rs new file mode 100644 index 0000000..91865f7 --- /dev/null +++ b/src/map/mod.rs @@ -0,0 +1,2 @@ +mod structs; +mod parser; \ No newline at end of file diff --git a/src/map/parser.rs b/src/map/parser.rs new file mode 100644 index 0000000..9cbd7a9 --- /dev/null +++ b/src/map/parser.rs @@ -0,0 +1,18 @@ +use lazy_static::lazy_static; +use regex::Regex; + +lazy_static! { + static ref REQUIRES_REGEX: Regex = Regex::new(r"Requires: (.*)").unwrap(); +} + +fn extract_dependencies(pip_show_output: &str) -> Vec { + if let Some(captures) = REQUIRES_REGEX.captures(pip_show_output) { + if let Some(deps) = captures.get(1) { + // Split the dependencies by a comma and trim whitespace + let deps_str = deps.as_str(); + return deps_str.split(',').map(|s| s.trim().to_string()).collect(); + } + } + + Vec::new() // Return an empty vector if "Requires:" line is not found or empty +} diff --git a/src/map/structs.rs b/src/map/structs.rs new file mode 100644 index 0000000..5e54b7a --- /dev/null +++ b/src/map/structs.rs @@ -0,0 +1,36 @@ +struct DependencyGraph { + inner: Vec +} + +struct DependencyNode { + inner: String, + version: String, + node: Vec +} + +impl DependencyGraph { + // ... your existing methods ... + + fn display_ascii_tree(&self) { + self.display_ascii_tree_recursive(&self.inner, &mut " ".to_string(), self.inner.len() <= 1); + } + + fn display_ascii_tree_recursive(&self, nodes: &Vec, pad: &mut String, is_child: bool) { + if nodes.is_empty() { + return; + } + + let parent = "|->"; + let mut new_pad = pad.clone(); + if is_child { + new_pad.push_str(". "); + } + + for (i, node) in nodes.iter().enumerate() { + let is_last = i == nodes.len() - 1; + let connector = if is_last { "> " } else { "^-- " }; + println!("{}{}{} {}", new_pad, if i == 0 { parent } else { connector }, node.inner, node.version); + self.display_ascii_tree_recursive(&node.node, &mut new_pad, true); // Pass true for is_child + } + } +} diff --git a/src/parser/extractor.rs b/src/parser/extractor.rs index 97a1e11..6393142 100644 --- a/src/parser/extractor.rs +++ b/src/parser/extractor.rs @@ -1,17 +1,13 @@ -use std::process::exit; - /// for the parser module, extractor.rs is the backbone of all parsing /// it takes a String and a mutable reference to a Vec. /// String is the contents of a source file, while the mut ref vector will /// be used to collect the dependencies that we have extracted from the contents. +use std::process::exit; use super::structs::{Dependency, VersionStatus}; - use lazy_static::lazy_static; -use pep_508::{self, Spec}; +use crate::pep_508; +use crate::pep_508::Spec; use regex::Regex; - - - use toml::{de::Error, Value}; pub fn extract_imports_python(text: String, imp: &mut Vec) { @@ -146,7 +142,7 @@ pub fn extract_imports_setup_py(setup_py_content: &str, imp: &mut Vec res.pyproject_found { @@ -183,6 +185,7 @@ async fn find_pyproject_imports(f: &Vec) { } // println!("{:?}", imports.clone()); // cons.clear_last_lines(1).unwrap(); + // --- pass the dependencies to the scanner/api --- scanner::start(imports).await.unwrap(); } diff --git a/src/parser/structs.rs b/src/parser/structs.rs index afc9275..054d783 100644 --- a/src/parser/structs.rs +++ b/src/parser/structs.rs @@ -1,14 +1,9 @@ use console::style; use std::{ffi::OsString, process::exit}; - +use crate::pep_508; use crate::{scanner::models::Query, utils, ARGS}; - use super::scanner::models::Vulnerability; -// struct Python; -// struct Requirements; -// struct Pyproject; - #[derive(Debug, PartialEq, Eq, Clone)] pub enum FileTypes { Python, @@ -97,16 +92,16 @@ impl Dependency { } #[derive(Debug, Clone)] +/// pyscan may get version info from a lot of places. This keeps it in check. pub struct VersionStatus { - // pyscan may get version info from a lot of places. This keeps it in check. pub pypi: bool, pub pip: bool, pub source: bool, } /// implementation for VersionStatus which can get return versions while updating the status, also pick the one decided via arguments, a nice abstraction really. +/// retreives versions from pip and pypi.org in (pip, pypi) format. impl VersionStatus { - /// retreives versions from pip and pypi.org in (pip, pypi) format. pub async fn _full_check(&mut self, name: &str) -> (String, String) { let pip = utils::get_python_package_version(name); let pip_v = if let Err(e) = pip { diff --git a/src/pep_508/macros.rs b/src/pep_508/macros.rs new file mode 100644 index 0000000..69f9e65 --- /dev/null +++ b/src/pep_508/macros.rs @@ -0,0 +1,7 @@ +macro_rules! set { + ($($tt:tt)+) => { + ::chumsky::primitive::any().filter(|c| matches!(c, $($tt)+)) + }; +} + +pub(crate) use set; diff --git a/src/pep_508/mod.rs b/src/pep_508/mod.rs new file mode 100644 index 0000000..0425b02 --- /dev/null +++ b/src/pep_508/mod.rs @@ -0,0 +1,290 @@ +//! Python dependency parser for [PEP 508](https://peps.python.org/pep-0508) +//! +//! ``` +//! # use pep_508::*; +//! let dep = "requests[security, socks] <= 2.28.1, == 2.28.*; python_version > '3.7' and extra == 'http'"; +//! let parsed = parse(dep).unwrap(); +//! let expected = Dependency { +//! name: "requests", +//! extras: vec!["security", "socks"], +//! spec: Some(Spec::Version(vec![ +//! VersionSpec { +//! comparator: Comparator::Le, +//! version: "2.28.1", +//! }, +//! VersionSpec { +//! comparator: Comparator::Eq, +//! version: "2.28.*", +//! }, +//! ])), +//! marker: Some(Marker::And( +//! Box::new(Marker::Operator( +//! Variable::PythonVersion, +//! Operator::Comparator(Comparator::Gt), +//! Variable::String("3.7"), +//! )), +//! Box::new(Marker::Operator( +//! Variable::Extra, +//! Operator::Comparator(Comparator::Eq), +//! Variable::String("http"), +//! )), +//! )), +//! }; +//! assert_eq!(parsed, expected); +//! ``` +mod macros; +mod url; + +use chumsky::{ + error::Error, + extra::Full, + prelude::Simple, + primitive::{any, choice, empty, end, group, just}, + recursive::recursive, + IterParser, Parser, +}; + +use macros::set; + +/// Python dependency specified by [PEP 508](https://peps.python.org/pep-0508) +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Dependency<'a> { + /// Name of the dependency + pub name: &'a str, + /// Extras for the dependency, things that go inside `[]` + pub extras: Vec<&'a str>, + /// Version specification or URL + pub spec: Option>, + /// Environment markers, conditions that go after `;` + pub marker: Option>, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum Spec<'a> { + /// `foo @ https://example.com` + Url(&'a str), + /// `foo >= 0.1.0, < 0.2.0` + Version(Vec>), +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct VersionSpec<'a> { + pub comparator: Comparator, + pub version: &'a str, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum Marker<'a> { + And(Box>, Box>), + Or(Box>, Box>), + Operator(Variable<'a>, Operator, Variable<'a>), +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum Variable<'a> { + PythonVersion, + PythonFullVersion, + OsName, + SysPlatform, + PlatformRelease, + PlatformSystem, + PlatformVersion, + PlatformMachine, + PlatformPythonImplementation, + ImplementationName, + ImplementationVersion, + Extra, + String(&'a str), +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum Operator { + Comparator(Comparator), + /// `in` + In, + /// `not in` + NotIn, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum Comparator { + /// `foo < '0.1.0'` + Lt, + /// `foo < '0.1.0'` + Le, + /// `foo != '0.1.0'` + Ne, + /// `foo == '0.1.0'` + Eq, + /// `foo >= '0.1.0'` + Ge, + /// `foo > '0.1.0'` + Gt, + /// `foo ~= '0.1.0'` + Cp, + /// `foo === '0.1.0'` + Ae, +} + +/// Parse a [PEP 508](https://peps.python.org/pep-0508) string into a [Dependency] +/// ``` +/// # use pep_508::parse; +/// assert_eq!(parse("requests >= 2").unwrap().name, "requests"); +/// assert_eq!(parse("numpy").unwrap().name, "numpy"); +/// ``` +pub fn parse(dependency: &str) -> Result>> { + parser().then_ignore(end()).parse(dependency).into_result() +} + +/// Create a [chumsky](https://docs.rs/chumsky) parser, +/// allows more customization than [parse] +pub fn parser<'a, E: Error<'a, &'a str> + 'a>( +) -> impl Parser<'a, &'a str, Dependency<'a>, Full> { + let ws = set!(' ' | '\t').repeated().ignored(); + let ident = any() + .filter(char::is_ascii_alphanumeric) + .then( + set!('-' | '_' | '.') + .or_not() + .then(any().filter(char::is_ascii_alphanumeric)) + .repeated(), + ) + .slice(); + + let cmp = choice(( + just("===").to(Comparator::Ae), + just("<=").to(Comparator::Le), + just("!=").to(Comparator::Ne), + just("==").to(Comparator::Eq), + just(">=").to(Comparator::Ge), + just("~=").to(Comparator::Cp), + just('<').to(Comparator::Lt), + just('>').to(Comparator::Gt), + )); + + let version_spec = cmp + .then_ignore(ws) + .then( + set!( + 'A' ..= 'Z' | 'a' ..= 'z' | '0' ..= '9' | '-' | '_' | '.' | '*' | '+' | '!' + ) + .repeated() + .at_least(1) + .slice(), + ) + .map(|(comparator, version)| VersionSpec { + comparator, + version, + }) + .then_ignore(ws) + .separated_by(just(',').ignore_then(ws)) + .at_least(1) + .collect(); + + group(( + ws.ignore_then(ident).then_ignore(ws), + ident + .then_ignore(ws) + .separated_by(just(',').ignore_then(ws)) + .at_least(1) + .collect() + .delimited_by(just('[').ignore_then(ws), just(']')) + .then_ignore(ws) + .or(empty().map(|_| Vec::new())), + just('@') + .ignore_then(ws) + .ignore_then(url::parser()) + .map(Spec::Url) + .or(version_spec + .delimited_by(just('(').then_ignore(ws), just(')')) + .or(version_spec) + .map(Spec::Version)) + .then_ignore(ws) + .or_not(), + just(';') + .ignore_then(ws) + .ignore_then(recursive(|marker_or| { + macro_rules! c { + () => { + ' ' | '\t' | 'A' ..= 'Z' | 'a' ..= 'z' | '0' ..= '9' | '(' | ')' | '.' | + '{' | '}' | '-' | '_' | '*' | '#' | ':' | ';' | ',' | '/' | '?' | '[' | + ']' | '!' | '~' | '`' | '@' | '$' | '%' | '^' | '&' | '=' | '+' | '|' | + '<' | '>' + }; + } + + let marker_var = choice(( + just('\'') + .ignore_then(set!(c!() | '"').repeated().slice()) + .then_ignore(just('\'')) + .map(Variable::String), + just('"') + .ignore_then(set!(c!() | '\'').repeated().slice()) + .then_ignore(just('"')) + .map(Variable::String), + just("python_version").to(Variable::PythonVersion), + just("python_full_version").to(Variable::PythonFullVersion), + just("os_name").to(Variable::OsName), + just("sys_platform").to(Variable::SysPlatform), + just("platform_release").to(Variable::PlatformRelease), + just("platform_system").to(Variable::PlatformSystem), + just("platform_version").to(Variable::PlatformVersion), + just("platform_machine").to(Variable::PlatformMachine), + just("platform_python_implementation") + .to(Variable::PlatformPythonImplementation), + just("implementation_name").to(Variable::ImplementationName), + just("implementation_version").to(Variable::ImplementationVersion), + just("extra").to(Variable::Extra), + )); + + let marker_expr = group(( + marker_var.clone().then_ignore(ws), + cmp.map(Operator::Comparator) + .or(just("in").to(Operator::In).or(just("not") + .ignore_then(set!(' ' | '\t').repeated().at_least(1)) + .ignore_then(just("in")) + .to(Operator::NotIn))) + .then_ignore(ws), + marker_var, + )) + .map(|(lhs, op, rhs)| Marker::Operator(lhs, op, rhs)) + .or(marker_or + .then_ignore(ws) + .delimited_by(just('(').then_ignore(ws), just(')'))); + + let marker_and = marker_expr + .clone() + .then( + ws.ignore_then(just("and")) + .ignore_then(ws) + .ignore_then(marker_expr) + .or_not(), + ) + .map(|(lhs, rhs)| match rhs { + Some(rhs) => Marker::And(Box::new(lhs), Box::new(rhs)), + None => lhs, + }); + + marker_and + .clone() + .then( + ws.ignore_then(just("or")) + .ignore_then(ws) + .ignore_then(marker_and) + .or_not(), + ) + .map(|(lhs, rhs)| match rhs { + Some(rhs) => Marker::Or(Box::new(lhs), Box::new(rhs)), + None => lhs, + }) + })) + .or_not(), + )) + .then_ignore(ws) + .map(|(name, extras, spec, marker)| Dependency { + name, + extras, + spec, + marker, + }) +} diff --git a/src/pep_508/url.rs b/src/pep_508/url.rs new file mode 100644 index 0000000..15137df --- /dev/null +++ b/src/pep_508/url.rs @@ -0,0 +1,205 @@ +use chumsky::{ + error::Error, + extra::Full, + primitive::{any, choice, group, just}, + Parser, +}; + +use super::macros::set; + +macro_rules! c { + () => { + 'A' ..= 'Z' | 'a' ..= 'z' | '0' ..= '9' | '-' | '.' | '_' | '~' | + '!' | '$' | '&' | '\'' | '(' | ')' | '*' | '+' | ',' | ';' | '=' + }; +} + +pub(crate) fn parser<'a, E: Error<'a, &'a str> + 'a>( +) -> impl Parser<'a, &'a str, &'a str, Full> { + let c = set!(c!()); + let digit = any().filter(char::is_ascii_digit); + let hex = any().filter(char::is_ascii_hexdigit); + let percent = just('%').then_ignore(hex.repeated().exactly(2).rewind()); + let reg = percent.or(c); + let pchar = percent.or(set!(c!() | ':' | '@')); + + let octet = choice(( + group((just('1'), digit, digit)).ignored(), + just('2') + .then( + set!('0' ..= '4') + .then(digit) + .or(just('5').then(set!('0' ..= '5'))), + ) + .ignored(), + set!('1' ..= '9').then(digit).ignored(), + digit.map(|c| vec![c]).ignored(), + )); + let ipv4 = group((octet, just('.'), octet, just('.'), octet, just('.'), octet)).ignored(); + let h16 = hex.repeated().at_least(1).at_most(4); + let h16r = just(':').then(h16).repeated(); + let ls32 = group((h16, just(':'), h16)).ignored().or(ipv4); + + let segments = just('/').then(pchar.repeated()).repeated(); + let frag = percent.or(set!(c!() | ':' | '@' | '/' | '?')).repeated(); + let frags = just('?') + .then(frag) + .or_not() + .then(just('#').then(frag).or_not()); + + let path = just('/') + .then( + group(( + just('/'), + percent + .or(set!(c!() | ':')) + .repeated() + .then(just('@')) + .or_not(), + choice(( + just('[') + .then(choice(( + group(( + just('v'), + hex.repeated().at_least(1), + just('.'), + set!(c!() | ':').repeated().at_least(1), + )) + .ignored(), + h16.then(just(':')) + .repeated() + .exactly(6) + .then(ls32) + .ignored(), + group((just("::"), h16r.exactly(5), ls32)).ignored(), + group((h16.or_not(), just("::"), h16r.exactly(4), ls32)).ignored(), + group(( + h16.then(just(':')).or_not().then(h16).or_not(), + just("::"), + h16r.exactly(3), + ls32, + )) + .ignored(), + group(( + h16.then(h16r.at_most(2)).or_not(), + just(':'), + just(':'), + h16r.exactly(2), + ls32, + )) + .ignored(), + group(( + h16.then(h16r.at_most(3)).or_not(), + just("::"), + h16, + just(':'), + ls32, + )) + .ignored(), + group((h16.then(h16r.at_most(4)).or_not(), just("::"), ls32)).ignored(), + group((h16.then(h16r.at_most(5)).or_not(), just("::"), h16)).ignored(), + h16.then(h16r.at_most(6)) + .or_not() + .then(just("::")) + .ignored(), + ))) + .then(just(']')) + .ignored(), + ipv4, + reg.repeated(), + )), + just(':').then(digit.repeated()).or_not(), + segments, + )) + .ignored() + .or(pchar + .repeated() + .at_least(1) + .then(segments) + .or_not() + .ignored()) + .or_not(), + ) + .ignored(); + + choice(( + group(( + any().filter(char::is_ascii_alphabetic), + set!('A' ..= 'Z' | 'a' ..= 'z' | '0' ..= '9' | '+' | '-' | '.').repeated(), + just(':'), + path.or(pchar.repeated().at_least(1).then(segments).ignored()), + )) + .ignored(), + path, + percent + .or(set!(c!() | '@')) + .repeated() + .at_least(1) + .then(segments) + .ignored(), + )) + .then(frags) + .slice() +} + +#[cfg(test)] +mod tests { + use chumsky::{prelude::Rich, primitive::end, Parser}; + + use super::parser; + + fn parse(s: &str) -> Result<&str, Vec>> { + parser().then_ignore(end()).parse(s).into_result() + } + + fn check(urls: impl IntoIterator>) { + for url in urls { + let url = url.as_ref(); + assert_eq!(url, parse(url).expect(url)); + } + } + + #[test] + fn basic() { + check([ + "https://github.com/figsoda/pep-508", + "https://crates.io/search?q=pep-508&sort=recent-downloads", + "http://127.0.0.1:8000?some=query#anchor", + "/relative/url?query=good", + "another/relative/url#this", + ]); + + assert!(parse("").is_err()); + assert!(parse("https://example.com ").is_err()); + } + + // examples from https://datatracker.ietf.org/doc/html/rfc3986.htm + #[test] + fn examples() { + check([ + "ftp://ftp.is.co.za/rfc/rfc1808.txt", + "http://www.ietf.org/rfc/rfc2396.txt", + "ldap://[2001:db8::7]/c=GB?objectClass?one", + "mailto:John.Doe@example.com", + "news:comp.infosystems.www.servers.unix", + "tel:+1-816-555-1212", + "telnet://192.0.2.16:80/", + "urn:oasis:names:specification:docbook:dtd:xml:4.1.2", + ]); + } + + #[test] + fn ipv6() { + check([ + "https://[::]", + "https://[::1]", + "https://[0:0:0:0:0:0:0:0]", + "https://[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]", + ]); + + assert!(parse("[::]").is_err()); + assert!(parse("https://[:::1]").is_err()); + assert!(parse("https://[ffff:ffff:ffff:ffff:ffff:ffff:ffff]").is_err()); + assert!(parse("https://[0:0:0:0:0:0:0:0:0]").is_err()); + } +} diff --git a/src/scanner/api.rs b/src/scanner/api.rs index efd0b76..a01c495 100644 --- a/src/scanner/api.rs +++ b/src/scanner/api.rs @@ -126,7 +126,7 @@ impl Osv { if let Ok(dir) = env::current_dir() { let r = fs::write(dir.join(filename), restext); if let Err(er) = r { - eprintln!("Could not write output to file: {}", er.to_string()); + eprintln!("Could not write output to file: {}", er); exit(1) } else { diff --git a/src/scanner/models.rs b/src/scanner/models.rs index c861438..e7cd2b7 100644 --- a/src/scanner/models.rs +++ b/src/scanner/models.rs @@ -7,7 +7,7 @@ use serde::{Serialize, Deserialize}; use crate::parser::structs::ScannedDependency; - +/// contains a vector of vulnerabilities. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Vulnerability { #[serde(rename = "vulns")] diff --git a/src/store/cache.rs b/src/store/cache.rs new file mode 100644 index 0000000..ea3e704 --- /dev/null +++ b/src/store/cache.rs @@ -0,0 +1,54 @@ +use anyhow::Error; + +use chrono::{Utc, DateTime}; + +use super::queries::retrieve_root; + + +struct PipCache { + connected: bool, + last_update: DateTime, +} + +impl PipCache { + pub async fn create_table() -> Result<(),Error> { + let (conn, tx) = retrieve_root().await?; + + sqlx::query!(r#"CREATE TABLE IF NOT EXISTS pipcache ( + name TEXT NOT NULL, + version TEXT NOT NULL)"#).execute(&conn).await?; + tx.commit().await?; + Ok(()) + } + + pub async fn add(name: &str, version: &str) -> Result<(), Error> { + let (conn, tx) = retrieve_root().await?; + + sqlx::query!("INSERT INTO pipcache (name, version) VALUES (?, ?)", name, version).execute(&conn).await?; + + tx.commit().await?; + + Ok(()) + } + pub async fn update(name: &str, version: &str) -> Result<(), Error> { + let (conn, tx) = retrieve_root().await?; + + sqlx::query!("UPDATE pipcache SET name = ?, version = ?", name, version).execute(&conn).await?; + + tx.commit().await?; + + Ok(()) + } + + pub async fn remove(name: &str) -> Result<(), Error> { + let (conn, tx) = retrieve_root().await?; + + sqlx::query!("DELETE FROM pipcache WHERE name = ?;", name).execute(&conn).await?; + + tx.commit().await?; + + Ok(()) + } + +} + diff --git a/src/store/changes.rs b/src/store/changes.rs new file mode 100644 index 0000000..57ec35f --- /dev/null +++ b/src/store/changes.rs @@ -0,0 +1,12 @@ +//! Dependency changes! A way to track the evolution of the user's dependency configuration over time +//! in a config type agnostic way. They could switch from using a pyproject to a requirements.txt or setup.py +//! and shit should remain the same on how we track the information. +//! This involves:
+//! - encoding dependency info to a format and saving it to /changes
+//! - decoding it +//! +//! the change names are the hashes of the config file for easier change discovery.
+//! each change begins with the hash of the previous change but instead of delta encoding
+//! each file has the full dependency info of the time because i do not wanna do too many I/O calls +//! or deal with delta encoding for such a small project. + diff --git a/src/store/mod.rs b/src/store/mod.rs new file mode 100644 index 0000000..b3a2118 --- /dev/null +++ b/src/store/mod.rs @@ -0,0 +1,290 @@ +//! This module deals with data storage. Databases, Caches, Paths, etc. +pub mod cache; +pub mod paths; +pub mod queries; +pub mod changes; + +use std::collections::HashSet; + +use anyhow::Error; +use async_trait::async_trait; +use chrono::NaiveDate; +use queries::retrieve_root; +use sqlx::query; +use xxhash_rust::xxh3::xxh3_64; + +/// Represents the single, in-database Dependency row. NOT TO BE CONFUSED with the struct with same name in `parser::structs` +#[derive(PartialEq, Eq, Hash)] +pub struct Dependency { + pub name: String, + pub version: String, + pub added: NaiveDate, + pub updated: NaiveDate, +} +/// Represents the single, in-database Vulnerability. NOT TO BE CONFUSED with the struct with same name in `scanner::models` +pub struct Vulnerability { + pub cve: String, + pub name: String, +} +/// Represents the (many-to-many) relation between vulnerabilities and python packages. +pub struct VulnerabilityDependency { + pub cve: String, + pub package: String, +} + +// TODO HASH LOOKUP CHANGES +// use a dot (graphviz) file fs system + +/// in-database table for logging dependency changes. +pub struct DependencyChanges { + pub hash: u64, + pub name: String, + pub change: char, + pub timestamp: i64, +} + +/// Used to represent multiple dependencies. +/// Makes it easier to spot differences (between changes) and keeps it unique. +pub type Dependencies = HashSet; + +enum DatabaseTable { + Dependency(Dependency), + Vulnerability(Vulnerability), + VulnerabilityDependency(VulnerabilityDependency), + DependencyChanges(DependencyChanges), +} + +/// Database of a single individual project being watched by Pyscan. +/// All manipulations are done via functions. +/// To execute queries directly see `queries::retrieve_root` +struct ProjectDatabase; + +impl DatabaseOps for ProjectDatabase {} + +/// Database operations for different tables. +/// - Used by `ProjectDatabase` struct. +/// - Makes it so that its easy to update different tables just by passing structs. +#[async_trait] +trait DatabaseOps { + async fn insert(d: DatabaseTable) -> Result<(), Error> { + let (conn, tx) = retrieve_root().await?; + match d { + DatabaseTable::Dependency(dep) => { + query!( + " + INSERT INTO Dependency (name, version, added, updated) + VALUES (?,?,?,?); + ", + dep.name, + dep.version, + dep.added, + dep.updated + ) + .execute(&conn) + .await?; + tx.commit().await?; + Ok(()) + } + DatabaseTable::Vulnerability(v) => { + query!( + " + INSERT INTO Vulnerability (cve, name) + VALUES (?,?); + ", + v.cve, + v.name + ) + .execute(&conn) + .await?; + tx.commit().await?; + Ok(()) + } + DatabaseTable::VulnerabilityDependency(vd) => { + // have to use function here because the query macro doesnt agree with what + // i'm doing for some reason + sqlx::query( + r#" + INSERT INTO VulnerabilityDependency (vulnerability_cve, dependency_name) + VALUES (?,?); + "#, + ) + .bind(vd.cve) + .bind(vd.package) + .execute(&conn) + .await?; + tx.commit().await?; + Ok(()) + } + DatabaseTable::DependencyChanges(dc) => { + sqlx::query( + " + INSERT INTO DependencyChanges (hash, name, change, timestamp) + VALUES (?,?,?,?); + ", + ) + .bind(dc.hash.to_string()) + .bind(dc.name) + .bind(dc.change.to_string()) + .bind(dc.timestamp) + .execute(&conn) + .await?; + tx.commit().await?; + Ok(()) + } + } + } + + async fn update(d: DatabaseTable) -> Result<(), Error> { + let (conn, tx) = retrieve_root().await?; + match d { + DatabaseTable::Dependency(dep) => { + query!(" + UPDATE Dependency SET name = ?, version = ?, added = ?, updated = ? + WHERE name = ?; + ", dep.name, dep.version, dep.added, dep.updated, dep.name).execute(&conn).await?; + tx.commit().await?; + Ok(()) + }, + DatabaseTable::Vulnerability(v) => { + Err(Error::msg(format!("There is no reason to update the Vuln table. Rows should either be removed or created upon discovering and discarding Vulnerabilities.\nAn update attempt was made:\n {}", v))) + } + DatabaseTable::VulnerabilityDependency(vd) => { + Err(Error::msg(format!("There is no reason to update the VD table. Rows should either be removed or created upon discovering and discarding vulns in packages.\nAn update attempt was made with this row:\n {}", vd))) + }, + DatabaseTable::DependencyChanges(dc) => { + Err(Error::msg(format!("Why would you try to update a row in a table that tracks changes? Its a logger, there's no need to do changes, only insertion and deletion.\nAn update attempt was made with this row:\n {}", dc))) + } + } + } + + async fn delete(d: DatabaseTable) -> Result<(), Error> { + let (conn, tx) = retrieve_root().await?; + match d { + DatabaseTable::Dependency(dep) => { + sqlx::query( + r#" + DELETE FROM VulnerabilityDependency + WHERE dependency_name = ?; + DELETE FROM Dependency + WHERE name = ?; + "#, + ) + .bind(&dep.name) + .bind(&dep.name) + .execute(&conn) + .await?; + tx.commit().await?; + Ok(()) + } + DatabaseTable::Vulnerability(v) => { + sqlx::query( + r#" + DELETE FROM VulnerabilityDependency + WHERE vulnerability_cve = ?; + DELETE FROM Vulnerability + WHERE cve = ?; + "#, + ) + .bind(v.cve) + .execute(&conn) + .await?; + tx.commit().await?; + Ok(()) + } + DatabaseTable::VulnerabilityDependency(vd) => { + sqlx::query( + r#" + DELETE FROM VulnerabilityDependency + WHERE vulnerability_cve = ? AND dependency_name = ?; + "#, + ) + .bind(vd.cve) + .bind(vd.package) + .execute(&conn) + .await?; + tx.commit().await?; + Ok(()) + } + DatabaseTable::DependencyChanges(dc) => { + sqlx::query( + r#" + DELETE FROM DependencyChanges + WHERE hash = ? AND name = ? AND change = ? AND timestamp = ?; + "#, + ) + .bind(dc.hash.to_string()) + .bind(dc.name) + .bind(dc.change.to_string()) + .bind(dc.timestamp) + .execute(&conn) + .await?; + tx.commit().await?; + Ok(()) + } + } + } +} + +/// Trait for representing changes in the configuration files. +#[async_trait] +pub trait Change { + /// returns `Ok(true)` if a change has been detected by doing a hash look-up + async fn has_changed(s: &str) -> anyhow::Result { + let (conn, tx) = retrieve_root().await?; + let shash = xxh3_64(s.as_bytes()).to_string(); + let r = sqlx::query!( + r#" + SELECT hash from DependencyChanges + WHERE hash = ?; + "#, + shash + ) + .fetch_optional(&conn) + .await?; + tx.commit().await?; + + if r.is_some() { // file hasnt been changed (or reverted) + Ok(true) + } else { + Ok(false) + } + } +} + +impl Change for DependencyChanges {} + +impl std::fmt::Display for Dependency { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + write!( + f, + "Dependency: {},\nVersion: {},\nAdded: {},\nUpdated: {}\n", + self.name, self.version, self.added, self.updated + ) + } +} + +impl std::fmt::Display for Vulnerability { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + write!(f, "CVE: {},\nName: {}\n", self.cve, self.name) + } +} + +impl std::fmt::Display for VulnerabilityDependency { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + write!( + f, + "Vulnerability {} was found in {}", + self.cve, self.package + ) + } +} + +impl std::fmt::Display for DependencyChanges { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + write!( + f, + "Dependency: {},\nChange: {},\nTimestamp: {},\nHash: {}\n", + self.name, self.change, self.timestamp, self.hash + ) + } +} diff --git a/src/store/paths.rs b/src/store/paths.rs new file mode 100644 index 0000000..8c41e55 --- /dev/null +++ b/src/store/paths.rs @@ -0,0 +1,345 @@ +//! Functions and statics concerning with paths and directories important for pyscan's functionality. +use super::queries::{retrieve_root, retrieve_home, Project, self, PyscanData}; +use anyhow::Error; +use once_cell::sync::Lazy; +use std::{ + env, + fs::{self, File, OpenOptions}, + io::Write, + path::PathBuf, + process::exit, +}; + +// contains data on all projects being watched across the user's system +pub static PYSCAN_HOME: Lazy> = Lazy::new(init_data_dir); + +// TODO ! : reinitialize if db already exists (add project to HOME db) + +// at the project's root directory after `pyscan init` +pub static PYSCAN_ROOT: Lazy> = Lazy::new(init_project_dir); + +fn init_data_dir() -> Result { + //! checks for a pyscan data directory (different to a project directory), otherwise creates one. + //! returns `Ok(path)` if exists or has been created, exits otherwise. + //! exits because: + //! - whatever needs this function MUST use the data directory, + //! which only gets made if the user has done `pyscan init` + //! - it would be troublesome if the user already had `pyscan init`ed + //! but couldn't find a data directory, which keeps track of the + //! projects being watched by pyscan. + let dir = dirs::data_dir(); + if let Some(d) = dir { + let path = d.join("pyscan"); + if let Err(e) = path.try_exists() { + eprintln!( + "There was an error while checking for pyscan's data in {}.\nerror: {}", + path.display(), + e + ); + exit(1) + } else { + // unwrapping should be fine since Err value is accounted for above. + if path.try_exists().unwrap() { + Ok(path) + } else { + let r = fs::create_dir(path.clone()); + if let Err(e) = r { + eprintln!("Pyscan failed to create a folder on your system's data directory.\ndirectory: {}\nerror: {}", d.display(), e); + exit(1) + } else { + Ok(path) + } + } + } + } else { + eprintln!("Pyscan failed to recognize a data directory for your OS. This rarely happens and should be reported at github.com/aswinnnn/pyscan/issues"); + exit(1) + } +} + +fn init_project_dir() -> Result { + //! Creates a project directory OR if it exists, returns its path. + //! This directory contains the sqlite db and most of the persistent stuff useful for an individual project's security. + //! - created on `pyscan init` and NOWHERE else + //! - its used inside a lazy static, which should be the main way of getting the project dir's path + //! - usage of the static should be done in a context where `pyscan init` has been confirmed to be run except for `pyscan init`. + let dir = env::current_dir(); + if let Ok(d) = dir { + let mut dpath = d.clone(); + if let Ok(Some(r)) = exists_check(&mut dpath) { + Ok(r) + } else { + let r = fs::create_dir(d.clone().join(".pyscan")); + if let Err(e) = r { + eprintln!("Pyscan failed to create a folder on the current directory.\ndirectory: {}\nerror: {}", d.display(), e); + exit(1) + } else { + Ok(d.join(".pyscan")) + } + } + } else { + eprintln!("Pyscan failed to get the current working directory. This should not happen and should be reported at github.com/aswinnnn/pyscan/issues"); + exit(1) + } +} + +pub async fn populate_project_dir() -> Result<(), Error> { + //! populates the .pyscan directory with a database and its tables. + //! this is only called on creation. + //! - creates .store file, creates tables + //! - adds .pyscan to gitignore + + let dbpath = PYSCAN_ROOT.clone().unwrap().join(".store"); + if let Ok(exists) = dbpath.try_exists() { + if !exists { + let r = fs::File::create(&dbpath); + if let Err(e) = r { + return Err(Error::msg(format!( + "failed to create database.\npath: {}\nerror: {}\n", + dbpath.display(), + e + ))); + } + } + } + + let (conn, tx) = retrieve_root().await?; + + sqlx::query!( + r#" + CREATE TABLE IF NOT EXISTS Vulnerability ( + cve TEXT PRIMARY KEY, + name TEXT NOT NULL + ); + "# + ) + .execute(&conn) + .await?; + + sqlx::query!( + r#" + CREATE TABLE IF NOT EXISTS Dependency ( + name TEXT PRIMARY KEY, + version TEXT NOT NULL, + added TEXT NOT NULL, + updated TEXT NOT NULL + ); + "# + ) + .execute(&conn) + .await?; + + sqlx::query!( + r#" + CREATE TABLE IF NOT EXISTS VulnerabilityDependency ( + vulnerability_cve TEXT NOT NULL, + dependency_name TEXT NOT NULL, + FOREIGN KEY (vulnerability_cve) REFERENCES Vulnerability(cve) ON DELETE CASCADE, + FOREIGN KEY (dependency_name) REFERENCES Dependency(name) ON DELETE CASCADE, + PRIMARY KEY (vulnerability_cve, dependency_name) + ); + "# + ) + .execute(&conn) + .await?; + + sqlx::query!( + r#" + CREATE TABLE IF NOT EXISTS DependencyChanges ( + hash TEXT NOT NULL, + name TEXT NOT NULL, + change CHAR(1) NOT NULL, + timestamp INTEGER NOT NULL + ); + "# + ) + .execute(&conn) + .await?; + tx.commit().await?; + + gitignore(); // add .pyscan to .gitignore + + Ok(()) +} + +pub async fn populate_data_dir() -> Result<(), Error> { + //! populates the pyscan directory (at user's data directory) with a database and its tables. + //! this is only called once, first time running pyscan. + //! - creates pdata (pyscan data) file, creates tables + + let dbpath = PYSCAN_HOME.clone().unwrap().join("pdata"); + if let Ok(exists) = dbpath.try_exists() { + if !exists { + let r = fs::File::create(&dbpath); + if let Err(e) = r { + return Err(Error::msg(format!( + "failed to create database.\npath: {}\nerror: {}\n", + dbpath.display(), + e + ))); + } + } + } + + let (conn, tx) = retrieve_home().await?; + + sqlx::query!( + r#" + CREATE TABLE IF NOT EXISTS Settings ( + key TEXT UNIQUE NOT NULL, + value TEXT NOT NULL + ); + "# + ) + .execute(&conn).await?; + + sqlx::query!( + r#" + CREATE TABLE IF NOT EXISTS ProjectInfo ( + id INTEGER PRIMARY_KEY AUTO_INCREMENT, + project_id TEXT NOT NULL, + path TEXT NOT NULL, + added TEXT NOT NULL + ); + "#) + .execute(&conn).await?; + tx.commit().await?; + Ok(()) +} + +/// perform checks to see if the .pyscan exists in a directory or its parent directory. +/// - checks cwd, its parent, and then its parent. +/// - `Ok(PathBuf)` => path exists +/// - `Ok(None)` => path does not exist +/// - `Err` => Error +fn exists_check(path: &mut PathBuf) -> Result, Error> { + // try_exists() guide: + // Ok(true) => exists + // Ok(false) => does not exist + // Err => Error + let mut depth: u8 = 0; + + loop { + if depth == 3 { + return Ok(None); + } + let checkpath = path.join(".pyscan"); + + if let Ok(r) = checkpath.try_exists() { + if r { + // path exists + return Ok(Some(checkpath)); + } else { + // doesnt exist + if let Some(p) = path.parent() { + // path has a parent + *path = p.to_path_buf(); // parent is checked next + depth += 1 + } + } + } else { + if let Err(e) = path.try_exists() { + return Err(Error::msg(format!( + "An error occurred while checking for .pyscan directory.\nerror: {}", + e + ))); + } + } + } +} + +fn gitignore() { + //! - add .pyscan to .gitignore + //! - checks cwd and its parent for one + let mut path = PYSCAN_ROOT.clone().unwrap(); + if path.pop() { + if let Ok(exists) = path.join(".gitignore").try_exists() { + if exists { + let f = OpenOptions::new() + .append(true) + .open(path.join(".gitignore")); + if let Err(e) = f { + eprintln!("There was a problem writing to .gitignore.\nerror: {}", e); + } else { + if let Ok(mut file) = f { + let b = b"\n.pyscan/"; + let r = file.write_all(b); + if let Err(e) = r { + eprintln!("{}", e) + } + } + } + } else { + // check parent's parent + if path.pop() { + if let Ok(exists) = path.join(".gitignore").try_exists() { + if exists { + let f = OpenOptions::new() + .append(true) + .open(path.join(".gitignore")); + if let Err(e) = f { + eprintln!( + "There was a problem writing to .gitignore.\nerror: {}", + e + ); + } else { + if let Ok(mut file) = f { + let b = b"\n.pyscan/"; + let r = file.write_all(b); + if let Err(e) = r { + eprintln!("{}", e) + } + } + } + } + } + } + } + } + } +} + +/// every project has a unique hashed identifier. This keeps the data +/// collected name and path agnostic. +async fn create_identifier(path: &PathBuf) -> anyhow::Result<()> { + // assuming the path is PYSCAN_ROOT (containing /.pyscan/) + + let hash = xxhash_rust::xxh3::xxh3_64(path.display().to_string().as_bytes()); + let identfile = path.join(".IDENT"); + + let tryexist = identfile.try_exists().map_err(|e| { + eprintln!("error: {}", e); exit(1) + }); + if let Ok(r) = tryexist { + if !r { + let p = Project::new(hash.to_string(), path.to_string_lossy().to_string(), chrono::Utc::now().to_rfc2822()); + PyscanData::projects_add(p).await?; + fs::write(identfile, hash.to_string())?; + Ok(()) + } + else { + // an .IDENT file already exists? User probably trying to re-init the file because of a path change. + // update/create the project info with the new path (after making sure the identifier exists in db) + + let identifier = fs::read_to_string(identfile)?; + + let foundproject = queries::PyscanData::project_by_id(identifier.trim()).await?; + let p = Project::new(foundproject.identifier, path.to_string_lossy().to_string(), foundproject.added); + let _ = PyscanData::projects_update(p).await.map_err(|e| {eprintln!("error: {}", e); exit(1)}); + Ok(()) + } + } + else { + Err(Error::msg("An unreachable error occurred. Open an issue at github.com/aswinnnn/pyscan")) + } +} + +/// important routines performed after initing a new project. +pub async fn pyscan_init_jobs(path: &PathBuf ) -> anyhow::Result<()> { + populate_project_dir().await?; // create if not exists the project db + populate_data_dir().await?; // create if not exists the data db + create_identifier(path).await?; // create/update the project info + + // i havent used join! because i do want these futures to execute in the above order. + Ok(()) +} \ No newline at end of file diff --git a/src/store/queries.rs b/src/store/queries.rs new file mode 100644 index 0000000..c029ec4 --- /dev/null +++ b/src/store/queries.rs @@ -0,0 +1,150 @@ +//! functions useful for querying the databases. +use std::collections::HashMap; +use std::collections::HashSet; +use std::hash::Hash; +use std::path::PathBuf; + +use anyhow::Error; +use chrono::NaiveDateTime; +use sqlx::Column; +use sqlx::Pool; +use sqlx::Row; +use sqlx::Sqlite; + +use sqlx::SqlitePool; +use super::paths::PYSCAN_HOME; +use super::paths::{PYSCAN_ROOT}; + + +pub async fn retrieve_root<'a>() -> Result<(Pool, sqlx::Transaction<'a, Sqlite>), Error> { + //! Begins a database connection to the db in .pyscan folder and returns the connection + //! and an open transaction. + //! it has a seperate function to make it easier to call and do transactions. + + let dburl = format!("sqlite://{}/.store", PYSCAN_ROOT.clone().unwrap().to_str().unwrap()); + let conn = SqlitePool::connect(&dburl).await?; + let tx: sqlx::Transaction<'_, Sqlite> = conn.begin().await?; + Ok((conn, tx)) +} + +pub async fn retrieve_home<'a>() -> Result<(Pool, sqlx::Transaction<'a, Sqlite>), Error> { + //! Begins a database connection to the HOME db in `{daata_directory}/pyscan` folder and returns the connection + //! and an open transaction. + //! it has a seperate function to make it easier to call and do transactions. + + let dburl = format!("sqlite://{}/pdata", PYSCAN_HOME.clone().unwrap().to_str().unwrap()); + let conn = SqlitePool::connect(&dburl).await?; + let tx: sqlx::Transaction<'_, Sqlite> = conn.begin().await?; + Ok((conn, tx)) +} +/// Represents project metadata stored in pyscan's home database. +#[derive(PartialEq, Eq, Hash)] +pub struct Project { + pub identifier: String, + pub path: String, + pub added: String +} + +impl Project { + pub fn new(ident: String, path: String, added: String) -> Project { + Project {identifier: ident, path, added: added} + } +} + +pub type Projects = HashSet; + +/// An abstraction over the pyscan home database. +/// this is just like the ProjectDatabase struct but for pyscan's main data directory +pub struct PyscanData; + +impl PyscanData { + pub async fn new() -> PyscanData { + PyscanData + } + pub async fn settings(&self) -> Result, Error> { + //! view global settings. + struct Setting {key: String, value: String} + let mut map: HashMap = HashMap::new(); + let (conn, tx) = retrieve_home().await?; + + let rows = sqlx::query_as!(Setting, r#" + SELECT key,value FROM Settings; + "#).fetch_all(&conn).await?; + tx.commit().await?; + + for row in rows { + map.insert(row.key, row.value); + } + + Ok(map) + } + + pub async fn update_settings(key: &str, value: &str) -> anyhow::Result<()> { + //! update or add a global setting. + let (conn, tx) = retrieve_home().await?; + + sqlx::query!(r#" + INSERT OR IGNORE INTO Settings (key,value) VALUES (?,?); + UPDATE Settings SET key = ?, value = ? + WHERE key = ?; + "#, + key,value,key,value,key + ).execute(&conn).await?; + tx.commit().await?; + + Ok(()) + } + pub async fn projects() -> anyhow::Result { + //! view all the projects' information. + let mut r = Projects::new(); + let (conn, tx) = retrieve_home().await?; + + let p = sqlx::query_as!(Project, r#" + SELECT project_id as identifier, path, added FROM ProjectInfo; + "#).fetch_all(&conn).await?; + + for project in p { + r.insert(project); + } + tx.commit().await?; + Ok(r) + + + } + + pub async fn project_by_id(identifier: &str) -> anyhow::Result { + //! view one of the projects' information by providing the project identifier. + let (conn, tx) = retrieve_home().await?; + + let p = sqlx::query_as!(Project, r#" + SELECT project_id as identifier, path, added FROM ProjectInfo + WHERE project_id = ?; + "#, identifier).fetch_one(&conn).await?; + tx.commit().await?; + Ok(p) + } + + pub async fn projects_add(project: Project) -> anyhow::Result<()> { + let (conn, tx) = retrieve_home().await?; + sqlx::query!(r#" + INSERT INTO ProjectInfo (project_id, path, added) + VALUES (?,?,?) + "#, project.identifier, project.path, project.added).execute(&conn).await?; + tx.commit().await?; + Ok(()) + } + pub async fn projects_update(project: Project) -> anyhow::Result<()> { + //! the only thing that can be updated in this table is path. + //! the others, one is a primary key and the other is a timestamp of when that project + //! was inited. + let (conn, tx) = retrieve_home().await?; + sqlx::query!(r#" + UPDATE ProjectInfo SET path = ? + WHERE project_id = ? + "#, project.path, project.identifier).execute(&conn).await?; + tx.commit().await?; + Ok(()) + } +} + + diff --git a/src/utils.rs b/src/utils.rs index a6fc2de..d1dc9fe 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,3 +1,4 @@ +//! provides common functionalities for pyscan. use chrono::{Timelike, Utc}; use reqwest::{ self, @@ -190,10 +191,10 @@ pub fn pip_list() -> io::Result> { let output = Command::new("pip") .arg("list") .output() - .map_err(|_| io::Error::new(ErrorKind::Other, "Failed to execute 'pip list' command. pyscan caches the dependencies from pip with versions to be faster and it could not run 'pip list'. You can turn this off via just using --cache-off [note: theres a chance pyscan might still fallback to using pip]"))?; + .map_err(|_| io::Error::new(ErrorKind::Other, "Failed to execute 'pip list' command.\npyscan caches the dependencies from pip with versions to be faster and it could not run 'pip list'.\nYou can turn this off via just using --cache-off\n[note: theres a chance pyscan might still fallback to using pip]"))?; let output_str = str::from_utf8(&output.stdout) - .map_err(|_| io::Error::new(ErrorKind::InvalidData, "Output from 'pip list' was not valid UTF-8. pyscan caches the dependencies from pip with versions to be faster and the output it recieved was not valid UTF-8. You can turn this off via just using --cache-off [note: theres a chance pyscan might still fallback to using pip]"))?; + .map_err(|_| io::Error::new(ErrorKind::InvalidData, "Output from 'pip list' was not valid UTF-8.\npyscan caches the dependencies from pip with versions to be faster and the output it recieved was not valid UTF-8.\nYou can turn this off via just using --cache-off\n[note: theres a chance pyscan might still fallback to using pip]"))?; let mut pip_list: HashMap = HashMap::new();