bmux_cli 0.0.1-alpha.1

Command-line interface for bmux terminal multiplexer
use super::state::PaneRect;
use bmux_attach_layout_protocol::{PaneLayoutNode, PaneSplitDirection};
use std::collections::BTreeMap;
use uuid::Uuid;

#[allow(dead_code)] // Will be used when multi-pane layout is wired into the attach runtime
pub fn collect_pane_ids(layout: &PaneLayoutNode, out: &mut Vec<Uuid>) {
    match layout {
        PaneLayoutNode::Leaf { pane_id } => out.push(*pane_id),
        PaneLayoutNode::Split { first, second, .. } => {
            collect_pane_ids(first, out);
            collect_pane_ids(second, out);
        }
    }
}

#[allow(clippy::cast_possible_truncation)] // Product of two u16 values divided by 100 always fits in u16
fn split_rect(rect: PaneRect, ratio_percent: u8, vertical: bool) -> (PaneRect, PaneRect) {
    if vertical {
        let split = ((u32::from(rect.w) * u32::from(ratio_percent)) / 100) as u16;
        let left_w = split.max(1).min(rect.w.saturating_sub(1));
        let right_w = rect.w.saturating_sub(left_w);
        (
            PaneRect {
                x: rect.x,
                y: rect.y,
                w: left_w,
                h: rect.h,
            },
            PaneRect {
                x: rect.x.saturating_add(left_w),
                y: rect.y,
                w: right_w,
                h: rect.h,
            },
        )
    } else {
        let split = ((u32::from(rect.h) * u32::from(ratio_percent)) / 100) as u16;
        let top_h = split.max(1).min(rect.h.saturating_sub(1));
        let bottom_h = rect.h.saturating_sub(top_h);
        (
            PaneRect {
                x: rect.x,
                y: rect.y,
                w: rect.w,
                h: top_h,
            },
            PaneRect {
                x: rect.x,
                y: rect.y.saturating_add(top_h),
                w: rect.w,
                h: bottom_h,
            },
        )
    }
}

#[allow(dead_code)] // Will be used when multi-pane layout is wired into the attach runtime
pub fn collect_layout_rects(
    layout: &PaneLayoutNode,
    rect: PaneRect,
    out: &mut BTreeMap<Uuid, PaneRect>,
) {
    match layout {
        PaneLayoutNode::Leaf { pane_id } => {
            out.insert(*pane_id, rect);
        }
        PaneLayoutNode::Split {
            direction,
            ratio_percent,
            first,
            second,
        } => {
            let vertical = matches!(direction, PaneSplitDirection::Vertical);
            let (first_rect, second_rect) = split_rect(rect, *ratio_percent, vertical);
            collect_layout_rects(first, first_rect, out);
            collect_layout_rects(second, second_rect, out);
        }
    }
}