Skip to content

Commit 37ccbf6

Browse files
committed
feat(indexes): workspace-wide list with filters and parallel fetch
Indexes list no longer requires connection, schema, and table. Defaults to all tables in the workspace; optional flags narrow the catalog scan. Resolve information_schema connection labels to connection IDs via GET /connections. Skip missing tables when the indexes endpoint returns 404 so stale catalog rows do not abort the run. Fetch per-table indexes in parallel with rayon to reduce wall-clock latency. Add ApiClient::Clone and get_none_if_not_found for the scan.
1 parent 1ea920d commit 37ccbf6

6 files changed

Lines changed: 294 additions & 32 deletions

File tree

Cargo.lock

Lines changed: 59 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ anstyle = "1.0.13"
1616
clap = { version = "4", features = ["derive"] }
1717
directories = "6"
1818
reqwest = { version = "0.12", features = ["blocking", "json"] }
19+
rayon = "1.10"
1920
serde = { version = "1", features = ["derive"] }
2021
serde_json = "1"
2122
serde_yaml = "0.9"

src/api.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::util;
44
use crossterm::style::Stylize;
55
use serde::de::DeserializeOwned;
66

7+
#[derive(Clone)]
78
pub struct ApiClient {
89
client: reqwest::blocking::Client,
910
api_key: String,
@@ -156,6 +157,37 @@ impl ApiClient {
156157
}
157158
}
158159

160+
/// GET request; returns `None` on HTTP 404. Other status codes use the same handling as
161+
/// [`Self::get`]. Used when probing many paths where a missing resource is normal.
162+
pub fn get_none_if_not_found<T: DeserializeOwned>(&self, path: &str) -> Option<T> {
163+
let url = format!("{}{path}", self.api_url);
164+
self.log_request("GET", &url, None);
165+
166+
let resp = match self.build_request(reqwest::Method::GET, &url).send() {
167+
Ok(r) => r,
168+
Err(e) => {
169+
eprintln!("error connecting to API: {e}");
170+
std::process::exit(1);
171+
}
172+
};
173+
174+
let (status, body) = util::debug_response(resp);
175+
if status == reqwest::StatusCode::NOT_FOUND {
176+
return None;
177+
}
178+
if !status.is_success() {
179+
self.fail_response(status, body);
180+
}
181+
182+
match serde_json::from_str(&body) {
183+
Ok(v) => Some(v),
184+
Err(e) => {
185+
eprintln!("error parsing response: {e}");
186+
std::process::exit(1);
187+
}
188+
}
189+
}
190+
159191
/// POST request with JSON body, returns parsed response.
160192
pub fn post<T: DeserializeOwned>(&self, path: &str, body: &serde_json::Value) -> T {
161193
let url = format!("{}{path}", self.api_url);

src/command.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -248,19 +248,19 @@ pub enum AuthCommands {
248248

249249
#[derive(Subcommand)]
250250
pub enum IndexesCommands {
251-
/// List indexes on a table
251+
/// List indexes (defaults to the whole workspace; narrow with filters)
252252
List {
253-
/// Connection ID
253+
/// Filter by connection ID
254254
#[arg(long, short = 'c')]
255-
connection_id: String,
255+
connection_id: Option<String>,
256256

257-
/// Schema name
257+
/// Filter by schema name
258258
#[arg(long)]
259-
schema: String,
259+
schema: Option<String>,
260260

261-
/// Table name
261+
/// Filter by table name
262262
#[arg(long)]
263-
table: String,
263+
table: Option<String>,
264264

265265
/// Output format
266266
#[arg(long = "output", short = 'o', default_value = "table", value_parser = ["table", "json", "yaml"])]

0 commit comments

Comments
 (0)