Repository: typst/typst
File: crates/typst-library/src/foundations/datetime.rs (Rust)

Code before:
        #[named]
        month: Option<Month>,
        /// The day of the datetime.
        #[named]
        day: Option<u8>,
        /// The hour of the datetime.
        #[named]
        hour: Option<u8>,
        /// The minute of the datetime.
        #[named]
        minute: Option<u8>,
        /// The second of the datetime.
        #[named]
        second: Option<u8>,
    ) -> HintedStrResult<Datetime> {
        fn format_missing_fields(fields: Vec<&str>) -> String {
            let len = fields.len();
            match len {
                0 => String::new(),
                1 => format!("{} field", fields[0]),
                _ => format!(
                    "{} and {} fields",
                    fields[0..len - 1].join(", "),
                    fields[len - 1]
                ),
            }
        }
        macro_rules! missing_fields_from {
            ($($key:expr => $value:expr),* $(,)?) => {
                format_missing_fields(
                    vec![$(if $key.is_none() { Some($value) } else { None }),*]
                        .into_iter()
                        .flatten()
                        .collect()
                )
            };
        }
        let time = match (hour, minute, second) {
            (Some(hour), Some(minute), Some(second)) => {
                match time::Time::from_hms(hour, minute, second) {
                    Ok(time) => Some(time),
                    Err(_) => bail!("time is invalid"),
                }
            }
            (None, None, None) => None,
            (hour, minute, second) => {
                let missing_fields = missing_fields_from![
                    hour => "`hour`",
                    minute => "`minute`",
                    second => "`second`"
                ];

Review comment (suggestion):
```suggestion
            match fields.as_slice() {
                [] => String::new(),
                [field] => format!("{field} field"),
                [fields @ .., field] => format!(
                    "{} and {field} fields",
                    fields.join(", "),
                ),
            }
```

Code after:
        /// The month of the datetime.
        #[named]
        month: Option<Month>,
        /// The day of the datetime.
        #[named]
        day: Option<u8>,
        /// The hour of the datetime.
        #[named]
        hour: Option<u8>,
        /// The minute of the datetime.
        #[named]
        minute: Option<u8>,
        /// The second of the datetime.
        #[named]
        second: Option<u8>,
    ) -> HintedStrResult<Datetime> {
        fn format_missing_args(args: ArrayVec<&str, 3>) -> EcoString {
            match args.as_slice() {
                [] => unreachable!(),
                [arg] => eco_format!("the {arg} argument"),
                [arg1, arg2] => eco_format!("the {arg1} and {arg2} arguments"),
                [args @ .., tail] => {
                    eco_format!("the {}, and {tail} arguments", args.join(", "))
                }
            }
        }

        let time = match (hour, minute, second) {
            (Some(hour), Some(minute), Some(second)) => {
                match time::Time::from_hms(hour, minute, second) {
                    Ok(time) => Some(time),
                    Err(_) => bail!("time is invalid"),
                }
            }
            (None, None, None) => None,
            (hour, minute, second) => {
                let args = [
                    if hour.is_none() { Some("`hour`") } else { None },
                    if minute.is_none() { Some("`minute`") } else { None },
                    if second.is_none() { Some("`second`") } else { None },
                ];
                bail!(
                    "time is incomplete";
                    hint: "add {} to get a valid time",
                    format_missing_args(args.into_iter().flatten().collect())
                )
            }
        };

        let date = match (year, month, day) {
            (Some(year), Some(month), Some(day)) => {