pub const MAX_ISSUE_LEN: usize = 7;
pub const MAX_DESC_LEN: usize = 200;
pub const MAX_NAME_LEN: usize = crate::naming::MAX_DIR_COMPONENT_BYTES;
#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
pub enum Mode {
#[default]
Structured,
Freeform,
}
#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
pub enum Field {
#[default]
Type,
Issue,
Desc,
Name,
}
impl Field {
fn from_segment(segment: &str) -> Option<Self> {
match segment {
"type" => Some(Field::Type),
"issue" => Some(Field::Issue),
"desc" => Some(Field::Desc),
_ => None,
}
}
}
pub fn fields_for(patterns: &[&str]) -> Vec<Field> {
crate::naming::editable_segments(patterns)
.into_iter()
.filter_map(Field::from_segment)
.collect()
}
const DEFAULT_FIELDS: [Field; 3] = [Field::Type, Field::Issue, Field::Desc];
#[derive(Debug)]
pub struct CreateForm {
pub mode: Mode,
pub field: Field,
pub type_index: usize,
pub issue: String,
pub desc: String,
pub name: String,
fields: Vec<Field>,
}
impl Default for CreateForm {
fn default() -> Self {
Self {
mode: Mode::default(),
field: Field::default(),
type_index: 0,
issue: String::new(),
desc: String::new(),
name: String::new(),
fields: DEFAULT_FIELDS.to_vec(),
}
}
}
impl CreateForm {
pub fn new() -> Self {
Self::default()
}
pub fn set_fields(&mut self, fields: Vec<Field>) {
self.fields = fields;
if self.mode == Mode::Structured && !self.fields.contains(&self.field) {
self.field = self.fields.first().copied().unwrap_or_default();
}
}
pub fn fields(&self) -> &[Field] {
&self.fields
}
pub fn entry_field(&self) -> Field {
self
.fields
.iter()
.copied()
.find(|f| *f != Field::Type)
.or_else(|| self.fields.first().copied())
.unwrap_or_default()
}
pub fn last_field(&self) -> Field {
self.fields.last().copied().unwrap_or_default()
}
pub fn reset(&mut self) {
self.mode = Mode::Structured;
self.field = self.fields.first().copied().unwrap_or_default();
self.type_index = 0;
self.issue.clear();
self.desc.clear();
self.name.clear();
}
pub fn toggle_mode(&mut self) {
match self.mode {
Mode::Structured => {
self.mode = Mode::Freeform;
self.field = Field::Name;
}
Mode::Freeform => {
self.mode = Mode::Structured;
self.field = self.entry_field();
}
}
}
pub fn next_field(&mut self) {
self.rotate(1);
}
pub fn prev_field(&mut self) {
self.rotate(-1);
}
fn rotate(&mut self, step: isize) {
if self.mode == Mode::Freeform || self.fields.is_empty() {
return;
}
let at = self.fields.iter().position(|f| *f == self.field).unwrap_or(0) as isize;
let len = self.fields.len() as isize;
self.field = self.fields[(at + step).rem_euclid(len) as usize];
}
pub fn next_type(&mut self, types_len: usize) {
if types_len == 0 {
return;
}
self.type_index = (self.type_index + 1) % types_len;
}
pub fn prev_type(&mut self, types_len: usize) {
if types_len == 0 {
return;
}
if self.type_index == 0 {
self.type_index = types_len - 1;
} else {
self.type_index -= 1;
}
}
pub fn push_char(&mut self, c: char) {
if !self.accepts_input() {
return;
}
match self.field {
Field::Issue if c.is_ascii_digit() && self.issue.chars().count() < MAX_ISSUE_LEN => self.issue.push(c),
Field::Desc if self.desc.chars().count() < MAX_DESC_LEN => self.desc.push(c),
Field::Name if self.name.len() + c.len_utf8() <= MAX_NAME_LEN => self.name.push(c),
_ => {}
}
}
fn accepts_input(&self) -> bool {
match self.mode {
Mode::Freeform => self.field == Field::Name,
Mode::Structured => self.fields.contains(&self.field),
}
}
pub fn pop_char(&mut self) {
if !self.accepts_input() {
return;
}
match self.field {
Field::Issue => {
self.issue.pop();
}
Field::Desc => {
self.desc.pop();
}
Field::Name => {
self.name.pop();
}
_ => {}
}
}
}