From 42e9f51ea51cbc26527f2951dc4bb839b3ae4ff9 Mon Sep 17 00:00:00 2001 From: Logan Markewich Date: Tue, 3 Feb 2026 20:49:17 -0600 Subject: [PATCH 1/5] Add --json mode --- Cargo.lock | 1662 ++++++++++++++---------------------- Cargo.toml | 2 +- src/ask/chat_agent.rs | 37 +- src/ask/responses_agent.rs | 59 +- src/ask/tools.rs | 18 +- src/bin/ask.rs | 42 +- src/bin/search.rs | 131 ++- src/bin/workspace.rs | 136 ++- src/json_mode.rs | 60 ++ src/lib.rs | 2 + 10 files changed, 1064 insertions(+), 1085 deletions(-) create mode 100644 src/json_mode.rs diff --git a/Cargo.lock b/Cargo.lock index 76564e44..987f60fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,17 +17,6 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" -[[package]] -name = "aes" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", -] - [[package]] name = "ahash" version = "0.8.12" @@ -144,6 +133,24 @@ version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + +[[package]] +name = "ar_archive_writer" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb93bbb63b9c227414f6eb3a0adfddca591a8ce1e9b60661bb08969b87e340b" +dependencies = [ + "object 0.37.3", +] + [[package]] name = "arc-swap" version = "1.7.1" @@ -393,15 +400,19 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.33" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93c1f86859c1af3d514fa19e8323147ff10ea98684e6c7b307912509f50e67b2" +checksum = "06575e6a9673580f52661c92107baabffbf41e2141373441cbcdc47cb733003c" dependencies = [ - "compression-codecs", - "compression-core", + "bzip2 0.5.2", + "flate2", "futures-core", + "memchr", "pin-project-lite", "tokio", + "xz2", + "zstd", + "zstd-safe", ] [[package]] @@ -506,354 +517,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" -[[package]] -name = "aws-config" -version = "1.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bc1b40fb26027769f16960d2f4a6bc20c4bb755d403e552c8c1a73af433c246" -dependencies = [ - "aws-credential-types", - "aws-runtime", - "aws-sdk-sso", - "aws-sdk-ssooidc", - "aws-sdk-sts", - "aws-smithy-async", - "aws-smithy-http", - "aws-smithy-json", - "aws-smithy-runtime", - "aws-smithy-runtime-api", - "aws-smithy-types", - "aws-types", - "bytes", - "fastrand", - "hex", - "http 1.3.1", - "ring", - "time", - "tokio", - "tracing", - "url", - "zeroize", -] - -[[package]] -name = "aws-credential-types" -version = "1.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d025db5d9f52cbc413b167136afb3d8aeea708c0d8884783cf6253be5e22f6f2" -dependencies = [ - "aws-smithy-async", - "aws-smithy-runtime-api", - "aws-smithy-types", - "zeroize", -] - -[[package]] -name = "aws-lc-rs" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b8ff6c09cd57b16da53641caa860168b88c172a5ee163b0288d3d6eea12786" -dependencies = [ - "aws-lc-sys", - "zeroize", -] - -[[package]] -name = "aws-lc-sys" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e44d16778acaf6a9ec9899b92cebd65580b83f685446bf2e1f5d3d732f99dcd" -dependencies = [ - "bindgen", - "cc", - "cmake", - "dunce", - "fs_extra", -] - -[[package]] -name = "aws-runtime" -version = "1.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c034a1bc1d70e16e7f4e4caf7e9f7693e4c9c24cd91cf17c2a0b21abaebc7c8b" -dependencies = [ - "aws-credential-types", - "aws-sigv4", - "aws-smithy-async", - "aws-smithy-http", - "aws-smithy-runtime", - "aws-smithy-runtime-api", - "aws-smithy-types", - "aws-types", - "bytes", - "fastrand", - "http 0.2.12", - "http-body 0.4.6", - "percent-encoding", - "pin-project-lite", - "tracing", - "uuid", -] - -[[package]] -name = "aws-sdk-sso" -version = "1.83.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "643cd43af212d2a1c4dedff6f044d7e1961e5d9e7cfe773d70f31d9842413886" -dependencies = [ - "aws-credential-types", - "aws-runtime", - "aws-smithy-async", - "aws-smithy-http", - "aws-smithy-json", - "aws-smithy-runtime", - "aws-smithy-runtime-api", - "aws-smithy-types", - "aws-types", - "bytes", - "fastrand", - "http 0.2.12", - "regex-lite", - "tracing", -] - -[[package]] -name = "aws-sdk-ssooidc" -version = "1.84.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20ec4a95bd48e0db7a424356a161f8d87bd6a4f0af37204775f0da03d9e39fc3" -dependencies = [ - "aws-credential-types", - "aws-runtime", - "aws-smithy-async", - "aws-smithy-http", - "aws-smithy-json", - "aws-smithy-runtime", - "aws-smithy-runtime-api", - "aws-smithy-types", - "aws-types", - "bytes", - "fastrand", - "http 0.2.12", - "regex-lite", - "tracing", -] - -[[package]] -name = "aws-sdk-sts" -version = "1.85.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "410309ad0df4606bc721aff0d89c3407682845453247213a0ccc5ff8801ee107" -dependencies = [ - "aws-credential-types", - "aws-runtime", - "aws-smithy-async", - "aws-smithy-http", - "aws-smithy-json", - "aws-smithy-query", - "aws-smithy-runtime", - "aws-smithy-runtime-api", - "aws-smithy-types", - "aws-smithy-xml", - "aws-types", - "fastrand", - "http 0.2.12", - "regex-lite", - "tracing", -] - -[[package]] -name = "aws-sigv4" -version = "1.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "084c34162187d39e3740cb635acd73c4e3a551a36146ad6fe8883c929c9f876c" -dependencies = [ - "aws-credential-types", - "aws-smithy-http", - "aws-smithy-runtime-api", - "aws-smithy-types", - "bytes", - "form_urlencoded", - "hex", - "hmac", - "http 0.2.12", - "http 1.3.1", - "percent-encoding", - "sha2", - "time", - "tracing", -] - -[[package]] -name = "aws-smithy-async" -version = "1.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e190749ea56f8c42bf15dd76c65e14f8f765233e6df9b0506d9d934ebef867c" -dependencies = [ - "futures-util", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "aws-smithy-http" -version = "0.62.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c4dacf2d38996cf729f55e7a762b30918229917eca115de45dfa8dfb97796c9" -dependencies = [ - "aws-smithy-runtime-api", - "aws-smithy-types", - "bytes", - "bytes-utils", - "futures-core", - "http 0.2.12", - "http 1.3.1", - "http-body 0.4.6", - "percent-encoding", - "pin-project-lite", - "pin-utils", - "tracing", -] - -[[package]] -name = "aws-smithy-http-client" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147e8eea63a40315d704b97bf9bc9b8c1402ae94f89d5ad6f7550d963309da1b" -dependencies = [ - "aws-smithy-async", - "aws-smithy-runtime-api", - "aws-smithy-types", - "h2", - "http 1.3.1", - "hyper", - "hyper-rustls", - "hyper-util", - "pin-project-lite", - "rustls", - "rustls-native-certs", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "tower", - "tracing", -] - -[[package]] -name = "aws-smithy-json" -version = "0.61.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaa31b350998e703e9826b2104dd6f63be0508666e1aba88137af060e8944047" -dependencies = [ - "aws-smithy-types", -] - -[[package]] -name = "aws-smithy-observability" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9364d5989ac4dd918e5cc4c4bdcc61c9be17dcd2586ea7f69e348fc7c6cab393" -dependencies = [ - "aws-smithy-runtime-api", -] - -[[package]] -name = "aws-smithy-query" -version = "0.60.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2fbd61ceb3fe8a1cb7352e42689cec5335833cd9f94103a61e98f9bb61c64bb" -dependencies = [ - "aws-smithy-types", - "urlencoding", -] - -[[package]] -name = "aws-smithy-runtime" -version = "1.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fa63ad37685ceb7762fa4d73d06f1d5493feb88e3f27259b9ed277f4c01b185" -dependencies = [ - "aws-smithy-async", - "aws-smithy-http", - "aws-smithy-http-client", - "aws-smithy-observability", - "aws-smithy-runtime-api", - "aws-smithy-types", - "bytes", - "fastrand", - "http 0.2.12", - "http 1.3.1", - "http-body 0.4.6", - "http-body 1.0.1", - "pin-project-lite", - "pin-utils", - "tokio", - "tracing", -] - -[[package]] -name = "aws-smithy-runtime-api" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07f5e0fc8a6b3f2303f331b94504bbf754d85488f402d6f1dd7a6080f99afe56" -dependencies = [ - "aws-smithy-async", - "aws-smithy-types", - "bytes", - "http 0.2.12", - "http 1.3.1", - "pin-project-lite", - "tokio", - "tracing", - "zeroize", -] - -[[package]] -name = "aws-smithy-types" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d498595448e43de7f4296b7b7a18a8a02c61ec9349128c80a368f7c3b4ab11a8" -dependencies = [ - "base64-simd", - "bytes", - "bytes-utils", - "http 0.2.12", - "http 1.3.1", - "http-body 0.4.6", - "http-body 1.0.1", - "http-body-util", - "itoa", - "num-integer", - "pin-project-lite", - "pin-utils", - "ryu", - "serde", - "time", -] - -[[package]] -name = "aws-smithy-xml" -version = "0.60.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3db87b96cb1b16c024980f133968d52882ca0daaee3a086c6decc500f6c99728" -dependencies = [ - "xmlparser", -] - -[[package]] -name = "aws-types" -version = "1.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b069d19bf01e46298eaedd7c6f283fe565a59263e53eebec945f3e6398f42390" -dependencies = [ - "aws-credential-types", - "aws-smithy-async", - "aws-smithy-runtime-api", - "aws-smithy-types", - "rustc_version", - "tracing", -] - [[package]] name = "backoff" version = "0.4.0" @@ -868,17 +531,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "backon" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "592277618714fbcecda9a02ba7a8781f319d26532a88553bbacc77ba5d2b3a8d" -dependencies = [ - "fastrand", - "gloo-timers", - "tokio", -] - [[package]] name = "backtrace" version = "0.3.75" @@ -889,7 +541,7 @@ dependencies = [ "cfg-if", "libc", "miniz_oxide", - "object", + "object 0.36.7", "rustc-demangle", "windows-targets 0.52.6", ] @@ -906,22 +558,6 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" -[[package]] -name = "base64-simd" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" -dependencies = [ - "outref", - "vsimd", -] - -[[package]] -name = "base64ct" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" - [[package]] name = "bigdecimal" version = "0.4.8" @@ -935,26 +571,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "bindgen" -version = "0.72.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" -dependencies = [ - "bitflags", - "cexpr", - "clang-sys", - "itertools 0.13.0", - "log", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "syn 2.0.106", -] - [[package]] name = "bitflags" version = "2.9.3" @@ -1013,15 +629,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "block-padding" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" -dependencies = [ - "generic-array", -] - [[package]] name = "bon" version = "3.7.2" @@ -1104,31 +711,40 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" [[package]] -name = "bytes-utils" -version = "0.1.4" +name = "bzip2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dafe3a8757b027e2be6e4e5601ed563c55989fcf1546e933c66c8eb3a058d35" +checksum = "49ecfb22d906f800d4fe833b6282cf4dc1c298f5057ca0b5445e5c209735ca47" dependencies = [ - "bytes", - "either", + "bzip2-sys", ] [[package]] -name = "castaway" -version = "0.2.4" +name = "bzip2" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" +checksum = "f3a53fac24f34a81bc9954b5d6cfce0c21e18ec6959f44f56e8e90e4bb7c346c" dependencies = [ - "rustversion", + "libbz2-rs-sys", ] [[package]] -name = "cbc" -version = "0.1.2" +name = "bzip2-sys" +version = "0.1.13+1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" dependencies = [ - "cipher", + "cc", + "pkg-config", +] + +[[package]] +name = "castaway" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" +dependencies = [ + "rustversion", ] [[package]] @@ -1148,15 +764,6 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f4c707c6a209cbe82d10abd08e1ea8995e9ea937d2550646e02798948992be0" -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom 7.1.3", -] - [[package]] name = "cfg-if" version = "1.0.3" @@ -1194,27 +801,6 @@ dependencies = [ "phf", ] -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", -] - -[[package]] -name = "clang-sys" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" -dependencies = [ - "glob", - "libc", - "libloading", -] - [[package]] name = "clap" version = "4.5.45" @@ -1255,15 +841,6 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" -[[package]] -name = "cmake" -version = "0.1.54" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" -dependencies = [ - "cc", -] - [[package]] name = "colorchoice" version = "1.0.4" @@ -1296,23 +873,6 @@ dependencies = [ "static_assertions", ] -[[package]] -name = "compression-codecs" -version = "0.4.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "680dc087785c5230f8e8843e2e57ac7c1c90488b6a91b88caa265410568f441b" -dependencies = [ - "compression-core", - "flate2", - "memchr", -] - -[[package]] -name = "compression-core" -version = "0.4.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" - [[package]] name = "concurrent-queue" version = "2.5.0" @@ -1335,12 +895,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "const-oid" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" - [[package]] name = "const-random" version = "0.1.18" @@ -1392,15 +946,6 @@ dependencies = [ "libc", ] -[[package]] -name = "crc32c" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a47af21622d091a8f0fb295b88bc886ac74efcc613efc19f5d0b21de5c89e47" -dependencies = [ - "rustc_version", -] - [[package]] name = "crc32fast" version = "1.5.0" @@ -1594,6 +1139,7 @@ dependencies = [ "arrow-schema", "async-trait", "bytes", + "bzip2 0.6.1", "chrono", "datafusion-catalog", "datafusion-catalog-listing", @@ -1602,6 +1148,7 @@ dependencies = [ "datafusion-datasource", "datafusion-datasource-csv", "datafusion-datasource-json", + "datafusion-datasource-parquet", "datafusion-execution", "datafusion-expr", "datafusion-expr-common", @@ -1618,11 +1165,13 @@ dependencies = [ "datafusion-physical-plan", "datafusion-session", "datafusion-sql", + "flate2", "futures", "itertools 0.14.0", "log", "object_store", "parking_lot", + "parquet", "rand 0.9.2", "regex", "sqlparser", @@ -1630,6 +1179,8 @@ dependencies = [ "tokio", "url", "uuid", + "xz2", + "zstd", ] [[package]] @@ -1698,7 +1249,9 @@ dependencies = [ "libc", "log", "object_store", + "parquet", "paste", + "recursive", "sqlparser", "tokio", "web-time", @@ -1722,8 +1275,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7256c9cb27a78709dd42d0c80f0178494637209cac6e29d5c93edd09b6721b86" dependencies = [ "arrow", + "async-compression", "async-trait", "bytes", + "bzip2 0.6.1", "chrono", "datafusion-common", "datafusion-common-runtime", @@ -1734,14 +1289,20 @@ dependencies = [ "datafusion-physical-expr-common", "datafusion-physical-plan", "datafusion-session", + "flate2", "futures", "glob", "itertools 0.14.0", "log", "object_store", + "parquet", "rand 0.9.2", + "tempfile", "tokio", + "tokio-util", "url", + "xz2", + "zstd", ] [[package]] @@ -1794,6 +1355,39 @@ dependencies = [ "tokio", ] +[[package]] +name = "datafusion-datasource-parquet" +version = "50.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09e783c4c7d7faa1199af2df4761c68530634521b176a8d1331ddbc5a5c75133" +dependencies = [ + "arrow", + "async-trait", + "bytes", + "datafusion-catalog", + "datafusion-common", + "datafusion-common-runtime", + "datafusion-datasource", + "datafusion-execution", + "datafusion-expr", + "datafusion-functions-aggregate", + "datafusion-physical-expr", + "datafusion-physical-expr-adapter", + "datafusion-physical-expr-common", + "datafusion-physical-optimizer", + "datafusion-physical-plan", + "datafusion-pruning", + "datafusion-session", + "futures", + "itertools 0.14.0", + "log", + "object_store", + "parking_lot", + "parquet", + "rand 0.9.2", + "tokio", +] + [[package]] name = "datafusion-doc" version = "50.3.0" @@ -1837,6 +1431,7 @@ dependencies = [ "datafusion-physical-expr-common", "indexmap 2.11.1", "paste", + "recursive", "serde_json", "sqlparser", ] @@ -2009,6 +1604,7 @@ dependencies = [ "indexmap 2.11.1", "itertools 0.14.0", "log", + "recursive", "regex", "regex-syntax", ] @@ -2082,6 +1678,7 @@ dependencies = [ "datafusion-pruning", "itertools 0.14.0", "log", + "recursive", ] [[package]] @@ -2169,6 +1766,7 @@ dependencies = [ "datafusion-expr", "indexmap 2.11.1", "log", + "recursive", "regex", "sqlparser", ] @@ -2193,17 +1791,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "der" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" -dependencies = [ - "const-oid", - "pem-rfc7468", - "zeroize", -] - [[package]] name = "deranged" version = "0.5.3" @@ -2252,7 +1839,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", - "const-oid", "crypto-common", "subtle", ] @@ -2310,33 +1896,28 @@ dependencies = [ "syn 2.0.106", ] -[[package]] -name = "dlv-list" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" -dependencies = [ - "const-random", -] - [[package]] name = "downcast-rs" version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "117240f60069e65410b3ae1bb213295bd828f707b5bec6596a1afc8793ce0cbc" -[[package]] -name = "dunce" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" - [[package]] name = "dyn-clone" version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" +[[package]] +name = "earcutr" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79127ed59a85d7687c409e9978547cffb7dc79675355ed22da6b66fd5f6ead01" +dependencies = [ + "itertools 0.11.0", + "num-traits", +] + [[package]] name = "either" version = "1.15.0" @@ -2471,9 +2052,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" dependencies = [ "crc32fast", + "libz-rs-sys", "miniz_oxide", ] +[[package]] +name = "float_next_after" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bf7cc16383c4b8d58b9905a8509f02926ce3058053c056376248d958c9df1e8" + [[package]] name = "fnv" version = "1.0.7" @@ -2505,17 +2093,11 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "fs_extra" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" - [[package]] name = "fsst" -version = "0.39.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2475ce218217196b161b025598f77e2b405d5e729f7c37bfff145f5df00a41" +checksum = "5ffdff7a2d68d22afc0657eddde3e946371ce7cfe730a3f78a5ed44ea5b1cb2e" dependencies = [ "arrow-array", "rand 0.9.2", @@ -2655,6 +2237,128 @@ dependencies = [ "version_check", ] +[[package]] +name = "geo" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fc1a1678e54befc9b4bcab6cd43b8e7f834ae8ea121118b0fd8c42747675b4a" +dependencies = [ + "earcutr", + "float_next_after", + "geo-types", + "geographiclib-rs", + "i_overlay", + "log", + "num-traits", + "robust", + "rstar", + "spade", +] + +[[package]] +name = "geo-traits" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e7c353d12a704ccfab1ba8bfb1a7fe6cb18b665bf89d37f4f7890edcd260206" +dependencies = [ + "geo-types", +] + +[[package]] +name = "geo-types" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24f8647af4005fa11da47cd56252c6ef030be8fa97bdbf355e7dfb6348f0a82c" +dependencies = [ + "approx", + "num-traits", + "rayon", + "rstar", + "serde", +] + +[[package]] +name = "geoarrow-array" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d1884b17253d8572e88833c282fcbb442365e4ae5f9052ced2831608253436c" +dependencies = [ + "arrow-array", + "arrow-buffer", + "arrow-schema", + "geo-traits", + "geoarrow-schema", + "num-traits", + "wkb", + "wkt", +] + +[[package]] +name = "geoarrow-expr-geo" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a67d3b543bc3ebeffdc204b67d69b8f9fcd33d76269ddd4a4618df99f053a934" +dependencies = [ + "arrow-array", + "arrow-buffer", + "geo", + "geo-traits", + "geoarrow-array", + "geoarrow-schema", +] + +[[package]] +name = "geoarrow-schema" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02f1b18b1c9a44ecd72be02e53d6e63bbccfdc8d1765206226af227327e2be6e" +dependencies = [ + "arrow-schema", + "geo-traits", + "serde", + "serde_json", + "thiserror 1.0.69", +] + +[[package]] +name = "geodatafusion" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83d676b8d8b5f391ab4270ba31e9b599ee2c3d780405a38e272a0a7565ea189c" +dependencies = [ + "arrow-arith", + "arrow-array", + "arrow-schema", + "datafusion", + "geo", + "geo-traits", + "geoarrow-array", + "geoarrow-expr-geo", + "geoarrow-schema", + "geohash", + "thiserror 1.0.69", + "wkt", +] + +[[package]] +name = "geographiclib-rs" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f611040a2bb37eaa29a78a128d1e92a378a03e0b6e66ae27398d42b1ba9a7841" +dependencies = [ + "libm", +] + +[[package]] +name = "geohash" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fb94b1a65401d6cbf22958a9040aa364812c26674f841bee538b12c135db1e6" +dependencies = [ + "geo-types", + "libm", +] + [[package]] name = "getrandom" version = "0.2.16" @@ -2707,18 +2411,6 @@ dependencies = [ "regex-syntax", ] -[[package]] -name = "gloo-timers" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] - [[package]] name = "grep" version = "0.3.2" @@ -2809,7 +2501,7 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http 1.3.1", + "http", "indexmap 2.11.1", "slab", "tokio", @@ -2828,6 +2520,15 @@ dependencies = [ "num-traits", ] +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -2861,6 +2562,16 @@ version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + [[package]] name = "heck" version = "0.5.0" @@ -2886,34 +2597,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "629d8f3bbeda9d148036d6b0de0a3ab947abd08ce90626327fc3547a49d59d97" dependencies = [ "dirs 6.0.0", - "http 1.3.1", + "http", "indicatif", "libc", "log", "rand 0.9.2", "serde", - "serde_json", - "thiserror 2.0.16", - "ureq", - "windows-sys 0.60.2", -] - -[[package]] -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.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" -dependencies = [ - "windows-sys 0.59.0", + "serde_json", + "thiserror 2.0.16", + "ureq", + "windows-sys 0.60.2", ] [[package]] @@ -2922,17 +2615,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9025058dae765dee5070ec375f591e2ba14638c63feff74f13805a72e523163" -[[package]] -name = "http" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - [[package]] name = "http" version = "1.3.1" @@ -2944,17 +2626,6 @@ dependencies = [ "itoa", ] -[[package]] -name = "http-body" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" -dependencies = [ - "bytes", - "http 0.2.12", - "pin-project-lite", -] - [[package]] name = "http-body" version = "1.0.1" @@ -2962,7 +2633,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.3.1", + "http", ] [[package]] @@ -2973,8 +2644,8 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http 1.3.1", - "http-body 1.0.1", + "http", + "http-body", "pin-project-lite", ] @@ -3001,8 +2672,8 @@ dependencies = [ "futures-channel", "futures-core", "h2", - "http 1.3.1", - "http-body 1.0.1", + "http", + "http-body", "httparse", "itoa", "pin-project-lite", @@ -3018,7 +2689,7 @@ version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ - "http 1.3.1", + "http", "hyper", "hyper-util", "rustls", @@ -3041,8 +2712,8 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "http 1.3.1", - "http-body 1.0.1", + "http", + "http-body", "hyper", "ipnet", "libc", @@ -3063,6 +2734,49 @@ dependencies = [ "serde", ] +[[package]] +name = "i_float" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "010025c2c532c8d82e42d0b8bb5184afa449fa6f06c709ea9adcb16c49ae405b" +dependencies = [ + "libm", +] + +[[package]] +name = "i_key_sort" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9190f86706ca38ac8add223b2aed8b1330002b5cdbbce28fb58b10914d38fc27" + +[[package]] +name = "i_overlay" +version = "4.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413183068e6e0289e18d7d0a1f661b81546e6918d5453a44570b9ab30cbed1b3" +dependencies = [ + "i_float", + "i_key_sort", + "i_shape", + "i_tree", + "rayon", +] + +[[package]] +name = "i_shape" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ea154b742f7d43dae2897fcd5ead86bc7b5eefcedd305a7ebf9f69d44d61082" +dependencies = [ + "i_float", +] + +[[package]] +name = "i_tree" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35e6d558e6d4c7b82bc51d9c771e7a927862a161a7d87bf2b0541450e0e20915" + [[package]] name = "iana-time-zone" version = "0.1.63" @@ -3235,16 +2949,6 @@ dependencies = [ "web-time", ] -[[package]] -name = "inout" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" -dependencies = [ - "block-padding", - "generic-array", -] - [[package]] name = "instant" version = "0.1.13" @@ -3254,6 +2958,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "integer-encoding" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02" + [[package]] name = "io-uring" version = "0.7.10" @@ -3287,6 +2997,15 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.13.0" @@ -3385,33 +3104,18 @@ dependencies = [ "jiff", "nom 8.0.0", "num-traits", - "ordered-float", + "ordered-float 5.0.0", "rand 0.9.2", "ryu", "serde", "serde_json", ] -[[package]] -name = "jsonwebtoken" -version = "9.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde" -dependencies = [ - "base64 0.22.1", - "js-sys", - "pem", - "ring", - "serde", - "serde_json", - "simple_asn1", -] - [[package]] name = "lance" -version = "0.39.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2f0ca022d0424d991933a62d2898864cf5621873962bd84e65e7d1f023f9c36" +checksum = "e8c439decbc304e180748e34bb6d3df729069a222e83e74e2185c38f107136e9" dependencies = [ "arrow", "arrow-arith", @@ -3425,7 +3129,6 @@ dependencies = [ "async-recursion", "async-trait", "async_cell", - "aws-credential-types", "byteorder", "bytes", "chrono", @@ -3446,6 +3149,7 @@ dependencies = [ "lance-datafusion", "lance-encoding", "lance-file", + "lance-geo", "lance-index", "lance-io", "lance-linalg", @@ -3474,9 +3178,9 @@ dependencies = [ [[package]] name = "lance-arrow" -version = "0.39.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7552f8d528775bf0ab21e1f75dcb70bdb2a828eeae58024a803b5a4655fd9a11" +checksum = "f4ee5508b225456d3d56998eaeef0d8fbce5ea93856df47b12a94d2e74153210" dependencies = [ "arrow-array", "arrow-buffer", @@ -3494,9 +3198,9 @@ dependencies = [ [[package]] name = "lance-bitpacking" -version = "0.39.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2ea14583cc6fa0bb190bcc2d3bc364b0aa545b345702976025f810e4740e8ce" +checksum = "d1c065fb3bd4a8cc4f78428443e990d4921aa08f707b676753db740e0b402a21" dependencies = [ "arrayref", "paste", @@ -3505,9 +3209,9 @@ dependencies = [ [[package]] name = "lance-core" -version = "0.39.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69c752dedd207384892006c40930f898d6634e05e3d489e89763abfe4b9307e7" +checksum = "e8856abad92e624b75cd57a04703f6441948a239463bdf973f2ac1924b0bcdbe" dependencies = [ "arrow-array", "arrow-buffer", @@ -3543,9 +3247,9 @@ dependencies = [ [[package]] name = "lance-datafusion" -version = "0.39.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21e1e98ca6e5cd337bdda2d9fb66063f295c0c2852d2bc6831366fea833ee608" +checksum = "4c8835308044cef5467d7751be87fcbefc2db01c22370726a8704bd62991693f" dependencies = [ "arrow", "arrow-array", @@ -3564,6 +3268,7 @@ dependencies = [ "lance-arrow", "lance-core", "lance-datagen", + "lance-geo", "log", "pin-project", "prost", @@ -3574,9 +3279,9 @@ dependencies = [ [[package]] name = "lance-datagen" -version = "0.39.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "483c643fc2806ed1a2766edf4d180511bbd1d549bcc60373e33f4785c6185891" +checksum = "612de1e888bb36f6bf51196a6eb9574587fdf256b1759a4c50e643e00d5f96d0" dependencies = [ "arrow", "arrow-array", @@ -3593,9 +3298,9 @@ dependencies = [ [[package]] name = "lance-encoding" -version = "0.39.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a199d1fa3487529c5ffc433fbd1721231330b9350c2ff9b0c7b7dbdb98f0806a" +checksum = "2b456b29b135d3c7192602e516ccade38b5483986e121895fa43cf1fdb38bf60" dependencies = [ "arrow-arith", "arrow-array", @@ -3632,9 +3337,9 @@ dependencies = [ [[package]] name = "lance-file" -version = "0.39.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b57def2279465232cf5a8cd996300c632442e368745768bbed661c7f0a35334b" +checksum = "ab1538d14d5bb3735b4222b3f5aff83cfa59cc6ef7cdd3dd9139e4c77193c80b" dependencies = [ "arrow-arith", "arrow-array", @@ -3664,11 +3369,24 @@ dependencies = [ "tracing", ] +[[package]] +name = "lance-geo" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5a69a2f3b55703d9c240ad7c5ffa2c755db69e9cf8aa05efe274a212910472d" +dependencies = [ + "datafusion", + "geo-types", + "geoarrow-array", + "geoarrow-schema", + "geodatafusion", +] + [[package]] name = "lance-index" -version = "0.39.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75938c61e986aef8c615dc44c92e4c19e393160a59e2b57402ccfe08c5e63af" +checksum = "0ea84613df6fa6b9168a1f056ba4f9cb73b90a1b452814c6fd4b3529bcdbfc78" dependencies = [ "arrow", "arrow-arith", @@ -3729,9 +3447,9 @@ dependencies = [ [[package]] name = "lance-io" -version = "0.39.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa6c3b5b28570d6c951206c5b043f1b35c936928af14fca6f2ac25b0097e4c32" +checksum = "6b3fc4c1d941fceef40a0edbd664dbef108acfc5d559bb9e7f588d0c733cbc35" dependencies = [ "arrow", "arrow-arith", @@ -3743,8 +3461,6 @@ dependencies = [ "arrow-select", "async-recursion", "async-trait", - "aws-config", - "aws-credential-types", "byteorder", "bytes", "chrono", @@ -3755,8 +3471,6 @@ dependencies = [ "lance-namespace", "log", "object_store", - "object_store_opendal", - "opendal", "path_abs", "pin-project", "prost", @@ -3771,9 +3485,9 @@ dependencies = [ [[package]] name = "lance-linalg" -version = "0.39.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3cbc7e85a89ff9cb3a4627559dea3fd1c1fb16c0d8bc46ede75eefef51eec06" +checksum = "b62ffbc5ce367fbf700a69de3fe0612ee1a11191a64a632888610b6bacfa0f63" dependencies = [ "arrow-array", "arrow-buffer", @@ -3789,9 +3503,9 @@ dependencies = [ [[package]] name = "lance-namespace" -version = "0.39.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897dd6726816515bb70a698ce7cda44670dca5761637696d7905b45f405a8cd9" +checksum = "791bbcd868ee758123a34e07d320a1fb99379432b5ecc0e78d6b4686e999b629" dependencies = [ "arrow", "async-trait", @@ -3803,23 +3517,27 @@ dependencies = [ [[package]] name = "lance-namespace-impls" -version = "0.39.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e3cfcd3ba369de2719abf6fb6233f69cda639eb5cbcb328487a790e745ab988" +checksum = "ee713505576f6b1988a491f77c7ca8b0cf7090a393598e63c85079fa70a53ebf" dependencies = [ "arrow", "arrow-ipc", "arrow-schema", "async-trait", "bytes", + "futures", "lance", "lance-core", + "lance-index", "lance-io", "lance-namespace", + "log", "object_store", - "reqwest", + "rand 0.9.2", "serde_json", "snafu", + "tokio", "url", ] @@ -3838,9 +3556,9 @@ dependencies = [ [[package]] name = "lance-table" -version = "0.39.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8facc13760ba034b6c38767b16adba85e44cbcbea8124dc0c63c43865c60630" +checksum = "6fdb2d56bfa4d1511c765fa0cc00fdaa37e5d2d1cd2f57b3c6355d9072177052" dependencies = [ "arrow", "arrow-array", @@ -3877,9 +3595,9 @@ dependencies = [ [[package]] name = "lance-testing" -version = "0.39.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05052ef86188d6ae6339bdd9f2c5d77190e8ad1158f3dc8a42fa91bde9e5246" +checksum = "d8ccb1a4a9284435c6a8c02c8c06e7e041bece0d7f722152159353cf55dc51e3" dependencies = [ "arrow-array", "arrow-schema", @@ -3890,9 +3608,9 @@ dependencies = [ [[package]] name = "lancedb" -version = "0.22.3" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1da241266792d8caa58005a3deb06ba1388a99350d89b5c904ef6f8de5d936f" +checksum = "9217d7d3a1f4e088bdedaad9b4fa79045b077e07f961f1cd3ec6f90850c425f2" dependencies = [ "ahash", "arrow", @@ -3952,9 +3670,6 @@ name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -dependencies = [ - "spin", -] [[package]] name = "levenshtein_automata" @@ -4027,20 +3742,16 @@ dependencies = [ ] [[package]] -name = "libc" -version = "0.2.177" +name = "libbz2-rs-sys" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7" [[package]] -name = "libloading" -version = "0.8.8" +name = "libc" +version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" -dependencies = [ - "cfg-if", - "windows-targets 0.53.3", -] +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" [[package]] name = "libm" @@ -4058,6 +3769,15 @@ dependencies = [ "libc", ] +[[package]] +name = "libz-rs-sys" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c10501e7805cee23da17c7790e59df2870c0d4043ec6d03f67d31e2b53e77415" +dependencies = [ + "zlib-rs", +] + [[package]] name = "linux-raw-sys" version = "0.4.15" @@ -4148,6 +3868,17 @@ dependencies = [ "twox-hash", ] +[[package]] +name = "lzma-sys" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + [[package]] name = "macro_rules_attribute" version = "0.2.2" @@ -4419,22 +4150,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-bigint-dig" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" -dependencies = [ - "lazy_static", - "libm", - "num-integer", - "num-iter", - "num-traits", - "rand 0.8.5", - "smallvec", - "zeroize", -] - [[package]] name = "num-complex" version = "0.4.6" @@ -4501,6 +4216,28 @@ dependencies = [ "libc", ] +[[package]] +name = "num_enum" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "number_prefix" version = "0.4.0" @@ -4516,6 +4253,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +dependencies = [ + "memchr", +] + [[package]] name = "object_store" version = "0.12.3" @@ -4523,28 +4269,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "efc4f07659e11cd45a341cd24d71e683e3be65d9ff1f8150061678fe60437496" dependencies = [ "async-trait", - "base64 0.22.1", "bytes", "chrono", - "form_urlencoded", "futures", - "http 1.3.1", - "http-body-util", - "httparse", + "http", "humantime", - "hyper", "itertools 0.14.0", - "md-5", "parking_lot", "percent-encoding", - "quick-xml 0.38.3", - "rand 0.9.2", - "reqwest", - "ring", - "rustls-pemfile", - "serde", - "serde_json", - "serde_urlencoded", "thiserror 2.0.16", "tokio", "tracing", @@ -4554,21 +4286,6 @@ dependencies = [ "web-time", ] -[[package]] -name = "object_store_opendal" -version = "0.54.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce697ee723fdc3eaf6c457abf4059034be15167022b18b619993802cd1443d5" -dependencies = [ - "async-trait", - "bytes", - "futures", - "object_store", - "opendal", - "pin-project", - "tokio", -] - [[package]] name = "once_cell" version = "1.21.3" @@ -4609,35 +4326,6 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "opendal" -version = "0.54.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb9838d0575c6dbaf3fcec7255af8d5771996d4af900bbb6fa9a314dec00a1a" -dependencies = [ - "anyhow", - "backon", - "base64 0.22.1", - "bytes", - "chrono", - "crc32c", - "futures", - "getrandom 0.2.16", - "http 1.3.1", - "http-body 1.0.1", - "log", - "md-5", - "percent-encoding", - "quick-xml 0.37.5", - "reqsign", - "reqwest", - "serde", - "serde_json", - "sha2", - "tokio", - "uuid", -] - [[package]] name = "openssl-probe" version = "0.1.6" @@ -4652,29 +4340,22 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "ordered-float" -version = "5.0.0" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2c1f9f56e534ac6a9b8a4600bdf0f530fb393b5f393e7b4d03489c3cf0c3f01" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" dependencies = [ "num-traits", ] [[package]] -name = "ordered-multimap" -version = "0.7.3" +name = "ordered-float" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79" +checksum = "e2c1f9f56e534ac6a9b8a4600bdf0f530fb393b5f393e7b4d03489c3cf0c3f01" dependencies = [ - "dlv-list", - "hashbrown 0.14.5", + "num-traits", ] -[[package]] -name = "outref" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" - [[package]] name = "ownedbytes" version = "0.9.0" @@ -4713,6 +4394,43 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "parquet" +version = "56.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dbd48ad52d7dccf8ea1b90a3ddbfaea4f69878dd7683e51c507d4bc52b5b27" +dependencies = [ + "ahash", + "arrow-array", + "arrow-buffer", + "arrow-cast", + "arrow-data", + "arrow-ipc", + "arrow-schema", + "arrow-select", + "base64 0.22.1", + "brotli", + "bytes", + "chrono", + "flate2", + "futures", + "half", + "hashbrown 0.16.1", + "lz4_flex", + "num", + "num-bigint", + "object_store", + "paste", + "ring", + "seq-macro", + "simdutf8", + "snap", + "thrift", + "tokio", + "twox-hash", + "zstd", +] + [[package]] name = "paste" version = "1.0.15" @@ -4731,35 +4449,6 @@ dependencies = [ "stfu8", ] -[[package]] -name = "pbkdf2" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" -dependencies = [ - "digest", - "hmac", -] - -[[package]] -name = "pem" -version = "3.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" -dependencies = [ - "base64 0.22.1", - "serde_core", -] - -[[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.3.2" @@ -4844,44 +4533,6 @@ 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 = "pkcs5" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e847e2c91a18bfa887dd028ec33f2fe6f25db77db3619024764914affe8b69a6" -dependencies = [ - "aes", - "cbc", - "der", - "pbkdf2", - "scrypt", - "sha2", - "spki", -] - -[[package]] -name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der", - "pkcs5", - "rand_core 0.6.4", - "spki", -] - [[package]] name = "pkg-config" version = "0.3.32" @@ -4937,6 +4588,15 @@ dependencies = [ "syn 2.0.106", ] +[[package]] +name = "proc-macro-crate" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +dependencies = [ + "toml_edit", +] + [[package]] name = "proc-macro2" version = "1.0.101" @@ -4999,23 +4659,13 @@ dependencies = [ ] [[package]] -name = "quick-xml" -version = "0.37.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" -dependencies = [ - "memchr", - "serde", -] - -[[package]] -name = "quick-xml" -version = "0.38.3" +name = "psm" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42a232e7487fc2ef313d96dde7948e7a3c05101870d8985e4fd8d26aedd27b89" +checksum = "1fa96cb91275ed31d6da3e983447320c4eb219ac180fa1679a0889ff32861e2d" dependencies = [ - "memchr", - "serde", + "ar_archive_writer", + "cc", ] [[package]] @@ -5238,6 +4888,26 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "recursive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0786a43debb760f491b1bc0269fe5e84155353c67482b9e60d0cfb596054b43e" +dependencies = [ + "recursive-proc-macro-impl", + "stacker", +] + +[[package]] +name = "recursive-proc-macro-impl" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76009fbe0614077fc1a2ce255e3a1881a2e3a3527097d5dc6d8212c585e7e38b" +dependencies = [ + "quote", + "syn 2.0.106", +] + [[package]] name = "redox_syscall" version = "0.5.17" @@ -5312,50 +4982,12 @@ dependencies = [ "regex-syntax", ] -[[package]] -name = "regex-lite" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "943f41321c63ef1c92fd763bfe054d2668f7f225a5c29f0105903dc2fc04ba30" - [[package]] name = "regex-syntax" version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" -[[package]] -name = "reqsign" -version = "0.16.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43451dbf3590a7590684c25fb8d12ecdcc90ed3ac123433e500447c7d77ed701" -dependencies = [ - "anyhow", - "async-trait", - "base64 0.22.1", - "chrono", - "form_urlencoded", - "getrandom 0.2.16", - "hex", - "hmac", - "home", - "http 1.3.1", - "jsonwebtoken", - "log", - "once_cell", - "percent-encoding", - "quick-xml 0.37.5", - "rand 0.8.5", - "reqwest", - "rsa", - "rust-ini", - "serde", - "serde_json", - "sha1", - "sha2", - "tokio", -] - [[package]] name = "reqwest" version = "0.12.23" @@ -5369,8 +5001,8 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http 1.3.1", - "http-body 1.0.1", + "http", + "http-body", "http-body-util", "hyper", "hyper-rustls", @@ -5444,34 +5076,20 @@ dependencies = [ ] [[package]] -name = "rsa" -version = "0.9.9" +name = "robust" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40a0376c50d0358279d9d643e4bf7b7be212f1f4ff1da9070a7b54d22ef75c88" -dependencies = [ - "const-oid", - "digest", - "num-bigint-dig", - "num-integer", - "num-traits", - "pkcs1", - "pkcs8", - "rand_core 0.6.4", - "sha2", - "signature", - "spki", - "subtle", - "zeroize", -] +checksum = "4e27ee8bb91ca0adcf0ecb116293afa12d393f9c2b9b9cd54d33e8078fe19839" [[package]] -name = "rust-ini" -version = "0.21.3" +name = "rstar" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "796e8d2b6696392a43bea58116b667fb4c29727dc5abd27d6acf338bb4f688c7" +checksum = "421400d13ccfd26dfa5858199c30a5d76f9c54e0dba7575273025b43c5175dbb" dependencies = [ - "cfg-if", - "ordered-multimap", + "heapless", + "num-traits", + "smallvec", ] [[package]] @@ -5537,7 +5155,6 @@ version = "0.23.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" dependencies = [ - "aws-lc-rs", "log", "once_cell", "ring", @@ -5559,15 +5176,6 @@ dependencies = [ "security-framework", ] -[[package]] -name = "rustls-pemfile" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" -dependencies = [ - "rustls-pki-types", -] - [[package]] name = "rustls-pki-types" version = "1.12.0" @@ -5584,7 +5192,6 @@ version = "0.103.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" dependencies = [ - "aws-lc-rs", "ring", "rustls-pki-types", "untrusted", @@ -5612,15 +5219,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "salsa20" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" -dependencies = [ - "cipher", -] - [[package]] name = "same-file" version = "1.0.6" @@ -5675,17 +5273,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "scrypt" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" -dependencies = [ - "pbkdf2", - "salsa20", - "sha2", -] - [[package]] name = "secrecy" version = "0.10.3" @@ -5858,17 +5445,6 @@ dependencies = [ "syn 2.0.106", ] -[[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.9" @@ -5913,34 +5489,12 @@ dependencies = [ "libc", ] -[[package]] -name = "signature" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" -dependencies = [ - "digest", - "rand_core 0.6.4", -] - [[package]] name = "simdutf8" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" -[[package]] -name = "simple_asn1" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" -dependencies = [ - "num-bigint", - "num-traits", - "thiserror 2.0.16", - "time", -] - [[package]] name = "simsimd" version = "6.5.1" @@ -5998,6 +5552,12 @@ dependencies = [ "syn 2.0.106", ] +[[package]] +name = "snap" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" + [[package]] name = "socket2" version = "0.5.10" @@ -6030,19 +5590,15 @@ dependencies = [ ] [[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - -[[package]] -name = "spki" -version = "0.7.3" +name = "spade" +version = "2.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +checksum = "fb313e1c8afee5b5647e00ee0fe6855e3d529eb863a0fdae1d60006c4d1e9990" dependencies = [ - "base64ct", - "der", + "hashbrown 0.15.5", + "num-traits", + "robust", + "smallvec", ] [[package]] @@ -6064,6 +5620,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec4b661c54b1e4b603b37873a18c59920e4c51ea8ea2cf527d925424dbd4437c" dependencies = [ "log", + "recursive", "sqlparser_derive", ] @@ -6084,6 +5641,19 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "stacker" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1f8b29fb42aafcea4edeeb6b2f2d7ecd0d969c48b4cf0d2e64aafc471dd6e59" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "windows-sys 0.59.0", +] + [[package]] name = "static_assertions" version = "1.1.0" @@ -6416,6 +5986,17 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "thrift" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e54bc85fc7faa8bc175c4bab5b92ba8d9a3ce893d0e9f42cc455c8ab16a9e09" +dependencies = [ + "byteorder", + "integer-encoding", + "ordered-float 2.10.1", +] + [[package]] name = "time" version = "0.3.43" @@ -6580,6 +6161,36 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.23.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2ad0b7ae9cfeef5605163839cb9221f453399f15cfb5c10be9885fcf56611f9" +dependencies = [ + "indexmap 2.11.1", + "toml_datetime", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.0.6+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" +dependencies = [ + "winnow", +] + [[package]] name = "tower" version = "0.5.2" @@ -6604,8 +6215,8 @@ dependencies = [ "bitflags", "bytes", "futures-util", - "http 1.3.1", - "http-body 1.0.1", + "http", + "http-body", "iri-string", "pin-project-lite", "tower", @@ -6783,12 +6394,6 @@ dependencies = [ "serde", ] -[[package]] -name = "urlencoding" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" - [[package]] name = "utf8-ranges" version = "1.0.5" @@ -6831,12 +6436,6 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" -[[package]] -name = "vsimd" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" - [[package]] name = "walkdir" version = "2.5.0" @@ -7372,6 +6971,15 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" +[[package]] +name = "winnow" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +dependencies = [ + "memchr", +] + [[package]] name = "wit-bindgen-rt" version = "0.39.0" @@ -7381,6 +6989,31 @@ dependencies = [ "bitflags", ] +[[package]] +name = "wkb" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a120b336c7ad17749026d50427c23d838ecb50cd64aaea6254b5030152f890a9" +dependencies = [ + "byteorder", + "geo-traits", + "num_enum", + "thiserror 1.0.69", +] + +[[package]] +name = "wkt" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efb2b923ccc882312e559ffaa832a055ba9d1ac0cc8e86b3e25453247e4b81d7" +dependencies = [ + "geo-traits", + "geo-types", + "log", + "num-traits", + "thiserror 1.0.69", +] + [[package]] name = "writeable" version = "0.6.1" @@ -7396,18 +7029,21 @@ dependencies = [ "tap", ] -[[package]] -name = "xmlparser" -version = "0.13.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" - [[package]] name = "xxhash-rust" version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" +[[package]] +name = "xz2" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2" +dependencies = [ + "lzma-sys", +] + [[package]] name = "yoke" version = "0.8.0" @@ -7512,6 +7148,12 @@ dependencies = [ "syn 2.0.106", ] +[[package]] +name = "zlib-rs" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40990edd51aae2c2b6907af74ffb635029d5788228222c4bb811e9351c0caad3" + [[package]] name = "zstd" version = "0.13.3" diff --git a/Cargo.toml b/Cargo.toml index e4e35446..568f8a42 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,7 +51,7 @@ model2vec-rs = { version = "0.1.3", optional = true } simsimd = { version = "6.5.1", optional = true } # Workspace-specific dependencies -lancedb = { version = "0.22.3", default-features = false, optional = true } +lancedb = { version = "0.23.1", default-features = false, optional = true } arrow-schema = { version = "56.2.0", optional = true } arrow-array = { version = "56.2.0", optional = true } rand = { version = "0.8.5", optional = true } diff --git a/src/ask/chat_agent.rs b/src/ask/chat_agent.rs index 6cc31087..217fb532 100644 --- a/src/ask/chat_agent.rs +++ b/src/ask/chat_agent.rs @@ -1,3 +1,5 @@ +use std::vec; + use anyhow::Result; use async_openai::config::OpenAIConfig; use async_openai::types::chat::{ @@ -11,6 +13,7 @@ use serde_json::Value; use crate::ask::system_prompt::{STDIN_SYSTEM_PROMPT, SYSTEM_PROMPT}; use crate::ask::tools::{AgentTool, GrepTool, ReadTool, SearchTool}; +use crate::json_mode::AskOutput; use crate::search::SearchConfig; /// Run an agent loop with the search and read tools @@ -32,8 +35,13 @@ pub async fn ask_agent( client: &Client, api_model: &str, max_iterations: Option, -) -> Result { +) -> Result { let max_iterations = max_iterations.unwrap_or(20); + let mut result = AskOutput { + query: user_message.to_string(), + response: String::new(), + files_searched: vec![], + }; // Build the tools let tools: Vec = vec![ @@ -82,7 +90,7 @@ pub async fn ask_agent( let args = &tool_call.function.arguments; // Call the appropriate tool - let response_content = call_tool(name, args, &files, model).await?; + let response_content = call_tool(name, args, &files, model, &mut result).await?; // Print summary of the tool response print_tool_summary(&response_content); @@ -113,7 +121,8 @@ pub async fn ask_agent( } else { // No tool calls - we have a final response if let Some(content) = response_message.content { - return Ok(content); + result.response = content.clone(); + return Ok(result); } else { return Err(anyhow::anyhow!("No content in final response")); } @@ -132,6 +141,7 @@ async fn call_tool( args: &str, files: &[String], model: &StaticModel, + cur_output: &mut AskOutput, ) -> Result { let function_args: Value = serde_json::from_str(args)?; @@ -148,6 +158,15 @@ async fn call_tool( .collect() }); + // Update files_searched in cur_output + if let Some(paths) = file_paths.clone() { + for path in paths { + if !cur_output.files_searched.contains(&path) { + cur_output.files_searched.push(path); + } + } + } + let is_regex = function_args["is_regex"].as_bool().unwrap_or(false); let case_sensitive = function_args["case_sensitive"].as_bool().unwrap_or(true); let context_lines = function_args["context_lines"].as_u64().unwrap_or(3) as usize; @@ -206,7 +225,7 @@ async fn call_tool( println!(" top_k: {}", top_k); } - SearchTool::search(files, query, model, config).await + SearchTool::search(files, query, model, config, &mut cur_output.files_searched).await } "read" => { let path = function_args["path"] @@ -278,12 +297,17 @@ pub async fn ask_agent_with_stdin( user_message: &str, client: &Client, api_model: &str, -) -> Result { +) -> Result { // Construct the user message with stdin content let full_message = format!( "\n{}\n\n\n{}", stdin_content, user_message ); + let mut result = AskOutput { + query: user_message.to_string(), + response: String::new(), + files_searched: vec!["".to_string()], + }; // Initialize messages with system prompt and user message (no tools) let messages: Vec = vec![ @@ -313,7 +337,8 @@ pub async fn ask_agent_with_stdin( // Return the content if let Some(content) = response_message.content { - Ok(content) + result.response = content; + Ok(result) } else { Err(anyhow::anyhow!("No content in response")) } diff --git a/src/ask/responses_agent.rs b/src/ask/responses_agent.rs index 798ddf0d..94656b1a 100644 --- a/src/ask/responses_agent.rs +++ b/src/ask/responses_agent.rs @@ -11,6 +11,7 @@ use serde_json::Value; use crate::ask::system_prompt::{STDIN_SYSTEM_PROMPT, SYSTEM_PROMPT}; use crate::ask::tools::{AgentTool, GrepTool, ReadTool, SearchTool}; +use crate::json_mode::AskOutput; use crate::search::SearchConfig; /// Run an agent loop with the search and read tools using the Responses API @@ -24,7 +25,7 @@ use crate::search::SearchConfig; /// * `max_iterations` - Maximum number of agent loop iterations (default: 20) /// /// # Returns -/// The final response from the agent as a String +/// AskOutput containing the query, response, and files searched pub async fn ask_agent_responses( files: Vec, user_message: &str, @@ -32,8 +33,9 @@ pub async fn ask_agent_responses( client: &Client, api_model: &str, max_iterations: Option, -) -> Result { +) -> Result { let max_iterations = max_iterations.unwrap_or(20); + let mut files_searched: Vec = Vec::new(); // Build the tools using the responses API format let tools: Vec = vec![ @@ -91,7 +93,7 @@ pub async fn ask_agent_responses( let args = &function_call.arguments; // Call the appropriate tool - let response_content = call_tool(name, args, &files, model).await?; + let response_content = call_tool(name, args, &files, model, &mut files_searched).await?; // Print summary of the tool response print_tool_summary(&response_content); @@ -108,16 +110,27 @@ pub async fn ask_agent_responses( } } else { // No tool calls - we have a final response - return Ok(response + let response_text = response .output_text() - .unwrap_or("".to_string())); + .unwrap_or("".to_string()); + + return Ok(AskOutput { + query: user_message.to_string(), + response: response_text, + files_searched, + }); } } - Err(anyhow::anyhow!( - "Max iterations ({}) reached without final response", - max_iterations - )) + // If we reach here, max iterations was hit + Ok(AskOutput { + query: user_message.to_string(), + response: format!( + "Max iterations ({}) reached without final response", + max_iterations + ), + files_searched, + }) } /// Call a tool by name with the given arguments @@ -126,6 +139,7 @@ async fn call_tool( args: &str, files: &[String], model: &StaticModel, + files_searched: &mut Vec, ) -> Result { let function_args: Value = serde_json::from_str(args)?; @@ -142,6 +156,15 @@ async fn call_tool( .collect() }); + // Update files_searched + if let Some(paths) = file_paths.clone() { + for path in paths { + if !files_searched.contains(&path) { + files_searched.push(path); + } + } + } + let is_regex = function_args["is_regex"].as_bool().unwrap_or(false); let case_sensitive = function_args["case_sensitive"].as_bool().unwrap_or(true); let context_lines = function_args["context_lines"].as_u64().unwrap_or(3) as usize; @@ -200,7 +223,7 @@ async fn call_tool( println!(" top_k: {}", top_k); } - SearchTool::search(files, query, model, config).await + SearchTool::search(files, query, model, config, files_searched).await } "read" => { let path = function_args["path"] @@ -293,13 +316,13 @@ fn output_item_to_item(output_item: &OutputItem) -> Result { /// * `api_model` - The LLM model to use (e.g., "gpt-4.1") /// /// # Returns -/// The response from the agent as a String +/// AskOutput containing the query, response, and "" as the file searched pub async fn ask_agent_responses_with_stdin( stdin_content: &str, user_message: &str, client: &Client, api_model: &str, -) -> Result { +) -> Result { // Construct the user message with stdin content let full_message = format!( "\n{}\n\n\n{}", @@ -325,8 +348,14 @@ pub async fn ask_agent_responses_with_stdin( // Get response from LLM let response = client.responses().create(request).await?; - // Return the text output - Ok(response + // Return AskOutput with stdin as the file searched + let response_text = response .output_text() - .unwrap_or("".to_string())) + .unwrap_or("".to_string()); + + Ok(AskOutput { + query: user_message.to_string(), + response: response_text, + files_searched: vec!["".to_string()], + }) } diff --git a/src/ask/tools.rs b/src/ask/tools.rs index 68f6a2b9..7682aeb1 100644 --- a/src/ask/tools.rs +++ b/src/ask/tools.rs @@ -210,6 +210,7 @@ impl SearchTool { query: &str, model: &StaticModel, config: SearchConfig, + files_searched: &mut Vec, ) -> Result { let query = if config.ignore_case { query.to_lowercase() @@ -229,12 +230,27 @@ impl SearchTool { // Workspace mode: use persisted line embeddings for speed let ranked_lines = search_with_workspace(files, &query, model, &config).await?; - // Step 5: Convert results to SearchResult format and print + // Track files that were searched (have results) + for ranked_line in &ranked_lines { + if !files_searched.contains(&ranked_line.path) { + files_searched.push(ranked_line.path.clone()); + } + } + + // Convert results to SearchResult format and format let formatted = format_ranked_lines(&ranked_lines, config.n_lines); return Ok(formatted); } let search_results = search_files(files, &query, model, &config)?; + + // Track files that were searched (have results) + for result in &search_results { + if !files_searched.contains(&result.filename) { + files_searched.push(result.filename.clone()); + } + } + let formatted = format_search_results(&search_results); Ok(formatted) diff --git a/src/bin/ask.rs b/src/bin/ask.rs index 796cfd96..5311a87e 100644 --- a/src/bin/ask.rs +++ b/src/bin/ask.rs @@ -9,6 +9,7 @@ use semtools::SemtoolsConfig; use semtools::ask::chat_agent::{ask_agent, ask_agent_with_stdin}; use semtools::ask::responses_agent::{ask_agent_responses, ask_agent_responses_with_stdin}; use semtools::config::ApiMode; +use semtools::json_mode::ErrorOutput; use semtools::search::MODEL_NAME; #[derive(Parser, Debug)] @@ -40,6 +41,10 @@ struct Args { /// API mode to use: 'chat' or 'responses' (overrides config file) #[clap(long)] api_mode: Option, + + /// Output results in JSON or text format + #[clap(short, long)] + json: bool, } fn read_from_stdin() -> Result> { @@ -112,7 +117,7 @@ async fn main() -> Result<()> { let stdin_content = stdin_lines.join("\n"); // Run the appropriate agent with stdin content (no tools) - let response = match api_mode { + let output = match api_mode { ApiMode::Chat => { ask_agent_with_stdin(&stdin_content, &args.query, &client, &model_name).await? } @@ -127,16 +132,33 @@ async fn main() -> Result<()> { } }; - println!("\n{}", response); + if args.json { + let json_output = serde_json::to_string_pretty(&output)?; + println!("\n{}", json_output); + } + else { + println!("\n{}", output.response); + } + return Ok(()); } } // If no stdin, we need files to search through if args.files.is_empty() { - eprintln!( - "Error: No input provided. Either specify files as arguments or pipe input to stdin." - ); + let error_msg = "No input provided. Either specify files as arguments or pipe input to stdin."; + if args.json { + let error_output = ErrorOutput { + error: error_msg.to_string(), + error_type: "NoInput".to_string(), + }; + let json_output = serde_json::to_string_pretty(&error_output)?; + eprintln!("{}", json_output); + std::process::exit(1); + } + else { + eprintln!("{}", error_msg); + } std::process::exit(1); } @@ -149,7 +171,7 @@ async fn main() -> Result<()> { )?; // Run the appropriate agent based on API mode - let response = match api_mode { + let output = match api_mode { ApiMode::Chat => { ask_agent( args.files, @@ -174,7 +196,13 @@ async fn main() -> Result<()> { } }; - println!("\n{}", response); + if args.json { + let json_output = serde_json::to_string_pretty(&output)?; + println!("\n{}", json_output); + } + else { + println!("\n{}", output.response); + } Ok(()) } diff --git a/src/bin/search.rs b/src/bin/search.rs index 6b5c2ce5..ebe284a4 100644 --- a/src/bin/search.rs +++ b/src/bin/search.rs @@ -9,6 +9,7 @@ use semtools::workspace::{Workspace, store::RankedLine}; #[cfg(feature = "workspace")] use semtools::search::search_with_workspace; +use semtools::json_mode::{ErrorOutput, SearchOutput, SearchResultJSON}; use semtools::search::{ Document, MODEL_NAME, SearchConfig, SearchResult, search_documents, search_files, }; @@ -38,6 +39,10 @@ struct Args { /// Perform case-insensitive search (default is false) #[arg(short, long, default_value_t = false)] ignore_case: bool, + + /// Output results in JSON format + #[clap(short, long)] + json: bool, } fn read_from_stdin() -> Result> { @@ -46,6 +51,18 @@ fn read_from_stdin() -> Result> { Ok(lines?) } +// Convert SearchResult to SearchResultJSON +fn search_result_to_json(result: &SearchResult) -> SearchResultJSON { + SearchResultJSON { + filename: result.filename.clone(), + start_line_number: result.start, + end_line_number: result.end, + match_line_number: result.match_line, + distance: result.distance, + content: result.lines.join("\n"), + } +} + // Extracted function to format and print results fn print_search_results(results: &[SearchResult]) { let is_tty = io::stdout().is_terminal(); @@ -168,35 +185,113 @@ async fn main() -> Result<()> { }]; let search_results = search_documents(&documents, &query_embedding, &config); - print_search_results(&search_results); + + if args.json { + let output = SearchOutput { + results: search_results.iter().map(search_result_to_json).collect(), + }; + let json_output = serde_json::to_string_pretty(&output)?; + println!("{}", json_output); + } else { + print_search_results(&search_results); + } + return Ok(()); } } if args.files.is_empty() { - eprintln!( - "Error: No input provided. Either specify files as arguments or pipe input to stdin." - ); + let error_msg = "No input provided. Either specify files as arguments or pipe input to stdin."; + if args.json { + let error_output = ErrorOutput { + error: error_msg.to_string(), + error_type: "NoInput".to_string(), + }; + let json_output = serde_json::to_string_pretty(&error_output)?; + eprintln!("{}", json_output); + } else { + eprintln!("Error: {}", error_msg); + } std::process::exit(1); } // Handle file input with optional workspace integration #[cfg(feature = "workspace")] - if Workspace::active().is_ok() { - // Workspace mode: use persisted line embeddings for speed - let config = SearchConfig { - n_lines: args.n_lines, - top_k: args.top_k, - max_distance: args.max_distance, - ignore_case: args.ignore_case, - }; - let ranked_lines = search_with_workspace(&args.files, &query, &model, &config).await?; - - // Step 5: Convert results to SearchResult format and print - print_workspace_search_results(&ranked_lines, args.n_lines); - } else { + { + if Workspace::active().is_ok() { + // Workspace mode: use persisted line embeddings for speed + let config = SearchConfig { + n_lines: args.n_lines, + top_k: args.top_k, + max_distance: args.max_distance, + ignore_case: args.ignore_case, + }; + let ranked_lines = search_with_workspace(&args.files, &query, &model, &config).await?; + + if args.json { + // Convert workspace results to SearchResultJSON + let results: Vec = ranked_lines + .iter() + .map(|ranked_line| { + let match_line_number = ranked_line.line_number as usize; + let start = match_line_number.saturating_sub(args.n_lines); + let end = match_line_number + args.n_lines + 1; + + // Read file content for the result + let content = if let Ok(file_content) = std::fs::read_to_string(&ranked_line.path) { + let lines: Vec<&str> = file_content.lines().collect(); + let actual_start = start; + let actual_end = end.min(lines.len()); + lines[actual_start..actual_end].join("\n") + } else { + "[Error: Could not read file content]".to_string() + }; + + SearchResultJSON { + filename: ranked_line.path.clone(), + start_line_number: start, + end_line_number: end, + match_line_number, + distance: ranked_line.distance as f64, + content, + } + }) + .collect(); + + let output = SearchOutput { results }; + let json_output = serde_json::to_string_pretty(&output)?; + println!("{}", json_output); + } else { + print_workspace_search_results(&ranked_lines, args.n_lines); + } + } else { + let search_results = search_files(&args.files, &query, &model, &config)?; + + if args.json { + let output = SearchOutput { + results: search_results.iter().map(search_result_to_json).collect(), + }; + let json_output = serde_json::to_string_pretty(&output)?; + println!("{}", json_output); + } else { + print_search_results(&search_results); + } + } + } + + #[cfg(not(feature = "workspace"))] + { let search_results = search_files(&args.files, &query, &model, &config)?; - print_search_results(&search_results); + + if args.json { + let output = SearchOutput { + results: search_results.iter().map(search_result_to_json).collect(), + }; + let json_output = serde_json::to_string_pretty(&output)?; + println!("{}", json_output); + } else { + print_search_results(&search_results); + } } Ok(()) diff --git a/src/bin/workspace.rs b/src/bin/workspace.rs index 4311fe0d..f81ef2ea 100644 --- a/src/bin/workspace.rs +++ b/src/bin/workspace.rs @@ -4,9 +4,18 @@ use clap::{Parser, Subcommand}; #[cfg(feature = "workspace")] use semtools::workspace::{Workspace, WorkspaceConfig, store::Store}; +use semtools::json_mode::{PruneOutput, WorkspaceOutput}; + +#[cfg(not(feature = "workspace"))] +use semtools::json_mode::ErrorOutput; + #[derive(Parser, Debug)] #[command(version, about = "Manage semtools workspaces", long_about = None)] struct Args { + /// Output results in JSON format + #[clap(short, long, global = true)] + json: bool, + #[command(subcommand)] command: Commands, } @@ -39,15 +48,45 @@ async fn main() -> Result<()> { }; ws.save()?; - println!("Workspace '{name}' configured."); - println!("To activate it, run:"); - println!(" export SEMTOOLS_WORKSPACE={name}"); - println!(); - println!("Or add this to your shell profile (.bashrc, .zshrc, etc.)"); + if args.json { + // Try to get document count from store, or use 0 for new workspace + let total_documents = if let Ok(store) = Store::open(&ws.config.root_dir).await { + if let Ok(stats) = store.get_stats().await { + stats.total_documents + } else { + 0 + } + } else { + 0 + }; + + let output = WorkspaceOutput { + name: ws.config.name.clone(), + root_dir: ws.config.root_dir.clone(), + total_documents, + }; + let json_output = serde_json::to_string_pretty(&output)?; + println!("{}", json_output); + } else { + println!("Workspace '{name}' configured."); + println!("To activate it, run:"); + println!(" export SEMTOOLS_WORKSPACE={name}"); + println!(); + println!("Or add this to your shell profile (.bashrc, .zshrc, etc.)"); + } } #[cfg(not(feature = "workspace"))] { - println!("workspace feature not enabled"); + if args.json { + let error_output = ErrorOutput { + error: "workspace feature not enabled".to_string(), + error_type: "FeatureNotEnabled".to_string(), + }; + let json_output = serde_json::to_string_pretty(&error_output)?; + eprintln!("{}", json_output); + } else { + println!("workspace feature not enabled"); + } } } Commands::Status => { @@ -55,24 +94,43 @@ async fn main() -> Result<()> { { let _name = Workspace::active().context("No active workspace")?; let ws = Workspace::open()?; - println!("Active workspace: {}", ws.config.name); - println!("Root: {}", ws.config.root_dir); - // Open store and print counts/index status + // Open store and get stats let store = Store::open(&ws.config.root_dir).await?; let stats = store.get_stats().await?; - println!("Documents: {}", stats.total_documents); - if stats.has_index { - let index_info = stats.index_type.unwrap_or_else(|| "Unknown".to_string()); - println!("Index: Yes ({index_info})"); + if args.json { + let output = WorkspaceOutput { + name: ws.config.name.clone(), + root_dir: ws.config.root_dir.clone(), + total_documents: stats.total_documents, + }; + let json_output = serde_json::to_string_pretty(&output)?; + println!("{}", json_output); } else { - println!("Index: No"); + println!("Active workspace: {}", ws.config.name); + println!("Root: {}", ws.config.root_dir); + println!("Documents: {}", stats.total_documents); + if stats.has_index { + let index_info = stats.index_type.unwrap_or_else(|| "Unknown".to_string()); + println!("Index: Yes ({index_info})"); + } else { + println!("Index: No"); + } } } #[cfg(not(feature = "workspace"))] { - println!("workspace feature not enabled"); + if args.json { + let error_output = ErrorOutput { + error: "workspace feature not enabled".to_string(), + error_type: "FeatureNotEnabled".to_string(), + }; + let json_output = serde_json::to_string_pretty(&error_output)?; + eprintln!("{}", json_output); + } else { + println!("workspace feature not enabled"); + } } } Commands::Prune {} => { @@ -84,6 +142,7 @@ async fn main() -> Result<()> { // Get all document paths from the workspace let all_paths = store.get_all_document_paths().await?; + let total_before = all_paths.len(); // Check which files no longer exist let mut missing_paths = Vec::new(); @@ -93,25 +152,48 @@ async fn main() -> Result<()> { } } - if missing_paths.is_empty() { - println!("No stale documents found. Workspace is clean."); - } else { - println!("Found {} stale documents:", missing_paths.len()); - for path in &missing_paths { - println!(" - {path}"); - } + let files_removed = missing_paths.len(); + let files_remaining = total_before - files_removed; + if !missing_paths.is_empty() { // Remove stale documents store.delete_documents(&missing_paths).await?; - println!( - "Removed {} stale documents from workspace.", - missing_paths.len() - ); + } + + if args.json { + let output = PruneOutput { + files_removed, + files_remaining, + }; + let json_output = serde_json::to_string_pretty(&output)?; + println!("{}", json_output); + } else { + if missing_paths.is_empty() { + println!("No stale documents found. Workspace is clean."); + } else { + println!("Found {} stale documents:", missing_paths.len()); + for path in &missing_paths { + println!(" - {path}"); + } + println!( + "Removed {} stale documents from workspace.", + missing_paths.len() + ); + } } } #[cfg(not(feature = "workspace"))] { - println!("workspace feature not enabled"); + if args.json { + let error_output = ErrorOutput { + error: "workspace feature not enabled".to_string(), + error_type: "FeatureNotEnabled".to_string(), + }; + let json_output = serde_json::to_string_pretty(&error_output)?; + eprintln!("{}", json_output); + } else { + println!("workspace feature not enabled"); + } } } } diff --git a/src/json_mode.rs b/src/json_mode.rs new file mode 100644 index 00000000..a86e5036 --- /dev/null +++ b/src/json_mode.rs @@ -0,0 +1,60 @@ +use serde::Serialize; + +// Parse +#[derive(Debug, Serialize)] +pub struct ParseResultJSON { + pub input_path: String, + pub output_path: String, + pub was_cached: bool, +} + +#[derive(Debug, Serialize)] +pub struct ParseOutput { + pub results: Vec, +} + + +// Search +#[derive(Debug, Serialize)] +pub struct SearchResultJSON { + pub filename: String, + pub start_line_number: usize, + pub end_line_number: usize, + pub match_line_number: usize, + pub distance: f64, + pub content: String, +} + +#[derive(Debug, Serialize)] +pub struct SearchOutput { + pub results: Vec, +} + +// Ask +#[derive(Debug, Serialize)] +pub struct AskOutput { + pub query: String, + pub response: String, + pub files_searched: Vec, +} + +// Workspace +#[derive(Debug, Serialize)] +pub struct WorkspaceOutput { + pub name: String, + pub root_dir: String, + pub total_documents: usize, +} + +#[derive(Debug, Serialize)] +pub struct PruneOutput { + pub files_removed: usize, + pub files_remaining: usize, +} + +// Error output +#[derive(Debug, Serialize)] +pub struct ErrorOutput { + pub error: String, + pub error_type: String, +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index be5c5b19..aa8f64c8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,8 @@ pub mod config; pub use config::{AskConfig, SemtoolsConfig}; +pub mod json_mode; + #[cfg(feature = "parse")] pub mod parse; From 365caf23fe42edddb47734a1a5cc81affdeb1497 Mon Sep 17 00:00:00 2001 From: Logan Markewich Date: Tue, 3 Feb 2026 20:52:55 -0600 Subject: [PATCH 2/5] lint --- src/ask/chat_agent.rs | 5 +++-- src/ask/responses_agent.rs | 3 ++- src/bin/ask.rs | 12 +++++------- src/bin/search.rs | 20 +++++++++++--------- src/bin/workspace.rs | 3 ++- src/json_mode.rs | 5 ++--- 6 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/ask/chat_agent.rs b/src/ask/chat_agent.rs index 217fb532..52a5013f 100644 --- a/src/ask/chat_agent.rs +++ b/src/ask/chat_agent.rs @@ -90,7 +90,8 @@ pub async fn ask_agent( let args = &tool_call.function.arguments; // Call the appropriate tool - let response_content = call_tool(name, args, &files, model, &mut result).await?; + let response_content = + call_tool(name, args, &files, model, &mut result).await?; // Print summary of the tool response print_tool_summary(&response_content); @@ -166,7 +167,7 @@ async fn call_tool( } } } - + let is_regex = function_args["is_regex"].as_bool().unwrap_or(false); let case_sensitive = function_args["case_sensitive"].as_bool().unwrap_or(true); let context_lines = function_args["context_lines"].as_u64().unwrap_or(3) as usize; diff --git a/src/ask/responses_agent.rs b/src/ask/responses_agent.rs index 94656b1a..9abe857d 100644 --- a/src/ask/responses_agent.rs +++ b/src/ask/responses_agent.rs @@ -93,7 +93,8 @@ pub async fn ask_agent_responses( let args = &function_call.arguments; // Call the appropriate tool - let response_content = call_tool(name, args, &files, model, &mut files_searched).await?; + let response_content = + call_tool(name, args, &files, model, &mut files_searched).await?; // Print summary of the tool response print_tool_summary(&response_content); diff --git a/src/bin/ask.rs b/src/bin/ask.rs index 5311a87e..28c07892 100644 --- a/src/bin/ask.rs +++ b/src/bin/ask.rs @@ -135,8 +135,7 @@ async fn main() -> Result<()> { if args.json { let json_output = serde_json::to_string_pretty(&output)?; println!("\n{}", json_output); - } - else { + } else { println!("\n{}", output.response); } @@ -146,7 +145,8 @@ async fn main() -> Result<()> { // If no stdin, we need files to search through if args.files.is_empty() { - let error_msg = "No input provided. Either specify files as arguments or pipe input to stdin."; + let error_msg = + "No input provided. Either specify files as arguments or pipe input to stdin."; if args.json { let error_output = ErrorOutput { error: error_msg.to_string(), @@ -155,8 +155,7 @@ async fn main() -> Result<()> { let json_output = serde_json::to_string_pretty(&error_output)?; eprintln!("{}", json_output); std::process::exit(1); - } - else { + } else { eprintln!("{}", error_msg); } std::process::exit(1); @@ -199,8 +198,7 @@ async fn main() -> Result<()> { if args.json { let json_output = serde_json::to_string_pretty(&output)?; println!("\n{}", json_output); - } - else { + } else { println!("\n{}", output.response); } diff --git a/src/bin/search.rs b/src/bin/search.rs index ebe284a4..2a2fc24b 100644 --- a/src/bin/search.rs +++ b/src/bin/search.rs @@ -201,7 +201,8 @@ async fn main() -> Result<()> { } if args.files.is_empty() { - let error_msg = "No input provided. Either specify files as arguments or pipe input to stdin."; + let error_msg = + "No input provided. Either specify files as arguments or pipe input to stdin."; if args.json { let error_output = ErrorOutput { error: error_msg.to_string(), @@ -238,14 +239,15 @@ async fn main() -> Result<()> { let end = match_line_number + args.n_lines + 1; // Read file content for the result - let content = if let Ok(file_content) = std::fs::read_to_string(&ranked_line.path) { - let lines: Vec<&str> = file_content.lines().collect(); - let actual_start = start; - let actual_end = end.min(lines.len()); - lines[actual_start..actual_end].join("\n") - } else { - "[Error: Could not read file content]".to_string() - }; + let content = + if let Ok(file_content) = std::fs::read_to_string(&ranked_line.path) { + let lines: Vec<&str> = file_content.lines().collect(); + let actual_start = start; + let actual_end = end.min(lines.len()); + lines[actual_start..actual_end].join("\n") + } else { + "[Error: Could not read file content]".to_string() + }; SearchResultJSON { filename: ranked_line.path.clone(), diff --git a/src/bin/workspace.rs b/src/bin/workspace.rs index f81ef2ea..3e1f92cd 100644 --- a/src/bin/workspace.rs +++ b/src/bin/workspace.rs @@ -50,7 +50,8 @@ async fn main() -> Result<()> { if args.json { // Try to get document count from store, or use 0 for new workspace - let total_documents = if let Ok(store) = Store::open(&ws.config.root_dir).await { + let total_documents = if let Ok(store) = Store::open(&ws.config.root_dir).await + { if let Ok(stats) = store.get_stats().await { stats.total_documents } else { diff --git a/src/json_mode.rs b/src/json_mode.rs index a86e5036..1871dc98 100644 --- a/src/json_mode.rs +++ b/src/json_mode.rs @@ -13,8 +13,7 @@ pub struct ParseOutput { pub results: Vec, } - -// Search +// Search #[derive(Debug, Serialize)] pub struct SearchResultJSON { pub filename: String, @@ -57,4 +56,4 @@ pub struct PruneOutput { pub struct ErrorOutput { pub error: String, pub error_type: String, -} \ No newline at end of file +} From 06987862ab783d3763717df7ea6f9cd8ebefb348 Mon Sep 17 00:00:00 2001 From: Logan Markewich Date: Wed, 4 Feb 2026 09:48:37 -0600 Subject: [PATCH 3/5] make clippy happy --- src/bin/workspace.rs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/bin/workspace.rs b/src/bin/workspace.rs index 3e1f92cd..4946ca19 100644 --- a/src/bin/workspace.rs +++ b/src/bin/workspace.rs @@ -168,19 +168,17 @@ async fn main() -> Result<()> { }; let json_output = serde_json::to_string_pretty(&output)?; println!("{}", json_output); + } else if missing_paths.is_empty() { + println!("No stale documents found. Workspace is clean."); } else { - if missing_paths.is_empty() { - println!("No stale documents found. Workspace is clean."); - } else { - println!("Found {} stale documents:", missing_paths.len()); - for path in &missing_paths { - println!(" - {path}"); - } - println!( - "Removed {} stale documents from workspace.", - missing_paths.len() - ); + println!("Found {} stale documents:", missing_paths.len()); + for path in &missing_paths { + println!(" - {path}"); } + println!( + "Removed {} stale documents from workspace.", + missing_paths.len() + ); } } #[cfg(not(feature = "workspace"))] From 388d7891182182e31e66a1604c5dad0d23ac26ac Mon Sep 17 00:00:00 2001 From: Logan Markewich Date: Wed, 4 Feb 2026 10:57:08 -0600 Subject: [PATCH 4/5] fix security issues? --- Cargo.lock | 9 +++++---- Cargo.toml | 3 ++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 987f60fe..07fb3233 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -706,9 +706,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "bzip2" @@ -5314,6 +5314,7 @@ dependencies = [ "arrow-array", "arrow-schema", "async-openai", + "bytes", "clap", "dirs 5.0.1", "futures", @@ -6208,9 +6209,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ "bitflags", "bytes", diff --git a/Cargo.toml b/Cargo.toml index 568f8a42..049904c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,6 +40,7 @@ serde = { version = "1.0.219", features = ["derive"] } serde_json = "1.0.143" # Parse-specific dependencies +bytes = { version = "1.11.1", optional = true } reqwest = { version = "0.12.23", features = ["multipart", "json", "rustls-tls"], default-features = false, optional = true } tokio = { version = "1.47.1", features = ["full"], optional = true } sha2 = { version = "0.10.8", optional = true } @@ -69,7 +70,7 @@ tempfile = "3.14.0" [features] default = ["parse", "search", "workspace", "ask"] -parse = ["reqwest", "tokio", "sha2", "hex", "mime_guess"] +parse = ["bytes", "reqwest", "tokio", "sha2", "hex", "mime_guess"] search = ["model2vec-rs", "simsimd"] workspace = ["tokio", "lancedb", "arrow-schema", "arrow-array", "rand"] ask = ["async-openai", "model2vec-rs", "simsimd", "tokio", "grep", "grep-searcher", "grep-matcher", "grep-regex", "regex"] From 65a480ed3cecd7b1414d15c7c41f3f2c61f0f085 Mon Sep 17 00:00:00 2001 From: Logan Markewich Date: Wed, 4 Feb 2026 21:58:55 -0600 Subject: [PATCH 5/5] address offline review comments --- src/ask/chat_agent.rs | 162 ++---------------------------------- src/ask/mod.rs | 1 + src/ask/responses_agent.rs | 163 ++----------------------------------- src/ask/tool_calling.rs | 154 +++++++++++++++++++++++++++++++++++ 4 files changed, 171 insertions(+), 309 deletions(-) create mode 100644 src/ask/tool_calling.rs diff --git a/src/ask/chat_agent.rs b/src/ask/chat_agent.rs index 52a5013f..1075e9a5 100644 --- a/src/ask/chat_agent.rs +++ b/src/ask/chat_agent.rs @@ -1,5 +1,3 @@ -use std::vec; - use anyhow::Result; use async_openai::config::OpenAIConfig; use async_openai::types::chat::{ @@ -9,12 +7,11 @@ use async_openai::types::chat::{ }; use async_openai::{Client, types::chat::CreateChatCompletionRequestArgs}; use model2vec_rs::model::StaticModel; -use serde_json::Value; use crate::ask::system_prompt::{STDIN_SYSTEM_PROMPT, SYSTEM_PROMPT}; +use crate::ask::tool_calling::{call_tool, print_tool_summary}; use crate::ask::tools::{AgentTool, GrepTool, ReadTool, SearchTool}; use crate::json_mode::AskOutput; -use crate::search::SearchConfig; /// Run an agent loop with the search and read tools /// @@ -123,164 +120,19 @@ pub async fn ask_agent( // No tool calls - we have a final response if let Some(content) = response_message.content { result.response = content.clone(); - return Ok(result); } else { - return Err(anyhow::anyhow!("No content in final response")); + result.response = "".to_string(); } + + return Ok(result); } } - Err(anyhow::anyhow!( + result.response = format!( "Max iterations ({}) reached without final response", max_iterations - )) -} - -/// Call a tool by name with the given arguments -async fn call_tool( - name: &str, - args: &str, - files: &[String], - model: &StaticModel, - cur_output: &mut AskOutput, -) -> Result { - let function_args: Value = serde_json::from_str(args)?; - - match name { - "grep" => { - let pattern = function_args["pattern"] - .as_str() - .ok_or_else(|| anyhow::anyhow!("Missing 'pattern' parameter"))?; - - let file_paths: Option> = - function_args["file_paths"].as_array().map(|arr| { - arr.iter() - .filter_map(|v| v.as_str().map(|s| s.to_string())) - .collect() - }); - - // Update files_searched in cur_output - if let Some(paths) = file_paths.clone() { - for path in paths { - if !cur_output.files_searched.contains(&path) { - cur_output.files_searched.push(path); - } - } - } - - let is_regex = function_args["is_regex"].as_bool().unwrap_or(false); - let case_sensitive = function_args["case_sensitive"].as_bool().unwrap_or(true); - let context_lines = function_args["context_lines"].as_u64().unwrap_or(3) as usize; - - // Log the tool call - println!("\n[Tool Call: grep]"); - println!(" pattern: \"{}\"", pattern); - println!(" is_regex: {}", is_regex); - println!(" case_sensitive: {}", case_sensitive); - println!(" context_lines: {}", context_lines); - if let Some(ref paths) = file_paths - && !paths.is_empty() - { - println!(" file_paths: {:?}", paths); - } - - GrepTool::grep( - files, - pattern, - file_paths, - is_regex, - case_sensitive, - context_lines, - ) - .await - } - "search" => { - let query = function_args["query"] - .as_str() - .ok_or_else(|| anyhow::anyhow!("Missing 'query' parameter"))?; - - let config_json = &function_args["config"]; - let n_lines = config_json["n_lines"].as_u64().unwrap_or(5) as usize; - let ignore_case = config_json["ignore_case"].as_bool().unwrap_or(false); - let max_distance = config_json["max_distance"].as_f64(); - let top_k = config_json["top_k"].as_u64().unwrap_or(3) as usize; - - let config = SearchConfig { - n_lines, - ignore_case, - max_distance, - top_k, - }; - - // Log the tool call with formatted parameters - println!("\n[Tool Call: search]"); - println!(" query: \"{}\"", query); - println!(" config:"); - println!(" n_lines: {}", n_lines); - println!(" ignore_case: {}", ignore_case); - - // Max distance and top_k are mutually exclusive - if let Some(md) = max_distance { - println!(" max_distance: {:?}", md); - } else { - println!(" top_k: {}", top_k); - } - - SearchTool::search(files, query, model, config, &mut cur_output.files_searched).await - } - "read" => { - let path = function_args["path"] - .as_str() - .ok_or_else(|| anyhow::anyhow!("Missing 'path' parameter"))?; - let start_line = function_args["start_line"] - .as_u64() - .ok_or_else(|| anyhow::anyhow!("Missing 'start_line' parameter"))? - as usize; - let end_line = function_args["end_line"] - .as_u64() - .ok_or_else(|| anyhow::anyhow!("Missing 'end_line' parameter"))? - as usize; - - // Log the tool call with formatted parameters - println!("\n[Tool Call: read]"); - println!(" path: {}", path); - println!(" start_line: {}", start_line); - println!(" end_line: {}", end_line); - - ReadTool::read(path, start_line, end_line).await - } - _ => Err(anyhow::anyhow!("Unknown tool: {}", name)), - } -} - -/// Print a summary of the tool response -fn print_tool_summary(response: &str) { - // Count the number of tags - let chunk_count = response.matches(" - chunk - .split_once(">") - .and_then(|(_, rest)| rest.split_once("")) - .map(|(content, _)| content.lines().count()) - }) - .sum(); - - if chunk_count > 0 { - println!( - " → Returned {} chunk(s) with {} total lines", - chunk_count, total_lines - ); - } else if response.contains("No matches found") { - println!(" → No matches found"); - } else { - println!(" → Returned {} lines", response.lines().count()); - } + ); + Ok(result) } /// Run an agent with stdin content injected directly (no tools available) diff --git a/src/ask/mod.rs b/src/ask/mod.rs index 5676d3b1..f9539281 100644 --- a/src/ask/mod.rs +++ b/src/ask/mod.rs @@ -1,4 +1,5 @@ pub mod chat_agent; pub mod responses_agent; mod system_prompt; +mod tool_calling; pub mod tools; diff --git a/src/ask/responses_agent.rs b/src/ask/responses_agent.rs index 9abe857d..ad59d496 100644 --- a/src/ask/responses_agent.rs +++ b/src/ask/responses_agent.rs @@ -7,12 +7,11 @@ use async_openai::types::responses::{ MessageType, OutputItem, Role, Tool, }; use model2vec_rs::model::StaticModel; -use serde_json::Value; use crate::ask::system_prompt::{STDIN_SYSTEM_PROMPT, SYSTEM_PROMPT}; +use crate::ask::tool_calling::{call_tool, print_tool_summary}; use crate::ask::tools::{AgentTool, GrepTool, ReadTool, SearchTool}; use crate::json_mode::AskOutput; -use crate::search::SearchConfig; /// Run an agent loop with the search and read tools using the Responses API /// @@ -35,7 +34,11 @@ pub async fn ask_agent_responses( max_iterations: Option, ) -> Result { let max_iterations = max_iterations.unwrap_or(20); - let mut files_searched: Vec = Vec::new(); + let mut result = AskOutput { + query: user_message.to_string(), + response: String::new(), + files_searched: vec![], + }; // Build the tools using the responses API format let tools: Vec = vec![ @@ -93,8 +96,7 @@ pub async fn ask_agent_responses( let args = &function_call.arguments; // Call the appropriate tool - let response_content = - call_tool(name, args, &files, model, &mut files_searched).await?; + let response_content = call_tool(name, args, &files, model, &mut result).await?; // Print summary of the tool response print_tool_summary(&response_content); @@ -118,7 +120,7 @@ pub async fn ask_agent_responses( return Ok(AskOutput { query: user_message.to_string(), response: response_text, - files_searched, + files_searched: result.files_searched, }); } } @@ -130,157 +132,10 @@ pub async fn ask_agent_responses( "Max iterations ({}) reached without final response", max_iterations ), - files_searched, + files_searched: result.files_searched, }) } -/// Call a tool by name with the given arguments -async fn call_tool( - name: &str, - args: &str, - files: &[String], - model: &StaticModel, - files_searched: &mut Vec, -) -> Result { - let function_args: Value = serde_json::from_str(args)?; - - match name { - "grep" => { - let pattern = function_args["pattern"] - .as_str() - .ok_or_else(|| anyhow::anyhow!("Missing 'pattern' parameter"))?; - - let file_paths: Option> = - function_args["file_paths"].as_array().map(|arr| { - arr.iter() - .filter_map(|v| v.as_str().map(|s| s.to_string())) - .collect() - }); - - // Update files_searched - if let Some(paths) = file_paths.clone() { - for path in paths { - if !files_searched.contains(&path) { - files_searched.push(path); - } - } - } - - let is_regex = function_args["is_regex"].as_bool().unwrap_or(false); - let case_sensitive = function_args["case_sensitive"].as_bool().unwrap_or(true); - let context_lines = function_args["context_lines"].as_u64().unwrap_or(3) as usize; - - // Log the tool call - println!("\n[Tool Call: grep]"); - println!(" pattern: \"{}\"", pattern); - println!(" is_regex: {}", is_regex); - println!(" case_sensitive: {}", case_sensitive); - println!(" context_lines: {}", context_lines); - if let Some(ref paths) = file_paths - && !paths.is_empty() - { - println!(" file_paths: {:?}", paths); - } - - GrepTool::grep( - files, - pattern, - file_paths, - is_regex, - case_sensitive, - context_lines, - ) - .await - } - "search" => { - let query = function_args["query"] - .as_str() - .ok_or_else(|| anyhow::anyhow!("Missing 'query' parameter"))?; - - let config_json = &function_args["config"]; - let n_lines = config_json["n_lines"].as_u64().unwrap_or(5) as usize; - let ignore_case = config_json["ignore_case"].as_bool().unwrap_or(false); - let max_distance = config_json["max_distance"].as_f64(); - let top_k = config_json["top_k"].as_u64().unwrap_or(3) as usize; - - let config = SearchConfig { - n_lines, - ignore_case, - max_distance, - top_k, - }; - - // Log the tool call with formatted parameters - println!("\n[Tool Call: search]"); - println!(" query: \"{}\"", query); - println!(" config:"); - println!(" n_lines: {}", n_lines); - println!(" ignore_case: {}", ignore_case); - - // Max distance and top_k are mutually exclusive - if let Some(md) = max_distance { - println!(" max_distance: {:?}", md); - } else { - println!(" top_k: {}", top_k); - } - - SearchTool::search(files, query, model, config, files_searched).await - } - "read" => { - let path = function_args["path"] - .as_str() - .ok_or_else(|| anyhow::anyhow!("Missing 'path' parameter"))?; - let start_line = function_args["start_line"] - .as_u64() - .ok_or_else(|| anyhow::anyhow!("Missing 'start_line' parameter"))? - as usize; - let end_line = function_args["end_line"] - .as_u64() - .ok_or_else(|| anyhow::anyhow!("Missing 'end_line' parameter"))? - as usize; - - // Log the tool call with formatted parameters - println!("\n[Tool Call: read]"); - println!(" path: {}", path); - println!(" start_line: {}", start_line); - println!(" end_line: {}", end_line); - - ReadTool::read(path, start_line, end_line).await - } - _ => Err(anyhow::anyhow!("Unknown tool: {}", name)), - } -} - -/// Print a summary of the tool response -fn print_tool_summary(response: &str) { - // Count the number of tags - let chunk_count = response.matches(" - chunk - .split_once(">") - .and_then(|(_, rest)| rest.split_once("")) - .map(|(content, _)| content.lines().count()) - }) - .sum(); - - if chunk_count > 0 { - println!( - " → Returned {} chunk(s) with {} total lines", - chunk_count, total_lines - ); - } else if response.contains("No matches found") { - println!(" → No matches found"); - } else { - println!(" → Returned {} lines", response.lines().count()); - } -} - /// Convert an OutputItem to an Item for including in conversation history fn output_item_to_item(output_item: &OutputItem) -> Result { match output_item { diff --git a/src/ask/tool_calling.rs b/src/ask/tool_calling.rs new file mode 100644 index 00000000..ba9b5700 --- /dev/null +++ b/src/ask/tool_calling.rs @@ -0,0 +1,154 @@ +use anyhow::Result; +use model2vec_rs::model::StaticModel; +use serde_json::Value; + +use crate::ask::tools::{GrepTool, ReadTool, SearchTool}; +use crate::json_mode::AskOutput; +use crate::search::SearchConfig; + +/// Call a tool by name with the given arguments +pub async fn call_tool( + name: &str, + args: &str, + files: &[String], + model: &StaticModel, + cur_output: &mut AskOutput, +) -> Result { + let function_args: Value = serde_json::from_str(args)?; + + match name { + "grep" => { + let pattern = function_args["pattern"] + .as_str() + .ok_or_else(|| anyhow::anyhow!("Missing 'pattern' parameter"))?; + + let file_paths: Option> = + function_args["file_paths"].as_array().map(|arr| { + arr.iter() + .filter_map(|v| v.as_str().map(|s| s.to_string())) + .collect() + }); + + // Update files_searched in cur_output + if let Some(paths) = file_paths.clone() { + for path in paths { + if !cur_output.files_searched.contains(&path) { + cur_output.files_searched.push(path); + } + } + } + + let is_regex = function_args["is_regex"].as_bool().unwrap_or(false); + let case_sensitive = function_args["case_sensitive"].as_bool().unwrap_or(true); + let context_lines = function_args["context_lines"].as_u64().unwrap_or(3) as usize; + + // Log the tool call + println!("\n[Tool Call: grep]"); + println!(" pattern: \"{}\"", pattern); + println!(" is_regex: {}", is_regex); + println!(" case_sensitive: {}", case_sensitive); + println!(" context_lines: {}", context_lines); + if let Some(ref paths) = file_paths + && !paths.is_empty() + { + println!(" file_paths: {:?}", paths); + } + + GrepTool::grep( + files, + pattern, + file_paths, + is_regex, + case_sensitive, + context_lines, + ) + .await + } + "search" => { + let query = function_args["query"] + .as_str() + .ok_or_else(|| anyhow::anyhow!("Missing 'query' parameter"))?; + + let config_json = &function_args["config"]; + let n_lines = config_json["n_lines"].as_u64().unwrap_or(5) as usize; + let ignore_case = config_json["ignore_case"].as_bool().unwrap_or(false); + let max_distance = config_json["max_distance"].as_f64(); + let top_k = config_json["top_k"].as_u64().unwrap_or(3) as usize; + + let config = SearchConfig { + n_lines, + ignore_case, + max_distance, + top_k, + }; + + // Log the tool call with formatted parameters + println!("\n[Tool Call: search]"); + println!(" query: \"{}\"", query); + println!(" config:"); + println!(" n_lines: {}", n_lines); + println!(" ignore_case: {}", ignore_case); + + // Max distance and top_k are mutually exclusive + if let Some(md) = max_distance { + println!(" max_distance: {:?}", md); + } else { + println!(" top_k: {}", top_k); + } + + SearchTool::search(files, query, model, config, &mut cur_output.files_searched).await + } + "read" => { + let path = function_args["path"] + .as_str() + .ok_or_else(|| anyhow::anyhow!("Missing 'path' parameter"))?; + let start_line = function_args["start_line"] + .as_u64() + .ok_or_else(|| anyhow::anyhow!("Missing 'start_line' parameter"))? + as usize; + let end_line = function_args["end_line"] + .as_u64() + .ok_or_else(|| anyhow::anyhow!("Missing 'end_line' parameter"))? + as usize; + + // Log the tool call with formatted parameters + println!("\n[Tool Call: read]"); + println!(" path: {}", path); + println!(" start_line: {}", start_line); + println!(" end_line: {}", end_line); + + ReadTool::read(path, start_line, end_line).await + } + _ => Err(anyhow::anyhow!("Unknown tool: {}", name)), + } +} + +/// Print a summary of the tool response +pub fn print_tool_summary(response: &str) { + // Count the number of tags + let chunk_count = response.matches(" + chunk + .split_once(">") + .and_then(|(_, rest)| rest.split_once("")) + .map(|(content, _)| content.lines().count()) + }) + .sum(); + + if chunk_count > 0 { + println!( + " → Returned {} chunk(s) with {} total lines", + chunk_count, total_lines + ); + } else if response.contains("No matches found") { + println!(" → No matches found"); + } else { + println!(" → Returned {} lines", response.lines().count()); + } +}