Skip to main content

ez_tui/forms/
field.rs

1use crate::EzCptIds;
2use Direction::{Horizontal, Vertical};
3use FormFieldType::{CheckBox, Date, DateTime, Number, Password, Range, Select, Text, Time};
4use ratatui::layout::{Constraint, Direction};
5use std::cmp::max;
6use std::fmt::Display;
7
8/// All the possible form field type with their configs if needed.
9#[derive(Debug, Eq, PartialEq, Clone)]
10pub enum FormFieldType {
11    /// A simple text input
12    Text {},
13    /// A text input with hidden characters
14    Password {},
15    /// A simple boolean input (checkbox like)
16    CheckBox {},
17    /// A number input
18    Number {},
19    /// A range between two numbers
20    Range {},
21    /// A date and time input
22    DateTime {},
23    /// A date only input
24    Date {},
25    /// A time only input
26    Time {},
27    /// A select input with a list of values and a default index
28    Select {
29        /// All the possible values for this select input
30        values: Vec<String>,
31        /// The index of values to be selected by default
32        default: usize,
33    },
34}
35/// The type of date and time configuration for a form field.
36#[derive(Debug, Eq, PartialEq, Clone)]
37pub enum DateTimeConfig {
38    /// A date only configuration
39    Date,
40    /// A time only configuration
41    Time,
42    /// A date and time configuration
43    DateTime,
44}
45
46/// Describe one field inside a form.
47#[derive(Debug, Clone)]
48pub struct FormField<FID>
49where
50    FID: EzCptIds,
51{
52    /// The unique identifier of this field.
53    id: FID,
54    /// The name of this field, used for display purposes.
55    name: String,
56    /// The type of this field
57    field_type: FormFieldType,
58    /// Optional constraint for this field, used to override the constraint computed automatically
59    constraint: Option<Constraint>,
60}
61
62impl<FID> FormField<FID>
63where
64    FID: EzCptIds,
65{
66    /// Create a form field with a given id, name and type
67    pub fn with_name(id: FID, name: String, field_type: FormFieldType) -> Self {
68        Self {
69            id,
70            name,
71            field_type,
72            constraint: None,
73        }
74    }
75}
76
77impl<FID> FormField<FID>
78where
79    FID: EzCptIds + Display,
80{
81    /// Create a form field with a given id and type, using the id as the name
82    pub fn new(id: FID, field_type: FormFieldType) -> Self {
83        let name = id.to_string();
84        Self {
85            id,
86            name,
87            field_type,
88            constraint: None,
89        }
90    }
91}
92impl<FID> FormField<FID>
93where
94    FID: EzCptIds,
95{
96    /// Return this field's id.
97    pub fn id(&self) -> &FID {
98        &self.id
99    }
100    /// Return this field's name.
101    pub fn name(&self) -> String {
102        self.name.to_string()
103    }
104
105    /// Return a reference to this field's type
106    pub fn field_type(&self) -> &FormFieldType {
107        &self.field_type
108    }
109
110    #[allow(dead_code)] // TODO: count might be used for ratio based constraints in the future
111    pub(crate) fn as_constraint(&self, direction: Direction, _count: usize) -> Constraint {
112        if let Some(constraint) = self.constraint {
113            constraint
114        } else {
115            let name_length = u16::try_from(self.name.len()).unwrap_or_default();
116            match (direction, &self.field_type) {
117                (Horizontal, Select { values, .. }) => {
118                    let width: u16 = values
119                        .iter()
120                        .map(|s| u16::try_from(s.len()).unwrap_or_default())
121                        .sum();
122                    Constraint::Min(width + 2)
123                }
124                (Vertical, Select { values, .. }) => {
125                    Constraint::Length(u16::try_from(values.len()).unwrap_or_default() + 2)
126                }
127
128                (Horizontal, Text {} | Password {} | Number {} | Range {} | Time {}) => {
129                    Constraint::Min(name_length)
130                }
131                (Vertical, Text {} | Password {} | Number {} | Range {} | Time {}) => {
132                    Constraint::Length(3)
133                }
134
135                (Horizontal, CheckBox {}) => Constraint::Max(max(name_length + 2, 5)),
136                (Vertical, CheckBox {}) => Constraint::Length(3),
137
138                (Horizontal, Date {} | DateTime {}) => Constraint::Min(max(name_length + 2, 20)),
139                (Vertical, Date {}) => Constraint::Length(9),
140                (Vertical, DateTime {}) => Constraint::Length(10),
141            }
142        }
143    }
144}