use std::cell::Cell;
use std::ops::{Deref, DerefMut};
use ratatui::crossterm::event::KeyCode;
use ratatui::crossterm::event::KeyEvent;
use crate::i18n::Strings;
use super::super::editor::Editor;
use super::wizard::{NewField, NewReq, WizardTab};
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub(crate) enum KvdKind {
Header,
Cookie,
Query,
Options,
}
impl KvdKind {
pub(crate) const ALL: [KvdKind; 4] = [
KvdKind::Header,
KvdKind::Cookie,
KvdKind::Query,
KvdKind::Options,
];
pub(crate) fn field(self, i: usize, col: HdrCol) -> NewField {
NewField::Kvd(self, i, col)
}
pub(crate) fn add_field(self) -> NewField {
NewField::AddKvd(self)
}
pub(crate) fn wizard_tab(self) -> WizardTab {
match self {
KvdKind::Header => WizardTab::Headers,
KvdKind::Cookie => WizardTab::Cookies,
KvdKind::Query => WizardTab::Queries,
KvdKind::Options => WizardTab::Options,
}
}
pub(crate) fn title(self, s: &Strings) -> &'static str {
match self {
KvdKind::Header => s.field_headers,
KvdKind::Cookie => s.field_cookies,
KvdKind::Query => s.field_queries,
KvdKind::Options => s.field_options,
}
}
pub(crate) fn add_label(self, s: &Strings) -> &'static str {
match self {
KvdKind::Header => s.add_header,
KvdKind::Cookie => s.add_cookie,
KvdKind::Query => s.add_query,
KvdKind::Options => s.add_option,
}
}
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub(crate) enum HdrCol {
Key,
Value,
Desc,
Enabled,
}
pub(crate) struct HeaderRow {
pub(crate) key: Editor,
pub(crate) value: Editor,
pub(crate) desc: Editor,
pub(crate) enabled: bool,
}
impl HeaderRow {
pub(crate) fn new() -> Self {
Self {
key: Editor::blank(),
value: Editor::blank(),
desc: Editor::blank(),
enabled: true,
}
}
pub(crate) fn is_blank(&self) -> bool {
self.key.text().is_empty() && self.value.text().is_empty() && self.desc.text().is_empty()
}
pub(crate) fn cell_mut(&mut self, col: HdrCol) -> Option<&mut Editor> {
match col {
HdrCol::Key => Some(&mut self.key),
HdrCol::Value => Some(&mut self.value),
HdrCol::Desc => Some(&mut self.desc),
HdrCol::Enabled => None,
}
}
pub(crate) fn cell(&self, col: HdrCol) -> Option<&Editor> {
match col {
HdrCol::Key => Some(&self.key),
HdrCol::Value => Some(&self.value),
HdrCol::Desc => Some(&self.desc),
HdrCol::Enabled => None,
}
}
}
pub(crate) struct KvdSection {
pub(crate) rows: Vec<HeaderRow>,
pub(crate) desc_visible: Cell<bool>,
pub(crate) scroll: Cell<usize>,
}
impl KvdSection {
pub(crate) fn new() -> Self {
Self {
rows: Vec::new(),
desc_visible: Cell::new(true),
scroll: Cell::new(0),
}
}
pub(crate) fn from_rows(rows: Vec<HeaderRow>) -> Self {
Self {
rows,
..Self::new()
}
}
pub(crate) fn is_blank(&self) -> bool {
self.rows.iter().all(HeaderRow::is_blank)
}
pub(crate) fn row_cells(&self) -> Vec<HdrCol> {
if self.desc_visible.get() {
vec![HdrCol::Enabled, HdrCol::Key, HdrCol::Value, HdrCol::Desc]
} else {
vec![HdrCol::Enabled, HdrCol::Key, HdrCol::Value]
}
}
pub(crate) fn tab_cells(&self) -> Vec<HdrCol> {
if self.desc_visible.get() {
vec![HdrCol::Key, HdrCol::Value, HdrCol::Desc]
} else {
vec![HdrCol::Key, HdrCol::Value]
}
}
pub(crate) fn prev_col(&self, col: HdrCol) -> Option<HdrCol> {
let cells = self.row_cells();
let idx = cells.iter().position(|c| *c == col)?;
idx.checked_sub(1).map(|p| cells[p])
}
pub(crate) fn next_col(&self, col: HdrCol) -> Option<HdrCol> {
let cells = self.row_cells();
let idx = cells.iter().position(|c| *c == col)?;
cells.get(idx + 1).copied()
}
}
impl Default for KvdSection {
fn default() -> Self {
Self::new()
}
}
impl Deref for KvdSection {
type Target = Vec<HeaderRow>;
fn deref(&self) -> &Self::Target {
&self.rows
}
}
impl DerefMut for KvdSection {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.rows
}
}
impl NewReq {
pub(crate) fn kvd(&self, kind: KvdKind) -> &KvdSection {
match kind {
KvdKind::Header => &self.headers,
KvdKind::Cookie => &self.cookies,
KvdKind::Query => &self.queries,
KvdKind::Options => &self.options,
}
}
pub(crate) fn kvd_mut(&mut self, kind: KvdKind) -> &mut KvdSection {
match kind {
KvdKind::Header => &mut self.headers,
KvdKind::Cookie => &mut self.cookies,
KvdKind::Query => &mut self.queries,
KvdKind::Options => &mut self.options,
}
}
pub(crate) fn kvd_blank(&self, kind: KvdKind) -> bool {
self.kvd(kind).is_blank()
}
pub(crate) fn kvd_entry(&self, kind: KvdKind) -> NewField {
if self.kvd(kind).is_empty() {
kind.add_field()
} else {
kind.field(0, HdrCol::Key)
}
}
pub(crate) fn up_into_kvd(&self, kind: KvdKind) -> NewField {
kind.add_field()
}
fn kvd_up_destination(&self, kind: KvdKind) -> NewField {
match kind {
KvdKind::Header => NewField::Url,
KvdKind::Cookie => self.up_into_kvd(KvdKind::Header),
KvdKind::Query => self.up_into_kvd(KvdKind::Cookie),
KvdKind::Options => self.up_into_kvd(KvdKind::Query),
}
}
pub(crate) fn handle_kvd_key(&mut self, kind: KvdKind, i: usize, col: HdrCol, key: &KeyEvent) {
let ctrl = key
.modifiers
.contains(ratatui::crossterm::event::KeyModifiers::CONTROL);
match key.code {
KeyCode::Up => {
self.focus = if i > 0 {
kind.field(i - 1, col)
} else {
self.kvd_up_destination(kind)
};
}
KeyCode::Down => {
self.focus = if i + 1 < self.kvd(kind).len() {
kind.field(i + 1, col)
} else {
kind.add_field()
};
}
KeyCode::Left => {
let at_start = self.kvd(kind)[i]
.cell(col)
.map(|ed| ed.col == 0)
.unwrap_or(true);
if !at_start {
if let Some(ed) = self.kvd_mut(kind)[i].cell_mut(col) {
if ctrl {
ed.home()
} else {
ed.left();
}
}
} else if let Some(prev) = self.kvd(kind).prev_col(col) {
if let Some(ed) = self.kvd_mut(kind)[i].cell_mut(prev) {
ed.end();
}
self.focus = kind.field(i, prev);
}
}
KeyCode::Right => {
let at_end = self.kvd(kind)[i]
.cell(col)
.map(|ed| ed.col >= ed.line_len(ed.row))
.unwrap_or(true);
if !at_end {
if let Some(ed) = self.kvd_mut(kind)[i].cell_mut(col) {
if ctrl {
ed.end();
} else {
ed.right();
}
}
} else if let Some(next) = self.kvd(kind).next_col(col) {
if let Some(ed) = self.kvd_mut(kind)[i].cell_mut(next) {
ed.home();
}
self.focus = kind.field(i, next);
}
}
KeyCode::Enter => self.focus_next(true, true),
KeyCode::Char(' ') if col == HdrCol::Enabled => {
if let Some(row) = self.kvd_mut(kind).get_mut(i) {
row.enabled = !row.enabled;
}
}
_ => {
if let Some(ed) = self.kvd_mut(kind)[i].cell_mut(col) {
match key.code {
KeyCode::Char(ch) => ed.insert(ch),
KeyCode::Backspace => ed.backspace(),
KeyCode::Home => ed.home(),
KeyCode::End => ed.end(),
_ => {}
}
}
}
}
}
}