Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ sysinfo = { version = "0.38.4", default-features = false, features = ["system"]
self_update = { version = "0.42", default-features = false, features = ["rustls"] }
lzma-rs = "0.3"
tempfile = "3"
urlencoding = "2.1.3"

[dev-dependencies]
mockito = "1"
Expand Down
6 changes: 6 additions & 0 deletions src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub struct ApiClient {
pub api_url: String,
workspace_id: Option<String>,
sandbox_id: Option<String>,
database_id: Option<String>,
}

impl ApiClient {
Expand Down Expand Up @@ -117,6 +118,7 @@ impl ApiClient {
}
profile_config.sandbox
}),
database_id: workspace_id.and_then(|ws| crate::config::load_current_database("default", ws)),
}
}

Expand All @@ -129,6 +131,7 @@ impl ApiClient {
api_url: api_url.to_string(),
workspace_id: workspace_id.map(String::from),
sandbox_id: None,
database_id: None,
}
}

Expand Down Expand Up @@ -167,6 +170,9 @@ impl ApiClient {
req = req.header("X-Session-Id", sid);
req = req.header("X-Sandbox-Id", sid);
}
if let Some(ref db_id) = self.database_id {
req = req.header("X-Database-Id", db_id);
}
req
}

Expand Down
29 changes: 19 additions & 10 deletions src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ pub enum Commands {

/// Managed databases you create and populate with tables (parquet uploads)
Databases {
/// Database name or connection ID (omit to use a subcommand)
/// Database id or description (omit to use a subcommand)
Comment thread
eddietejeda marked this conversation as resolved.
name_or_id: Option<String>,

/// Workspace ID (defaults to first workspace from login)
Expand Down Expand Up @@ -557,15 +557,15 @@ pub enum DatabasesCommands {

/// Create a new managed database
Create {
/// Database name (used as the connection name in SQL: `name.schema.table`)
/// Optional display label (not unique, not an identifier — databases are addressed by id)
#[arg(long)]
name: String,
description: Option<String>,

/// Schema for tables declared at create time (default: public)
#[arg(long, default_value = "public")]
schema: String,

/// Table to declare up front (repeatable). Required before load on current API.
/// Table to declare up front (repeatable)
#[arg(long = "table")]
tables: Vec<String>,

Expand All @@ -574,6 +574,12 @@ pub enum DatabasesCommands {
output: String,
},

/// Set the current database (used by default when no database is specified)
Set {
/// Database id or description
id_or_description: String,
},

/// Delete a managed database and its tables
Delete {
/// Database name or connection ID
Expand Down Expand Up @@ -610,8 +616,9 @@ pub enum DatabasesCommands {
pub enum DatabaseTablesCommands {
/// List tables in a managed database
List {
/// Database name or connection ID
database: String,
/// Database id or description (defaults to current database)
#[arg(long)]
database: Option<String>,

/// Filter by schema name
#[arg(long)]
Expand All @@ -624,8 +631,9 @@ pub enum DatabaseTablesCommands {

/// Load a parquet file into a table (creates or replaces the table)
Load {
/// Database name or connection ID
database: String,
/// Database id or description (defaults to current database)
#[arg(long)]
database: Option<String>,

/// Table name
table: String,
Expand All @@ -649,8 +657,9 @@ pub enum DatabaseTablesCommands {

/// Delete a table from a managed database
Delete {
/// Database name or connection ID
database: String,
/// Database id or description (defaults to current database)
#[arg(long)]
database: Option<String>,

/// Table name
table: String,
Expand Down
56 changes: 56 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ pub struct ProfileConfig {
pub workspaces: Vec<WorkspaceEntry>,
#[serde(default, skip_serializing_if = "Option::is_none", alias = "session")]
pub sandbox: Option<String>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub current_databases: HashMap<String, String>,
}

#[derive(Debug, Deserialize, Serialize)]
Expand Down Expand Up @@ -227,6 +229,60 @@ pub fn clear_sandbox(profile: &str) -> Result<(), String> {
write_config(&config_path, &content)
}

pub fn save_current_database(profile: &str, workspace_id: &str, database_id: &str) -> Result<(), String> {
let config_path = config_path()?;

let mut config_file: ConfigFile = if config_path.exists() {
let content = fs::read_to_string(&config_path)
.map_err(|e| format!("error reading config file: {e}"))?;
serde_yaml::from_str(&content).map_err(|e| format!("error parsing config file: {e}"))?
} else {
ConfigFile { profiles: HashMap::new() }
};

config_file
.profiles
.entry(profile.to_string())
.or_default()
.current_databases
.insert(workspace_id.to_string(), database_id.to_string());

let content = serde_yaml::to_string(&config_file)
.map_err(|e| format!("error serializing config: {e}"))?;
write_config(&config_path, &content)
}

pub fn load_current_database(profile: &str, workspace_id: &str) -> Option<String> {
let config_path = config_path().ok()?;
if !config_path.exists() {
return None;
}
let content = fs::read_to_string(&config_path).ok()?;
let config_file: ConfigFile = serde_yaml::from_str(&content).ok()?;
config_file.profiles.get(profile)?.current_databases.get(workspace_id).cloned()
}

pub fn clear_current_database(profile: &str, workspace_id: &str) -> Result<(), String> {
let config_path = config_path()?;

if !config_path.exists() {
return Ok(());
}

let content = fs::read_to_string(&config_path)
.map_err(|e| format!("error reading config file: {e}"))?;
let mut config_file: ConfigFile =
serde_yaml::from_str(&content).map_err(|e| format!("error parsing config file: {e}"))?;

if let Some(entry) = config_file.profiles.get_mut(profile) {
entry.current_databases.remove(workspace_id);
}

let content = serde_yaml::to_string(&config_file)
.map_err(|e| format!("error serializing config: {e}"))?;
write_config(&config_path, &content)
}

pub fn resolve_workspace_id(provided: Option<String>, profile_config: &ProfileConfig) -> Result<String, String> {
if let Some(id) = provided {
return Ok(id);
Expand Down
Loading
Loading