@@ -115,6 +115,14 @@ pub struct MountListRow {
115 pub default_mount: String,
115 pub default_mount: String,
118+#[derive(Debug, Clone)]
119+pub struct NetworkListRow {
120+ pub network_type: String,
122+ pub host_to_vm: String,
123+ pub vm_to_host: String,
118#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
126#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
119struct PageLayout {
127struct PageLayout {
120 header: Rect,
128 header: Rect,
@@ -253,6 +261,65 @@ pub fn render_mounts_table(rows: &[MountListRow]) -> Result<()> {
253 let mut buffer = Buffer::empty(Rect::new(0, 0, width, height));
261 let mut buffer = Buffer::empty(Rect::new(0, 0, width, height));
254 let area = Rect::new(0, 0, width, height);
262 let area = Rect::new(0, 0, width, height);
264+ render_mounts_table_into(rows, area, &mut buffer);
266+ let mut stdout = io::stdout();
267+ execute!(stdout, Clear(ClearType::All), MoveTo(0, 0), Show)?;
268+ write_buffer_with_style(&buffer, &mut stdout)?;
273+pub fn render_explain_tables(mounts: &[MountListRow], networks: &[NetworkListRow]) -> Result<()> {
274+ let (width, _) = crossterm::terminal::size()?;
279+ let mounts_height = if mounts.is_empty() {
282+ (mounts.len() as u16).saturating_add(3)
284+ let networks_height = if networks.is_empty() {
287+ (networks.len() as u16).saturating_add(3)
289+ let gap = if mounts_height > 0 && networks_height > 0 {
294+ let total_height = mounts_height
295+ .saturating_add(gap)
296+ .saturating_add(networks_height);
297+ if total_height == 0 {
301+ let mut buffer = Buffer::empty(Rect::new(0, 0, width, total_height));
304+ if mounts_height > 0 {
305+ let area = Rect::new(0, y, width, mounts_height);
306+ render_mounts_table_into(mounts, area, &mut buffer);
307+ y = y.saturating_add(mounts_height).saturating_add(gap);
310+ if networks_height > 0 {
311+ let area = Rect::new(0, y, width, networks_height);
312+ render_networks_table_into(networks, area, &mut buffer);
315+ let mut stdout = io::stdout();
316+ execute!(stdout, Clear(ClearType::All), MoveTo(0, 0), Show)?;
317+ write_buffer_with_style(&buffer, &mut stdout)?;
322+fn render_mounts_table_into(rows: &[MountListRow], area: Rect, buffer: &mut Buffer) {
256 let header = Row::new(vec![
323 let header = Row::new(vec![
257 Cell::from("Host"),
324 Cell::from("Host"),
258 Cell::from("Guest"),
325 Cell::from("Guest"),
@@ -286,13 +353,41 @@ pub fn render_mounts_table(rows: &[MountListRow]) -> Result<()> {
286 .block(Block::default().title("Mounts").borders(Borders::ALL))
353 .block(Block::default().title("Mounts").borders(Borders::ALL))
287 .column_spacing(1);
354 .column_spacing(1);
289− table.render(area, &mut buffer);
356+ table.render(area, buffer);
291− let mut stdout = io::stdout();
359+fn render_networks_table_into(rows: &[NetworkListRow], area: Rect, buffer: &mut Buffer) {
292− execute!(stdout, Clear(ClearType::All), MoveTo(0, 0), Show)?;
360+ let header = Row::new(vec![
293− write_buffer_with_style(&buffer, &mut stdout)?;
361+ Cell::from("Type"),
294− stdout.flush()?;
362+ Cell::from("VM IP"),
295− Ok(())
363+ Cell::from("Host \u{2192} VM"),
364+ Cell::from("VM \u{2192} Host"),
366+ .style(Style::default().fg(Color::Cyan));
368+ let table_rows = rows.iter().map(|row| {
370+ Cell::from(row.network_type.clone()),
371+ Cell::from(row.vm_ip.clone()),
372+ Cell::from(row.host_to_vm.clone()),
373+ Cell::from(row.vm_to_host.clone()),
377+ let table = Table::new(
380+ Constraint::Length(8),
381+ Constraint::Length(16),
382+ Constraint::Min(24),
383+ Constraint::Min(20),
387+ .block(Block::default().title("Network").borders(Borders::ALL))
390+ table.render(area, buffer);
298pub fn passthrough_vm_io(
393pub fn passthrough_vm_io(