Lines
100 %
Functions
68.75 %
Branches
//! Free-text date entry widget.
//!
//! Wraps an [`Editor`] for free-text date entry (`YYYY-MM-DDTHH:MM` / RFC3339).
//! New widgets are pre-seeded with the current instant. Up/Down day-stepping is
//! available via [`DateWidget::step`].
use cli_core::forms::{now_template, step_date};
use super::editor::{EditMode, Editor};
/// A text field for date entry.
#[derive(Debug, Clone)]
pub struct DateWidget {
editor: Editor,
}
impl DateWidget {
/// Create a new date widget pre-seeded with the current instant (`YYYY-MM-DDTHH:MM`).
#[must_use]
pub fn new(mode: EditMode) -> Self {
Self {
editor: Editor::with_buffer(mode, now_template()),
pub fn with_value(mode: EditMode, value: impl Into<String>) -> Self {
editor: Editor::with_buffer(mode, value),
/// Buffer text (the current date string).
pub fn value(&self) -> &str {
self.editor.buffer()
/// Direct access to the inner editor.
pub fn as_editor_mut(&mut self) -> &mut Editor {
&mut self.editor
/// Advance the date by `delta_days` days, preserving the time-of-day component.
///
/// On parse failure the buffer is replaced with a now-relative step. On
/// date-range overflow the buffer is left unchanged.
pub fn step(&mut self, delta_days: i64) {
if let Some(new_val) = step_date(self.value(), delta_days) {
self.editor.replace_buffer(new_val);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_buffer_matches_now_template_format() {
let w = DateWidget::new(EditMode::Emacs);
let v = w.value();
assert_eq!(v.len(), 16, "YYYY-MM-DDTHH:MM is 16 chars, got: {v}");
assert!(v.contains('T'), "must contain T separator: {v}");
assert!(
cli_core::forms::validate_date(v).is_ok(),
"must be valid: {v}"
);
fn with_value_sets_initial_buffer() {
let w = DateWidget::with_value(EditMode::Emacs, "2024-12-31");
assert_eq!(w.value(), "2024-12-31");
fn free_typing_appends_to_buffer() {
let mut w = DateWidget::with_value(EditMode::Emacs, "");
for c in "2024-01-01".chars() {
w.as_editor_mut().insert_char(c);
assert_eq!(w.value(), "2024-01-01");
fn step_forward_advances_one_day() {
let mut w = DateWidget::with_value(EditMode::Emacs, "2024-01-31T10:30");
w.step(1);
assert_eq!(w.value(), "2024-02-01T10:30");
fn step_back_retreats_one_day() {
let mut w = DateWidget::with_value(EditMode::Emacs, "2024-03-01T08:15");
w.step(-1);
assert_eq!(w.value(), "2024-02-29T08:15");
fn step_forward_output_accepted_by_validate_date() {
let mut w = DateWidget::with_value(EditMode::Emacs, "2024-06-15T12:00");
assert!(cli_core::forms::validate_date(w.value()).is_ok());