iced_7guis 0.3.0

An implementation of 7 GUIs using iced.
use iced::{
    Alignment, Element, Length, Size,
    widget::{button, column, container, pick_list, scrollable, text, text_input},
    window,
};
use jiff::civil::{Date, DateTime, Time};

/// # Errors
///
/// The application may error.
pub fn main() -> iced::Result {
    iced::application(
        FlightBooker::default,
        FlightBooker::update,
        FlightBooker::view,
    )
    .title("Flight Booker")
    .window(window::Settings {
        size: Size {
            width: 300.0,
            height: 250.0,
        },
        ..Default::default()
    })
    .run()
}

#[derive(Default)]
struct FlightBooker {
    selected_flight: Flight,
    one_way_flight: String,
    one_way_flight_date: Option<DateTime>,
    return_flight: String,
    return_flight_date: Option<DateTime>,
    book: bool,
    show_dialogue: bool,
    dialogue_string: String,
}

#[derive(Debug, Clone)]
enum Message {
    Book,
    FlightSelected(Flight),
    OneWayFlightChanged(String),
    ReturnFlightChanged(String),
}

impl FlightBooker {
    fn _print_flights(&self) {
        println!(
            "flight: {}, return_flight: {}",
            self.one_way_flight, self.return_flight
        );
    }

    fn validate_flights(&mut self) -> anyhow::Result<()> {
        match self.selected_flight {
            Flight::OneWay => match validate_flight(&self.one_way_flight) {
                Ok(flight) => {
                    self.one_way_flight_date = Some(flight);
                    Ok(())
                }
                Err(error) => {
                    self.one_way_flight_date = None;
                    Err(error)
                }
            },
            Flight::Return => {
                if let (Ok(flight), Ok(return_flight)) = (
                    validate_flight(&self.one_way_flight),
                    validate_flight(&self.return_flight),
                ) {
                    self.one_way_flight_date = Some(flight);
                    self.return_flight_date = Some(return_flight);

                    if self.one_way_flight_date <= self.return_flight_date {
                        Ok(())
                    } else {
                        Err(anyhow::Error::msg(
                            "the return flight date is before the flight date",
                        ))
                    }
                } else {
                    self.one_way_flight_date = None;
                    self.return_flight_date = None;
                    Err(anyhow::Error::msg("invalid date"))
                }
            }
        }
    }

    fn update(&mut self, message: Message) {
        match message {
            Message::Book => {
                if let Err(error) = self.validate_flights() {
                    self.show_dialogue = false;
                    eprintln!("error: {error}");
                } else {
                    self.show_dialogue = true;
                    let one_way_string = format!(
                        "You have booked a one-way flight on {}",
                        self.one_way_flight
                    );

                    let return_string = format!(
                        "You have booked a flight leaving on {} and returning on {}",
                        self.one_way_flight, self.return_flight,
                    );

                    match self.selected_flight {
                        Flight::OneWay => self.dialogue_string = one_way_string,
                        Flight::Return => self.dialogue_string = return_string,
                    }
                }
            }
            Message::FlightSelected(flight) => {
                self.show_dialogue = false;
                self.selected_flight = flight;
            }
            Message::OneWayFlightChanged(date) => {
                self.show_dialogue = false;
                self.one_way_flight = date;

                match self.validate_flights() {
                    Ok(()) => self.book = true,
                    Err(error) => {
                        eprintln!("error: {error}");
                        self.book = false;
                    }
                }
            }
            Message::ReturnFlightChanged(date) => {
                self.show_dialogue = false;
                self.return_flight = date;

                match self.validate_flights() {
                    Ok(()) => self.book = true,
                    Err(error) => {
                        eprintln!("error: {error}");
                        self.book = false;
                    }
                }
            }
        }

        // self.print_flights();
    }

    fn view(&self) -> Element<'_, Message> {
        let pick_list = pick_list(
            [Flight::OneWay, Flight::Return],
            Some(self.selected_flight),
            Message::FlightSelected,
        )
        .width(Length::Fill);

        let one_way_flight = text_input("choose a flight date", &self.one_way_flight)
            .on_input(Message::OneWayFlightChanged);

        let return_flight = if self.selected_flight == Flight::Return {
            text_input("choose a flight date", &self.return_flight)
                .on_input(Message::ReturnFlightChanged)
        } else {
            text_input("", &self.one_way_flight)
        };

        let mut book = button(text("Book").center().width(Length::Fill)).width(Length::Fill);
        if self.book {
            book = book.on_press(Message::Book);
        }

        let mut column = column![pick_list, one_way_flight, return_flight, book]
            .width(Length::Fill)
            .align_x(Alignment::Center)
            .padding(10)
            .spacing(10);

        if self.book && self.show_dialogue {
            column = column.push(text(self.dialogue_string.clone()));
        }

        container(scrollable(column))
            .width(Length::Fill)
            .height(Length::Fill)
            .into()
    }
}

#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
enum Flight {
    #[default]
    OneWay,
    Return,
}

impl std::fmt::Display for Flight {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "{}",
            match self {
                Flight::OneWay => "one-way flight",
                Flight::Return => "return flight",
            }
        )
    }
}

fn validate_flight(string: &str) -> anyhow::Result<DateTime> {
    let mut day_month_year = string.split('.');
    let Some(day) = day_month_year.next() else {
        return Err(anyhow::Error::msg("invalid day string"));
    };
    let Some(month) = day_month_year.next() else {
        return Err(anyhow::Error::msg("invalid month string"));
    };
    let Some(year) = day_month_year.next() else {
        return Err(anyhow::Error::msg("invalid year string"));
    };

    Ok(DateTime::from_parts(
        Date::new(year.parse()?, month.parse()?, day.parse()?)?,
        Time::new(0, 0, 0, 0)?,
    ))
}