Skip to main content

systemprompt_cli/commands/cloud/tenant/
select.rs

1//! Interactive tenant selection.
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use anyhow::{Result, anyhow, bail};
7use systemprompt_cloud::{
8    CloudCredentials, CloudPath, CredentialsBootstrap, StoredTenant, TenantType, get_cloud_paths,
9};
10use systemprompt_config::ProfileBootstrap;
11use systemprompt_identifiers::TenantId;
12
13use crate::interactive::Prompter;
14
15pub fn get_credentials() -> Result<CloudCredentials> {
16    if CredentialsBootstrap::is_initialized() {
17        return CredentialsBootstrap::require()
18            .map_err(|e| {
19                anyhow!(
20                    "Credentials required. Run 'systemprompt cloud login': {}",
21                    e
22                )
23            })?
24            .clone()
25            .pipe(Ok);
26    }
27
28    let cloud_paths = get_cloud_paths();
29    let creds_path = cloud_paths.resolve(CloudPath::Credentials);
30
31    if creds_path.exists() {
32        CloudCredentials::load_from_path(&creds_path).map_err(Into::into)
33    } else {
34        bail!("Not logged in. Run 'systemprompt cloud login' first.")
35    }
36}
37
38pub fn select_tenant<'a>(
39    prompter: &dyn Prompter,
40    tenants: &'a [StoredTenant],
41) -> Result<&'a StoredTenant> {
42    let options: Vec<String> = tenants
43        .iter()
44        .map(|t| {
45            let type_str = match t.tenant_type {
46                TenantType::Local => "local",
47                TenantType::Cloud => "cloud",
48            };
49            format!("{} ({})", t.name, type_str)
50        })
51        .collect();
52
53    let selection = prompter.select("Select tenant", &options)?;
54
55    Ok(&tenants[selection])
56}
57
58pub fn resolve_tenant_id(tenant: Option<String>) -> Result<TenantId> {
59    if let Some(id) = tenant {
60        return Ok(TenantId::new(id));
61    }
62
63    ProfileBootstrap::get()
64        .ok()
65        .and_then(|p| p.cloud.as_ref()?.tenant_id.as_ref().map(TenantId::new))
66        .ok_or_else(|| {
67            anyhow!("No tenant specified. Use --tenant or configure a tenant in your profile.")
68        })
69}
70
71trait Pipe: Sized {
72    fn pipe<F, R>(self, f: F) -> R
73    where
74        F: FnOnce(Self) -> R,
75    {
76        f(self)
77    }
78}
79
80impl<T> Pipe for T {}