mirror of
https://github.com/robcholz/vibebox.git
synced 2026-10-11 01:08:41 +02:00
fix: added back explain
This commit is contained in:
1 parent
1451c31a4e
commit
dfecd12e12
3 files changed
+151
-117
No files matched your search
+7
-117
@@ -14,7 +14,9 @@ use time::format_description::well_known::Rfc3339;
|
|||||||
use tracing_subscriber::EnvFilter;
|
use tracing_subscriber::EnvFilter;
|
||||||
|
|
||||||
use vibebox::tui::{AppState, VmInfo};
|
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)]
|
#[derive(Debug, Parser)]
|
||||||
#[command(name = "vibebox", version, about = "Vibebox CLI")]
|
#[command(name = "vibebox", version, about = "Vibebox CLI")]
|
||||||
@@ -168,8 +170,10 @@ fn handle_command(command: Command, cwd: &PathBuf, config_override: Option<&Path
|
|||||||
}
|
}
|
||||||
Command::Explain => {
|
Command::Explain => {
|
||||||
let config = config::load_config_with_path(cwd, config_override);
|
let config = config::load_config_with_path(cwd, config_override);
|
||||||
let mounts = build_mount_rows(cwd, &config)?;
|
let mounts = explain::build_mount_rows(cwd, &config)
|
||||||
let networks = build_network_rows(cwd)?;
|
.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() {
|
if mounts.is_empty() && networks.is_empty() {
|
||||||
println!("No mounts or network info available.");
|
println!("No mounts or network info available.");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@@ -244,120 +248,6 @@ fn format_last_active(value: Option<&str>) -> String {
|
|||||||
format!("{} day{} ago", days, if days == 1 { "" } else { "s" })
|
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() {
|
fn init_tracing() {
|
||||||
let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));
|
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();
|
let ansi = std::io::stderr().is_terminal() && env::var("VIBEBOX_LOG_NO_COLOR").is_err();
|
||||||
|
|||||||
+143
@@ -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 commands;
|
||||||
|
pub mod explain;
|
||||||
pub mod instance;
|
pub mod instance;
|
||||||
pub mod session_manager;
|
pub mod session_manager;
|
||||||
pub mod tui;
|
pub mod tui;
|
||||||
|
|||||||
Reference in new issue
Block a user