use anyhow::Result;
use clap::Parser;
use crate::cli::drive::format::OutputFormat;
use crate::drive::account::ResolvedAccount;
use crate::drive::auth;
use crate::drive::client::DriveClient;
use crate::drive::write_gate::FolderPermissionRule;
use crate::utils::settings::{DriveAccountSettings, Settings};
#[derive(Parser)]
pub struct LeaseTokenArg {
#[arg(long, value_name = "TOKEN")]
pub lease: Option<String>,
}
#[derive(Parser)]
pub struct StructureWriteArgs {
#[arg(long)]
pub dry_run: bool,
#[command(flatten)]
pub lease: LeaseTokenArg,
#[arg(short = 'o', long, value_enum, default_value_t = OutputFormat::Table)]
pub output: OutputFormat,
}
pub fn create_client() -> Result<DriveClient> {
create_client_for(None)
}
pub fn create_client_for(account: Option<&str>) -> Result<DriveClient> {
create_client_from(auth::load_credentials_for(account)?)
}
pub fn create_client_from(credentials: auth::DriveCredentials) -> Result<DriveClient> {
DriveClient::from_credentials(&credentials)
}
fn account_field<T>(
account: Option<&str>,
f: impl FnOnce(&DriveAccountSettings) -> Option<T>,
) -> Result<Option<T>> {
let settings = Settings::load_or_warn_default();
let resolved = auth::resolve(&settings.drive, account)?;
Ok(match &resolved {
ResolvedAccount::Named(name) => settings.drive.accounts.get(name).and_then(f),
ResolvedAccount::Unconfigured => None,
})
}
pub fn active_account_rules() -> Result<Vec<FolderPermissionRule>> {
account_rules(None)
}
pub fn active_account_lease_backup_folder_id() -> Result<Option<String>> {
account_lease_backup_folder_id(None)
}
pub(crate) fn account_rules(account: Option<&str>) -> Result<Vec<FolderPermissionRule>> {
Ok(account_field(account, |a| Some(a.write_permissions.rules.clone()))?.unwrap_or_default())
}
pub(crate) fn account_lease_backup_folder_id(account: Option<&str>) -> Result<Option<String>> {
account_field(account, |a| a.lease_backup_folder_id.clone())
}
pub fn resolve_ledger_path(dry_run: bool) -> Result<std::path::PathBuf> {
if dry_run {
Ok(std::path::PathBuf::new())
} else {
crate::drive::lease::ledger::ledger_path()
}
}
pub fn render_list(rows: &[String], empty: &str) -> String {
if rows.is_empty() {
return format!("{empty}\n");
}
let mut out = String::new();
for row in rows {
out.push_str(&crate::cli::drive::format::sanitize_for_terminal(row));
out.push('\n');
}
out
}
pub fn print_list(rows: &[String], empty: &str) {
print!("{}", render_list(rows, empty));
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
use crate::drive::auth::{DriveCredentials, DriveGrantedScopes};
use crate::utils::secret::Secret;
#[test]
fn render_list_says_so_when_there_are_no_rows() {
assert_eq!(render_list(&[], "No bandings."), "No bandings.\n");
}
#[test]
fn render_list_prints_each_row_sanitised() {
let rows = vec!["id 1".to_string(), "id \u{1b}[31m2".to_string()];
assert_eq!(render_list(&rows, "No bandings."), "id 1\nid [31m2\n");
}
#[test]
fn create_client_from_uses_drive_api_host() {
let creds = DriveCredentials {
client_id: "client".to_string(),
client_secret: Secret::new("secret"),
refresh_token: Secret::new("refresh"),
scope: DriveGrantedScopes::READONLY,
};
let client = create_client_from(creds).unwrap();
assert_eq!(client.base_url(), "https://www.googleapis.com");
}
#[test]
fn create_client_for_named_account_uses_that_accounts_credentials() {
let guard = crate::drive::test_support::EnvGuard::take();
let dir = guard.clear_credentials();
let settings_path = dir.path().join(".omni-dev").join("settings.json");
crate::utils::settings::Settings::upsert_drive_account(
&settings_path,
"work",
&[
(
"client_id",
serde_json::Value::String("work-id".to_string()),
),
(
"client_secret",
serde_json::Value::String("work-secret".to_string()),
),
(
"refresh_token",
serde_json::Value::String("work-refresh".to_string()),
),
],
)
.unwrap();
let client = create_client_for(Some("work")).unwrap();
assert_eq!(client.base_url(), "https://www.googleapis.com");
}
#[test]
fn create_client_for_unknown_account_errors() {
let guard = crate::drive::test_support::EnvGuard::take();
let _dir = guard.clear_credentials();
let err = create_client_for(Some("bogus")).unwrap_err();
assert!(err.to_string().contains("unknown Drive account 'bogus'"));
}
#[test]
fn active_account_rules_unconfigured_account_is_empty() {
let guard = crate::drive::test_support::EnvGuard::take();
let _dir = guard.clear_credentials();
assert!(active_account_rules().unwrap().is_empty());
}
#[test]
fn active_account_rules_reads_the_sole_configured_accounts_rules() {
let guard = crate::drive::test_support::EnvGuard::take();
let dir = guard.clear_credentials();
let settings_path = dir.path().join(".omni-dev").join("settings.json");
crate::utils::settings::Settings::upsert_drive_account(
&settings_path,
"work",
&[(
"write_permissions",
serde_json::json!({
"rules": [{
"folder_id": "folder-1",
"recursive": true,
"allow": ["create"],
}],
}),
)],
)
.unwrap();
let rules = active_account_rules().unwrap();
assert_eq!(rules.len(), 1);
assert_eq!(rules[0].folder_id.as_deref(), Some("folder-1"));
assert!(rules[0]
.allow
.contains(&crate::drive::write_gate::DriveOperation::Create));
}
#[test]
fn active_account_rules_warns_when_settings_json_fails_to_parse() {
let guard = crate::drive::test_support::EnvGuard::take();
let dir = guard.clear_credentials();
let settings_dir = dir.path().join(".omni-dev");
std::fs::create_dir_all(&settings_dir).unwrap();
std::fs::write(settings_dir.join("settings.json"), "{not valid json").unwrap();
let logs = crate::test_support::capture_at(tracing::Level::WARN, || {
assert!(active_account_rules().unwrap().is_empty());
});
assert!(logs.contains("settings.json"), "{logs}");
}
#[test]
fn active_account_lease_backup_folder_id_is_none_for_an_unconfigured_account() {
let guard = crate::drive::test_support::EnvGuard::take();
let _dir = guard.clear_credentials();
assert_eq!(active_account_lease_backup_folder_id().unwrap(), None);
}
#[test]
fn active_account_lease_backup_folder_id_is_none_when_the_account_sets_none() {
let guard = crate::drive::test_support::EnvGuard::take();
let dir = guard.clear_credentials();
let settings_path = dir.path().join(".omni-dev").join("settings.json");
crate::utils::settings::Settings::upsert_drive_account(
&settings_path,
"work",
&[(
"client_id",
serde_json::Value::String("work-id".to_string()),
)],
)
.unwrap();
assert_eq!(active_account_lease_backup_folder_id().unwrap(), None);
}
#[test]
fn active_account_lease_backup_folder_id_reads_the_sole_configured_accounts_folder() {
let guard = crate::drive::test_support::EnvGuard::take();
let dir = guard.clear_credentials();
let settings_path = dir.path().join(".omni-dev").join("settings.json");
crate::utils::settings::Settings::upsert_drive_account(
&settings_path,
"work",
&[(
"lease_backup_folder_id",
serde_json::Value::String("backup-folder-1".to_string()),
)],
)
.unwrap();
assert_eq!(
active_account_lease_backup_folder_id().unwrap().as_deref(),
Some("backup-folder-1")
);
}
#[test]
fn resolve_ledger_path_for_a_real_run_resolves_the_ledger_path() {
let path = resolve_ledger_path(false).unwrap();
assert!(path.ends_with("lease-ledger.jsonl"), "{}", path.display());
assert_eq!(
resolve_ledger_path(true).unwrap(),
std::path::PathBuf::new()
);
}
}