mirror of
https://github.com/robcholz/vibebox.git
synced 2026-04-01 00:10:15 +02:00
fix: added back explain
This commit is contained in:
@@ -14,7 +14,9 @@ use time::format_description::well_known::Rfc3339;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
|
||||
use vibebox::tui::{AppState, VmInfo};
|
||||
use vibebox::{SessionManager, commands, config, instance, session_manager, tui, vm, vm_manager};
|
||||
use vibebox::{
|
||||
SessionManager, commands, config, explain, instance, session_manager, tui, vm, vm_manager,
|
||||
};
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(name = "vibebox", version, about = "Vibebox CLI")]
|
||||
@@ -168,8 +170,10 @@ fn handle_command(command: Command, cwd: &PathBuf, config_override: Option<&Path
|
||||
}
|
||||
Command::Explain => {
|
||||
let config = config::load_config_with_path(cwd, config_override);
|
||||
let mounts = build_mount_rows(cwd, &config)?;
|
||||
let networks = build_network_rows(cwd)?;
|
||||
let mounts = explain::build_mount_rows(cwd, &config)
|
||||
.map_err(|err| color_eyre::eyre::eyre!(err.to_string()))?;
|
||||
let networks = explain::build_network_rows(cwd)
|
||||
.map_err(|err| color_eyre::eyre::eyre!(err.to_string()))?;
|
||||
if mounts.is_empty() && networks.is_empty() {
|
||||
println!("No mounts or network info available.");
|
||||
return Ok(());
|
||||
@@ -244,120 +248,6 @@ fn format_last_active(value: Option<&str>) -> String {
|
||||
format!("{} day{} ago", days, if days == 1 { "" } else { "s" })
|
||||
}
|
||||
|
||||
fn build_mount_rows(cwd: &Path, config: &config::Config) -> Result<Vec<tui::MountListRow>> {
|
||||
let mut rows = Vec::new();
|
||||
rows.extend(default_mounts(cwd)?);
|
||||
for spec in &config.box_cfg.mounts {
|
||||
rows.push(parse_mount_spec(cwd, spec, false)?);
|
||||
}
|
||||
Ok(rows)
|
||||
}
|
||||
|
||||
fn build_network_rows(cwd: &Path) -> Result<Vec<tui::NetworkListRow>> {
|
||||
let instance_dir = cwd.join(session_manager::INSTANCE_DIR_NAME);
|
||||
let mut vm_ip = "-".to_string();
|
||||
if let Ok(Some(ip)) = instance::read_instance_vm_ip(&instance_dir) {
|
||||
vm_ip = ip;
|
||||
}
|
||||
let host_to_vm = if vm_ip == "-" {
|
||||
"ssh: <pending>:22".to_string()
|
||||
} else {
|
||||
format!("ssh: {vm_ip}:22")
|
||||
};
|
||||
let row = tui::NetworkListRow {
|
||||
network_type: "NAT".to_string(),
|
||||
vm_ip: vm_ip.clone(),
|
||||
host_to_vm,
|
||||
vm_to_host: "none".to_string(),
|
||||
};
|
||||
Ok(vec![row])
|
||||
}
|
||||
|
||||
fn default_mounts(cwd: &Path) -> Result<Vec<tui::MountListRow>> {
|
||||
let project_name = cwd
|
||||
.file_name()
|
||||
.and_then(|name| name.to_str())
|
||||
.unwrap_or("project");
|
||||
let project_guest = format!("/root/{project_name}");
|
||||
let project_host = relative_to_home(&cwd.to_path_buf());
|
||||
let mut rows = vec![tui::MountListRow {
|
||||
host: project_host,
|
||||
guest: project_guest,
|
||||
mode: "read-write".to_string(),
|
||||
default_mount: "yes".to_string(),
|
||||
}];
|
||||
|
||||
let home = env::var("HOME")
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(|_| PathBuf::from("/"));
|
||||
let cache_home = env::var("XDG_CACHE_HOME")
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(|_| home.join(".cache"));
|
||||
let cache_dir = cache_home.join(session_manager::GLOBAL_CACHE_DIR_NAME);
|
||||
let guest_mise_cache = cache_dir.join(".guest-mise-cache");
|
||||
rows.push(tui::MountListRow {
|
||||
host: relative_to_home(&guest_mise_cache),
|
||||
guest: "/root/.local/share/mise".to_string(),
|
||||
mode: "read-write".to_string(),
|
||||
default_mount: "yes".to_string(),
|
||||
});
|
||||
Ok(rows)
|
||||
}
|
||||
|
||||
fn parse_mount_spec(cwd: &Path, spec: &str, default_mount: bool) -> Result<tui::MountListRow> {
|
||||
let parts: Vec<&str> = spec.split(':').collect();
|
||||
if parts.len() < 2 || parts.len() > 3 {
|
||||
return Err(color_eyre::eyre::eyre!("invalid mount spec: {spec}"));
|
||||
}
|
||||
let host_part = parts[0];
|
||||
let guest_part = parts[1];
|
||||
let mode = if parts.len() == 3 {
|
||||
match parts[2] {
|
||||
"read-only" => "read-only",
|
||||
"read-write" => "read-write",
|
||||
other => {
|
||||
return Err(color_eyre::eyre::eyre!(
|
||||
"invalid mount mode '{}'; expected read-only or read-write",
|
||||
other
|
||||
));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
"read-write"
|
||||
};
|
||||
let host_path = resolve_host_path(cwd, host_part);
|
||||
let host_display = relative_to_home(&host_path);
|
||||
let guest_display = if Path::new(guest_part).is_absolute() {
|
||||
guest_part.to_string()
|
||||
} else {
|
||||
format!("/root/{guest_part}")
|
||||
};
|
||||
Ok(tui::MountListRow {
|
||||
host: host_display,
|
||||
guest: guest_display,
|
||||
mode: mode.to_string(),
|
||||
default_mount: if default_mount { "yes" } else { "no" }.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
fn resolve_host_path(cwd: &Path, host: &str) -> PathBuf {
|
||||
if let Some(stripped) = host.strip_prefix("~/") {
|
||||
if let Ok(home) = env::var("HOME") {
|
||||
return PathBuf::from(home).join(stripped);
|
||||
}
|
||||
} else if host == "~" {
|
||||
if let Ok(home) = env::var("HOME") {
|
||||
return PathBuf::from(home);
|
||||
}
|
||||
}
|
||||
let host_path = PathBuf::from(host);
|
||||
if host_path.is_absolute() {
|
||||
host_path
|
||||
} else {
|
||||
cwd.join(host_path)
|
||||
}
|
||||
}
|
||||
|
||||
fn init_tracing() {
|
||||
let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));
|
||||
let ansi = std::io::stderr().is_terminal() && env::var("VIBEBOX_LOG_NO_COLOR").is_err();
|
||||
|
||||
143
src/explain.rs
Normal file
143
src/explain.rs
Normal file
@@ -0,0 +1,143 @@
|
||||
use std::{
|
||||
env,
|
||||
error::Error,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use crate::{config, instance, session_manager, tui};
|
||||
|
||||
pub fn build_mount_rows(
|
||||
cwd: &Path,
|
||||
config: &config::Config,
|
||||
) -> Result<Vec<tui::MountListRow>, Box<dyn Error + Send + Sync>> {
|
||||
let mut rows = Vec::new();
|
||||
rows.extend(default_mounts(cwd)?);
|
||||
for spec in &config.box_cfg.mounts {
|
||||
rows.push(parse_mount_spec(cwd, spec, false)?);
|
||||
}
|
||||
Ok(rows)
|
||||
}
|
||||
|
||||
pub fn build_network_rows(
|
||||
cwd: &Path,
|
||||
) -> Result<Vec<tui::NetworkListRow>, Box<dyn Error + Send + Sync>> {
|
||||
let instance_dir = cwd.join(session_manager::INSTANCE_DIR_NAME);
|
||||
let mut vm_ip = "-".to_string();
|
||||
if let Ok(Some(ip)) = instance::read_instance_vm_ip(&instance_dir) {
|
||||
vm_ip = ip;
|
||||
}
|
||||
let host_to_vm = if vm_ip == "-" {
|
||||
"ssh: <pending>:22".to_string()
|
||||
} else {
|
||||
format!("ssh: {vm_ip}:22")
|
||||
};
|
||||
let row = tui::NetworkListRow {
|
||||
network_type: "NAT".to_string(),
|
||||
vm_ip: vm_ip.clone(),
|
||||
host_to_vm,
|
||||
vm_to_host: "none".to_string(),
|
||||
};
|
||||
Ok(vec![row])
|
||||
}
|
||||
|
||||
fn default_mounts(cwd: &Path) -> Result<Vec<tui::MountListRow>, Box<dyn Error + Send + Sync>> {
|
||||
let project_name = cwd
|
||||
.file_name()
|
||||
.and_then(|name| name.to_str())
|
||||
.unwrap_or("project");
|
||||
let project_guest = format!("/root/{project_name}");
|
||||
let project_host = display_path(cwd);
|
||||
let mut rows = vec![tui::MountListRow {
|
||||
host: project_host,
|
||||
guest: project_guest,
|
||||
mode: "read-write".to_string(),
|
||||
default_mount: "yes".to_string(),
|
||||
}];
|
||||
|
||||
let home = env::var("HOME")
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(|_| PathBuf::from("/"));
|
||||
let cache_home = env::var("XDG_CACHE_HOME")
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(|_| home.join(".cache"));
|
||||
let cache_dir = cache_home.join(session_manager::GLOBAL_CACHE_DIR_NAME);
|
||||
let guest_mise_cache = cache_dir.join(".guest-mise-cache");
|
||||
rows.push(tui::MountListRow {
|
||||
host: display_path(&guest_mise_cache),
|
||||
guest: "/root/.local/share/mise".to_string(),
|
||||
mode: "read-write".to_string(),
|
||||
default_mount: "yes".to_string(),
|
||||
});
|
||||
Ok(rows)
|
||||
}
|
||||
|
||||
fn parse_mount_spec(
|
||||
cwd: &Path,
|
||||
spec: &str,
|
||||
default_mount: bool,
|
||||
) -> Result<tui::MountListRow, Box<dyn Error + Send + Sync>> {
|
||||
let parts: Vec<&str> = spec.split(':').collect();
|
||||
if parts.len() < 2 || parts.len() > 3 {
|
||||
return Err(format!("invalid mount spec: {spec}").into());
|
||||
}
|
||||
let host_part = parts[0];
|
||||
let guest_part = parts[1];
|
||||
let mode = if parts.len() == 3 {
|
||||
match parts[2] {
|
||||
"read-only" => "read-only",
|
||||
"read-write" => "read-write",
|
||||
other => {
|
||||
return Err(format!(
|
||||
"invalid mount mode '{}'; expected read-only or read-write",
|
||||
other
|
||||
)
|
||||
.into());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
"read-write"
|
||||
};
|
||||
|
||||
let host_display = display_host_spec(cwd, host_part);
|
||||
let guest_display = if Path::new(guest_part).is_absolute() {
|
||||
guest_part.to_string()
|
||||
} else {
|
||||
format!("/root/{guest_part}")
|
||||
};
|
||||
Ok(tui::MountListRow {
|
||||
host: host_display,
|
||||
guest: guest_display,
|
||||
mode: mode.to_string(),
|
||||
default_mount: if default_mount { "yes" } else { "no" }.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
fn display_host_spec(cwd: &Path, host: &str) -> String {
|
||||
if host == "~" || host.starts_with("~/") {
|
||||
return host.to_string();
|
||||
}
|
||||
let host_path = PathBuf::from(host);
|
||||
if host_path.is_absolute() {
|
||||
return display_path(&host_path);
|
||||
}
|
||||
let candidate = cwd.join(&host_path);
|
||||
if candidate.is_absolute() {
|
||||
display_path(&candidate)
|
||||
} else {
|
||||
host.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
fn display_path(path: &Path) -> String {
|
||||
let Ok(home) = env::var("HOME") else {
|
||||
return path.display().to_string();
|
||||
};
|
||||
let home_path = PathBuf::from(home);
|
||||
if let Ok(stripped) = path.strip_prefix(&home_path) {
|
||||
if stripped.components().next().is_none() {
|
||||
return "~".to_string();
|
||||
}
|
||||
return format!("~/{}", stripped.display());
|
||||
}
|
||||
path.display().to_string()
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod commands;
|
||||
pub mod explain;
|
||||
pub mod instance;
|
||||
pub mod session_manager;
|
||||
pub mod tui;
|
||||
|
||||
Reference in New Issue
Block a user