hitman 0.12.1

A command line tool for hitting API endpoints
Documentation
use crossterm::event::Event;
use hitman::substitute::SubstitutionValue;
use ratatui::{
    layout::Rect,
    style::Stylize,
    text::{Line, Span},
    widgets::{Block, Clear, Paragraph},
    Frame,
};
use tui_input::{backend::crossterm::EventHandler, Input};

use super::{
    centered,
    keymap::{mapkey, KeyMapping},
    Component, PromptComponent, PromptIntent,
};

pub struct SimplePrompt {
    title: String,
    fallback: Option<String>,
    input: Input,
}

impl SimplePrompt {
    pub fn new(title: String) -> Self {
        Self {
            title,
            fallback: None,
            input: Input::default(),
        }
    }

    pub fn with_fallback(self, value: Option<String>) -> Self {
        Self {
            fallback: value,
            ..self
        }
    }

    fn value(&self) -> SubstitutionValue<String> {
        let input_value = self.input.value().to_string();
        if input_value.is_empty() {
            SubstitutionValue::Single(
                self.fallback.clone().unwrap_or(input_value),
            )
        } else {
            SubstitutionValue::Single(input_value)
        }
    }
}

impl Component for SimplePrompt {
    fn render_ui(&mut self, frame: &mut Frame, area: Rect) {
        let area = centered(area, 40, 3);

        let block = Block::bordered().cyan().title(self.title.clone());
        let inner = block.inner(area);

        let input_value = self.input.value();
        let mut spans = Vec::new();
        spans.push(Span::from("> "));
        spans.push(Span::from(input_value));
        let cur = spans[0].width() as u16;

        if input_value.is_empty() {
            if let Some(value) = &self.fallback {
                spans.push(Span::from(value).dark_gray());
            }
        }

        frame.render_widget(Clear, area);
        frame.render_widget(
            Paragraph::new(Line::from(spans)).white().block(block),
            area,
        );

        frame.set_cursor_position((
            inner.x + cur + self.input.visual_cursor() as u16,
            inner.y,
        ));
    }
}

impl PromptComponent for SimplePrompt {
    fn handle_prompt(&mut self, event: &Event) -> Option<PromptIntent> {
        match mapkey(event) {
            KeyMapping::Accept => {
                return Some(PromptIntent::Accept(self.value()));
            }
            KeyMapping::Abort => {
                return Some(PromptIntent::Abort);
            }
            _ => (),
        }

        self.input.handle_event(event);

        None
    }
}