fabro/lib/crates/fabro-cli/src/command_context.rs
Bryan Helmkamp aa8cdd6986
refactor(cli): rename ServerStoreClient -> Client, fabro_api::Client -> ApiClient
The CLI façade is the primary type callers reach for, so it deserves
the bare `Client` name (per `reqwest::Client`, `hyper::Client`
convention). The raw generated HTTP binding is secondary and is more
accurately named `ApiClient`. "Store" in `ServerStoreClient` was
leftover from the SlateDB-ownership refactor and no longer describes
the type.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 08:56:09 -04:00

167 lines
4.8 KiB
Rust

use std::path::{Path, PathBuf};
use std::sync::Arc;
use anyhow::{Context as _, Result, bail};
use fabro_config::merge::combine_files;
use fabro_types::settings::cli::CliLayer;
use fabro_types::settings::{CliSettings, SettingsLayer};
use fabro_util::printer::Printer;
use tokio::sync::OnceCell;
use crate::args::{ServerConnectionArgs, ServerTargetArgs};
use crate::server_client::Client;
use crate::{server_client, user_config};
#[derive(Clone, Debug)]
pub(crate) enum ServerMode {
None,
ByTarget {
target_override: Option<String>,
},
ByStorageDir {
target_override: Option<String>,
storage_dir_override: Option<PathBuf>,
},
}
pub(crate) struct CommandContext {
#[allow(
dead_code,
reason = "This item is kept for command plumbing that is still being wired through."
)]
printer: Printer,
cwd: PathBuf,
base_config_path: PathBuf,
machine_settings: SettingsLayer,
cli_settings: CliSettings,
server_mode: ServerMode,
server: OnceCell<Arc<Client>>,
}
impl CommandContext {
pub(crate) fn base(
printer: Printer,
cli_settings: CliSettings,
cli_layer: &CliLayer,
) -> Result<Self> {
Self::new(printer, ServerMode::None, cli_settings, cli_layer)
}
pub(crate) fn for_target(
args: &ServerTargetArgs,
printer: Printer,
cli_settings: CliSettings,
cli_layer: &CliLayer,
) -> Result<Self> {
Self::new(
printer,
ServerMode::ByTarget {
target_override: args.server.clone(),
},
cli_settings,
cli_layer,
)
}
pub(crate) fn for_connection(
args: &ServerConnectionArgs,
printer: Printer,
cli_settings: CliSettings,
cli_layer: &CliLayer,
) -> Result<Self> {
Self::new(
printer,
ServerMode::ByStorageDir {
target_override: args.target.server.clone(),
storage_dir_override: args.storage_dir.clone_path(),
},
cli_settings,
cli_layer,
)
}
fn new(
printer: Printer,
server_mode: ServerMode,
cli_settings: CliSettings,
cli_layer: &CliLayer,
) -> Result<Self> {
let cwd = std::env::current_dir().context("Failed to get current directory")?;
let base_config_path = user_config::active_settings_path(None);
let disk_settings = match &server_mode {
ServerMode::None | ServerMode::ByTarget { .. } => user_config::load_settings()?,
ServerMode::ByStorageDir {
storage_dir_override,
..
} => user_config::load_settings_with_storage_dir(storage_dir_override.as_deref())?,
};
let machine_settings = combine_files(disk_settings, SettingsLayer {
cli: Some(cli_layer.clone()),
..SettingsLayer::default()
});
Ok(Self {
printer,
cwd,
base_config_path,
machine_settings,
cli_settings,
server_mode,
server: OnceCell::new(),
})
}
#[allow(
dead_code,
reason = "This item is kept for command plumbing that is still being wired through."
)]
pub(crate) fn printer(&self) -> Printer {
self.printer
}
pub(crate) fn cwd(&self) -> &Path {
&self.cwd
}
pub(crate) fn base_config_path(&self) -> &Path {
&self.base_config_path
}
pub(crate) fn machine_settings(&self) -> &SettingsLayer {
&self.machine_settings
}
pub(crate) fn cli_settings(&self) -> &CliSettings {
&self.cli_settings
}
pub(crate) async fn server(&self) -> Result<Arc<Client>> {
let server_mode = self.server_mode.clone();
let base_config_path = self.base_config_path.clone();
let machine_settings = self.machine_settings.clone();
let client = self
.server
.get_or_try_init(|| async move {
let target = match server_mode {
ServerMode::None => bail!("This command context does not have server access"),
ServerMode::ByTarget { target_override }
| ServerMode::ByStorageDir {
target_override, ..
} => ServerTargetArgs {
server: target_override,
},
};
server_client::connect_server_with_settings(
&target,
&machine_settings,
&base_config_path,
)
.await
.map(Arc::new)
})
.await?;
Ok(Arc::clone(client))
}
}